BRL-CAD
sscanf.c
Go to the documentation of this file.
1 /* S S C A N F . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2012-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * Copyright (c) 1990, 1993 The Regents of the University of California.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above
17  * copyright notice, this list of conditions and the following
18  * disclaimer in the documentation and/or other materials provided
19  * with the distribution.
20  *
21  * 3. The name of the author may not be used to endorse or promote
22  * products derived from this software without specific prior written
23  * permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
26  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
29  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
31  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 /** @file sscanf.c
38  *
39  * Custom sscanf and vsscanf.
40  *
41  */
42 
43 #include "common.h"
44 
45 #include <ctype.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <string.h>
49 
50 #include "bu/log.h"
51 
52 
53 /*
54  * Flags used during conversion.
55  */
56 #define LONG 0x00001 /* l: long or double */
57 #define LONGDBL 0x00002 /* L: long double */
58 #define SHORT 0x00004 /* h: short */
59 #define SUPPRESS 0x00008 /* *: suppress assignment */
60 #define POINTER 0x00010 /* p: void * (as hex) */
61 #define NOSKIP 0x00020 /* [ or c: do not skip blanks */
62 #define LONGLONG 0x00400 /* ll: long long (+ deprecated q: quad) */
63 #define INTMAXT 0x00800 /* j: intmax_t */
64 #define PTRDIFFT 0x01000 /* t: ptrdiff_t */
65 #define SIZET 0x02000 /* z: size_t */
66 #define SHORTSHORT 0x04000 /* hh: char */
67 #define UNSIGNED 0x08000 /* %[oupxX] conversions */
68 #define ALTERNATE 0x40000 /* # flag for alternate behavior */
69 
70 /*
71  * The following are used in integral conversions only:
72  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
73  */
74 #define SIGNOK 0x00040 /* +/- is (still) legal */
75 #define NDIGITS 0x00080 /* no digits detected */
76 #define PFXOK 0x00100 /* 0x prefix is (still) legal */
77 #define NZDIGITS 0x00200 /* no zero digits detected */
78 #define HAVESIGN 0x10000 /* sign detected */
79 #define HAVEWIDTH 0x20000
80 
81 /*
82  * Conversion types.
83  */
84 #define CT_CHAR 0 /* %c conversion */
85 #define CT_CCL 1 /* %[...] conversion */
86 #define CT_STRING 2 /* %s conversion */
87 #define CT_INT 3 /* %[dioupxX] conversion */
88 #define CT_FLOAT 4 /* %[efgEFG] conversion */
89 #define CT_VLS 5 /* %V and %#V conversion */
90 
91 /* The basic strategy of this routine is to break the formatted scan into
92  * pieces, one for each conversion in the format string.
93  *
94  * New two-conversion format strings are created by appending "%n" (request to
95  * sscanf for consumed char count) to each conversion of the provided format
96  * string. sscanf is called with each two-conversion format string, followed by
97  * an appropriately cast pointer from the vararg list, followed by the local
98  * consumed count pointer.
99  *
100  * Each time sscanf successfully returns, the total assignment count and the
101  * total consumed character count are updated. The consumed character count is
102  * used to offset the read position of the source string on successive calls to
103  * sscanf. The total assignment count is the ultimate return value of the
104  * routine.
105  */
106 int
107 bu_vsscanf(const char *src, const char *fmt0, va_list ap)
108 {
109  int c;
110  long flags;
111  size_t i, width;
112  int numCharsConsumed, partConsumed;
113  int numFieldsAssigned, partAssigned;
114  struct bu_vls partFmt = BU_VLS_INIT_ZERO;
115  const char *fmt;
116 
117  BU_ASSERT(src != NULL);
118  BU_ASSERT(fmt0 != NULL);
119 
120  fmt = fmt0;
121 
122  numFieldsAssigned = 0;
123  numCharsConsumed = 0;
124  partConsumed = 0;
125  partAssigned = 0;
126 
127 #define UPDATE_COUNTS \
128  numCharsConsumed += partConsumed; \
129  numFieldsAssigned += partAssigned;
130 
131 #define FREE_FORMAT_PART \
132  bu_vls_free(&partFmt);
133 
134 #define GET_FORMAT_PART \
135  bu_vls_strcat(&partFmt, "%n");
136 
137 #define EXIT_DUE_TO_INPUT_FAILURE \
138  FREE_FORMAT_PART; \
139  if (numFieldsAssigned == 0) { \
140  return EOF; \
141  } \
142  return numFieldsAssigned;
143 
144 #define EXIT_DUE_TO_MATCH_FAILURE \
145  FREE_FORMAT_PART; \
146  return numFieldsAssigned;
147 
148 #define EXIT_DUE_TO_MISC_ERROR \
149  FREE_FORMAT_PART; \
150  return EOF;
151 
152  while (1) {
153  /* skip to first non-white char */
154  bu_vls_trunc(&partFmt, 0);
155  do {
156  c = *fmt;
157  if (c == '\0') {
158  /* Found EOI before next word; implies fmt contains trailing
159  * whitespace or is empty. No worries; exit normally.
160  */
162  return numFieldsAssigned;
163  }
164  bu_vls_putc(&partFmt, c);
165  ++fmt;
166  } while (isspace(c));
167 
168  if (c != '%') {
169  /* Must have found literal sequence. Find where it ends. */
170  while (1) {
171  c = *fmt;
172  if (c == '\0' || isspace(c) || c == '%') {
173  break;
174  }
175  bu_vls_putc(&partFmt, c);
176  ++fmt;
177  }
178 
179  /* scan literal sequence */
181  partAssigned = sscanf(&src[numCharsConsumed],
182  bu_vls_addr(&partFmt), &partConsumed);
183 
184  if (partAssigned < 0) {
186  }
188  continue;
189  }
190 
191  /* Found conversion specification. Parse it. */
192 
193  width = 0;
194  flags = 0;
195  again:
196  c = *fmt++;
197  bu_vls_putc(&partFmt, c);
198  switch (c) {
199 
200  /* Literal '%'. */
201  case '%':
203  partAssigned = sscanf(&src[numCharsConsumed],
204  bu_vls_addr(&partFmt), &partConsumed);
205 
206  if (partAssigned < 0) {
208  }
210  continue;
211 
212 
213  /* MODIFIER */
214  case '*':
215  flags |= SUPPRESS;
216  goto again;
217  case '#':
218  flags |= ALTERNATE;
219  goto again;
220  case 'j':
221  flags |= INTMAXT;
222  goto again;
223  case 'l':
224  if (!(flags & LONG)) {
225  /* First occurrence of 'l' in this conversion specifier. */
226  flags |= LONG;
227  } else {
228  /* Since LONG is set, the previous conversion character must
229  * have been 'l'. With this second 'l', we know we have an "ll"
230  * modifier, not an 'l' modifier. We need to replace the
231  * incorrect flag with the correct one.
232  */
233  flags &= ~LONG;
234  flags |= LONGLONG;
235  }
236  goto again;
237  case 't':
238 #ifndef HAVE_C99_FORMAT_SPECIFIERS
239  /* remove C99 't' */
240  bu_vls_trunc(&partFmt, bu_vls_strlen(&partFmt) - 1);
241 
242  /* Assume MSVC.
243  *
244  * For 32-bit, ptrdiff_t is __int32, and equivalent of %t[dioxX] is
245  * %[dioxX].
246  *
247  * For 64-bit, ptrdiff_t is __int64, and equivalent of %t[dioxX] is
248  * %I64[dioxX].
249  */
250 #if defined(SIZEOF_SIZE_T) && SIZEOF_SIZE_T == 8
251  bu_vls_strcat(&partFmt, "I64");
252 #endif
253 #endif
254  flags |= PTRDIFFT;
255  goto again;
256  case 'z':
257 #ifndef HAVE_C99_FORMAT_SPECIFIERS
258  /* remove C99 'z' */
259  bu_vls_trunc(&partFmt, bu_vls_strlen(&partFmt) - 1);
260 
261  /* Assume MSVC.
262  *
263  * For 32-bit, size_t is unsigned __int32, and equivalent of
264  * %z[dioxX] is %[dioxX].
265  *
266  * For 64-bit, size_t is unsigned __int64, and equivalent of
267  * %z[dioxX] is %I64[dioxX].
268  */
269 #if defined(SIZEOF_SIZE_T) && SIZEOF_SIZE_T == 8
270  bu_vls_strcat(&partFmt, "I64");
271 #endif
272 #endif
273  flags |= SIZET;
274  goto again;
275  case 'L':
276  flags |= LONGDBL;
277  goto again;
278  case 'h':
279  if (!(flags & SHORT)) {
280  /* First occurrence of 'h' in this conversion specifier. */
281  flags |= SHORT;
282  } else {
283 #ifndef HAVE_C99_FORMAT_SPECIFIERS
284  /* Assume MSVC, where there is no equivalent of %hh[diouxX].
285  * Will use %h[diouxX] with short instead, then cast into
286  * char argument.
287  */
288  bu_vls_trunc(&partFmt, bu_vls_strlen(&partFmt) - 1);
289 #endif
290  /* Since SHORT is set, the previous conversion character must
291  * have been 'h'. With this second 'h', we know we have an "hh"
292  * modifier, not an 'h' modifier. We need to replace the
293  * incorrect flag with the correct one.
294  */
295  flags &= ~SHORT;
296  flags |= SHORTSHORT;
297  }
298  goto again;
299 
300 
301  /* MAXIMUM FIELD WIDTH */
302 #define NUMERIC_CHAR_TO_INT(c) (c - '0')
303  case '0':
304  /* distinguish default width from width set to 0 */
305  flags |= HAVEWIDTH;
306  /* FALLTHROUGH */
307  case '1': case '2': case '3': case '4':
308  case '5': case '6': case '7': case '8': case '9':
309  width = (width * 10) + NUMERIC_CHAR_TO_INT(c);
310  goto again;
311 
312 
313  /* CONVERSION */
314  case 'd':
315  c = CT_INT;
316  break;
317  case 'i':
318  c = CT_INT;
319  break;
320  case 'o':
321  c = CT_INT;
322  flags |= UNSIGNED;
323  break;
324  case 'u':
325  c = CT_INT;
326  flags |= UNSIGNED;
327  break;
328  case 'p':
329  case 'x':
330  case 'X':
331  if (c == 'p') {
332  flags |= POINTER;
333  }
334  flags |= PFXOK;
335  flags |= UNSIGNED;
336  c = CT_INT;
337  break;
338  case 'A': case 'E': case 'F': case 'G':
339  case 'a': case 'e': case 'f': case 'g':
340  /* e/f/g, E/F/G, and a/A (C99) are all synonyms for float
341  * conversion. Support for a/A is limited to C99-compliant
342  * implementations, and there is varying support for E/F/G.
343  * Replace all with the most portable 'f' variant.
344  */
345  bu_vls_trunc(&partFmt, bu_vls_strlen(&partFmt) - 1);
346  bu_vls_putc(&partFmt, 'f');
347  c = CT_FLOAT;
348  break;
349  case 's':
350  c = CT_STRING;
351  break;
352  case '[':
353  /* note that at this point c == '[' == fmt[-1] and so fmt[0] is
354  * either '^' or the first character of the class
355  */
356 
357  /* there should be at least one character in between brackets */
358  if (fmt[0] == '\0' || fmt[1] == '\0') {
360  }
361 
362  /* skip literal ']' ("[]" or "[^]") */
363  if (fmt[0] == ']') {
364  fmt = &fmt[1];
365  } else if (fmt[0] == '^' && fmt[1] == ']') {
366  fmt = &fmt[2];
367  }
368 
369  /* point fmt after character class */
370  while (1) {
371  c = *fmt++;
372  bu_vls_putc(&partFmt, c);
373 
374  if (c == '\0') {
376  }
377  if (c == ']') {
378  /* found end of character class */
379  break;
380  }
381  }
382 
383  flags |= NOSKIP;
384  c = CT_CCL;
385  break;
386  case 'c':
387  flags |= NOSKIP;
388  c = CT_CHAR;
389  break;
390  case 'V':
391  c = CT_VLS;
392  break;
393  case 'n':
394  if (flags & SUPPRESS) {
395  /* This is legal, but doesn't really make sense. Caller
396  * requested assignment of the current count of consumed
397  * characters, but then suppressed the assignment they
398  * requested!
399  */
400  continue;
401  }
402 
403  /* Store current count of consumed characters to whatever kind of
404  * int pointer was provided.
405  */
406  if (flags & SHORTSHORT) {
407  *va_arg(ap, char *) = numCharsConsumed;
408  } else if (flags & SHORT) {
409  *va_arg(ap, short *) = numCharsConsumed;
410  } else if (flags & LONG) {
411  *va_arg(ap, long *) = numCharsConsumed;
412  } else if (flags & LONGLONG) {
413  *va_arg(ap, long long *) = numCharsConsumed;
414  } else if (flags & INTMAXT) {
415  *va_arg(ap, intmax_t *) = numCharsConsumed;
416  } else if (flags & SIZET) {
417  *va_arg(ap, size_t *) = numCharsConsumed;
418  } else if (flags & PTRDIFFT) {
419  *va_arg(ap, ptrdiff_t *) = numCharsConsumed;
420  } else {
421  *va_arg(ap, int *) = numCharsConsumed;
422  }
423  continue;
424 
425  case '\0':
426  /* Format string ends with bare '%'. Returning EOF regardless of
427  * successful assignments is a backwards compatibility behavior.
428  */
430 
431  default:
433  }
434 
435  /* Done parsing conversion specification.
436  * Now do the actual conversion.
437  */
438 
440 
441 #define SSCANF_TYPE(type) \
442  if (flags & SUPPRESS) { \
443  partAssigned = sscanf(&src[numCharsConsumed], bu_vls_addr(&partFmt), \
444  &partConsumed); \
445  } else { \
446  partAssigned = sscanf(&src[numCharsConsumed], bu_vls_addr(&partFmt), \
447  va_arg(ap, type), &partConsumed); \
448  }
449 
450 #define SSCANF_SIGNED_UNSIGNED(type) \
451  if (flags & UNSIGNED) { \
452  SSCANF_TYPE(unsigned type); \
453  } else { \
454  SSCANF_TYPE(type); \
455  }
456 
457 
458  partAssigned = partConsumed = 0;
459 
460  switch (c) {
461 
462  case CT_VLS:
463  /* %V %#V conversion */
464  {
465  struct bu_vls *vls = NULL;
466 
467  if (src[numCharsConsumed] == '\0') {
469  }
470 
471  /* Leading input whitespace is skipped for %#V, and for %V if the
472  * conversion specification is preceded by at least one whitespace
473  * character.
474  */
475  if (isspace((int)(*bu_vls_addr(&partFmt))) || flags & ALTERNATE) {
476  while (1) {
477  c = src[numCharsConsumed];
478  if (c == '\0' || !isspace(c)) {
479  break;
480  }
481  ++numCharsConsumed;
482  }
483  }
484 
485  /* if no width provided, width is infinity */
486  if (width == 0 && !(flags & HAVEWIDTH)) {
487  width = ~(width & 0);
488  }
489 
490  /* grab vls pointer if we're assigning */
491  if (!(flags & SUPPRESS)) {
492  vls = va_arg(ap, struct bu_vls*);
493  }
494 
495  /* Copy characters from src to vls. Stop at width, whitespace
496  * character, or EOI.
497  */
498  if (flags & SUPPRESS) {
499  for (i = 0; i < width; ++i) {
500  c = src[numCharsConsumed + i];
501 
502  /* stop at non-matching or EOI */
503  if (c == '\0') {
504  break;
505  }
506  if ((flags & ALTERNATE) && isspace(c)) {
507  break;
508  }
509  ++partConsumed;
510  }
511  } else {
512  for (i = 0; i < width; ++i) {
513  c = src[numCharsConsumed + i];
514 
515  /* stop at non-matching or EOI */
516  /* stop at non-matching or EOI */
517  if (c == '\0') {
518  break;
519  }
520  if ((flags & ALTERNATE) && isspace(c)) {
521  break;
522  }
523 
524  /* copy valid char to vls */
525  bu_vls_putc(vls, c);
526  ++partConsumed;
527  }
528 
529  if (partConsumed > 0) {
530  /* successful assignment */
531  ++partAssigned;
532  }
533  }
534  break;
535  } /* CT_VLS */
536 
537  case CT_CHAR:
538  case CT_CCL:
539  case CT_STRING:
540 
541  /* %lc %l[...] %ls are unsupported */
542  if (flags & LONG) {
544  }
545 
546  /* unsuppressed %s or %[...] conversion */
547  if (!(flags & SUPPRESS)) {
548  if (width == 0) {
549  if (flags & HAVEWIDTH) {
550  /* Caller specified zero width in the format string.
551  * (%0c %0s or %0[...])
552  *
553  * The behavior of sscanf for a zero width is
554  * undefined, so we provide our own consistent
555  * behavior here.
556  *
557  * The assignment wasn't suppressed, so we'll assume
558  * the caller provided a pointer and wants us to write
559  * to it. Just write '\0' and call it a successful
560  * assignment.
561  */
562  *va_arg(ap, char*) = '\0';
563  ++partAssigned;
564  break;
565  } else if (c != CT_CHAR) {
566  struct bu_vls err = BU_VLS_INIT_ZERO;
567 
568  /* No width was provided by caller.
569  *
570  * If the caller is using %s or %[...] without a
571  * maximum field width, then there is a bug in the
572  * caller code.
573  *
574  * sscanf could easily overrun the provided buffer and
575  * cause a program crash, so just bomb here and make
576  * the source of the problem clear.
577  */
578  bu_vls_sprintf(&err, "ERROR.\n"
579  " bu_sscanf was called with bad format string: \"%s\"\n"
580  " %%s and %%[...] conversions must be bounded using "
581  "a maximum field width.", fmt0);
582  bu_bomb(bu_vls_addr(&err));
583  }
584  }
585  }
586 
587  /* ordinary %c or %[...] or %s conversion */
588  SSCANF_TYPE(char*);
589  break;
590 
591  /* %[dioupxX] conversion */
592  case CT_INT:
593  if (flags & SHORT) {
594  SSCANF_SIGNED_UNSIGNED(short int*);
595  } else if (flags & SHORTSHORT) {
596 #ifndef HAVE_C99_FORMAT_SPECIFIERS
597  /* Assume MSVC, where there is no equivalent of %hh[diouxX].
598  * Will use %h[diouxX] with short instead, then cast into
599  * char argument.
600  */
601  if (flags & SUPPRESS) {
602  partAssigned = sscanf(&src[numCharsConsumed],
603  bu_vls_addr(&partFmt), &partConsumed);
604  } else {
605  if (flags & UNSIGNED) {
606  unsigned short charConvVal = 0;
607  partAssigned = sscanf(&src[numCharsConsumed],
608  bu_vls_addr(&partFmt), &charConvVal,
609  &partConsumed);
610  *va_arg(ap, unsigned char*) = (unsigned char)charConvVal;
611  } else {
612  short charConvVal = 0;
613  partAssigned = sscanf(&src[numCharsConsumed],
614  bu_vls_addr(&partFmt), &charConvVal,
615  &partConsumed);
616  *va_arg(ap, signed char*) = (signed char)charConvVal;
617  }
618  }
619 
620 #else
621  SSCANF_SIGNED_UNSIGNED(char*);
622 #endif
623  } else if (flags & LONG) {
624  SSCANF_SIGNED_UNSIGNED(long int*);
625  } else if (flags & LONGLONG) {
626  SSCANF_SIGNED_UNSIGNED(long long int*);
627  } else if (flags & POINTER) {
628  SSCANF_TYPE(void*);
629  } else if (flags & PTRDIFFT) {
630  SSCANF_TYPE(ptrdiff_t*);
631  } else if (flags & SIZET) {
632  SSCANF_TYPE(size_t*);
633  } else if (flags & INTMAXT) {
634  if (flags & UNSIGNED) {
635  SSCANF_TYPE(uintmax_t*);
636  } else {
637  SSCANF_TYPE(intmax_t*);
638  }
639  } else {
640  SSCANF_TYPE(int*);
641  }
642  break;
643 
644  /* %[aefgAEFG] conversion */
645  case CT_FLOAT:
646  if (flags & LONG) {
647  SSCANF_TYPE(double*);
648  } else if (flags & LONGDBL) {
649  SSCANF_TYPE(long double*);
650  } else {
651  SSCANF_TYPE(float*);
652  }
653  }
654 
655  /* check for read error or bad source string */
656  if (partAssigned == EOF) {
658  }
659 
660  /* check that assignment was successful */
661  if (!(flags & SUPPRESS) && partAssigned < 1) {
663  }
664 
665  /* Conversion successful, on to the next one! */
667 
668  } /* while (1) */
669 
671  return numFieldsAssigned;
672 } /* bu_vsscanf */
673 
674 
675 int
676 bu_sscanf(const char *src, const char *fmt, ...)
677 {
678  int ret;
679  va_list ap;
680 
681  va_start(ap, fmt);
682  ret = bu_vsscanf(src, fmt, ap);
683  va_end(ap);
684 
685  return ret;
686 }
687 
688 
689 /*
690  * Local Variables:
691  * tab-width: 8
692  * mode: C
693  * indent-tabs-mode: t
694  * c-file-style: "stroustrup"
695  * End:
696  * ex: shiftwidth=4 tabstop=8
697  */
#define PTRDIFFT
Definition: sscanf.c:64
#define LONGDBL
Definition: sscanf.c:57
#define CT_STRING
Definition: sscanf.c:86
#define EXIT_DUE_TO_MISC_ERROR
#define SUPPRESS
Definition: sscanf.c:59
void bu_vls_strcat(struct bu_vls *vp, const char *s)
Definition: vls.c:368
void bu_vls_trunc(struct bu_vls *vp, int len)
Definition: vls.c:198
#define SIZET
Definition: sscanf.c:65
#define SHORT
Definition: sscanf.c:58
Header file for the BRL-CAD common definitions.
#define BU_ASSERT(_equation)
Definition: defines.h:216
#define GET_FORMAT_PART
ustring width
#define EXIT_DUE_TO_MATCH_FAILURE
#define FREE_FORMAT_PART
#define CT_FLOAT
Definition: sscanf.c:88
void bu_vls_sprintf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:707
int bu_vsscanf(const char *src, const char *fmt0, va_list ap)
Definition: sscanf.c:107
#define CT_CHAR
Definition: sscanf.c:84
#define INTMAXT
Definition: sscanf.c:63
int bu_sscanf(const char *src, const char *fmt,...)
Definition: sscanf.c:676
#define EXIT_DUE_TO_INPUT_FAILURE
size_t bu_vls_strlen(const struct bu_vls *vp)
Definition: vls.c:189
#define HAVEWIDTH
Definition: sscanf.c:79
#define PFXOK
Definition: sscanf.c:76
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
#define CT_CCL
Definition: sscanf.c:85
#define UNSIGNED
Definition: sscanf.c:67
#define NUMERIC_CHAR_TO_INT(c)
#define NOSKIP
Definition: sscanf.c:61
#define CT_VLS
Definition: sscanf.c:89
#define LONG
Definition: sscanf.c:56
#define SHORTSHORT
Definition: sscanf.c:66
#define SSCANF_TYPE(type)
#define UPDATE_COUNTS
#define POINTER
Definition: sscanf.c:60
#define SSCANF_SIGNED_UNSIGNED(type)
#define ALTERNATE
Definition: sscanf.c:68
#define LONGLONG
Definition: sscanf.c:62
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
Definition: vls.h:56
void bu_bomb(const char *str) _BU_ATTR_NORETURN
Definition: bomb.c:91
#define CT_INT
Definition: sscanf.c:87
void bu_vls_putc(struct bu_vls *vp, int c)
Definition: vls.c:666