BRL-CAD
bu_sscanf.c
Go to the documentation of this file.
1 /* T E S T _ 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  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file bu_sscanf.c
21  *
22  * Test bu_sscanf routine.
23  *
24  */
25 
26 #include "common.h"
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 #include "bu.h"
33 #include "vmath.h"
34 
35 #define TS_FLOAT_TOL .0005
36 
37 #define TS_STR_SIZE 128
38 #define TS_STR_WIDTH "127"
39 
40 /* scanf specification summarized from Linux man page:
41  *
42  * If processing of a directive fails, no further input is read, and
43  * scanf returns.
44  *
45  * Returns number of input items successfully matched and assigned, or
46  * EOF if a read error occurs (errno and stream error indicator are set),
47  * EOI is encountered before the first successful conversion, or a
48  * matching failure occurs.
49  *
50  * Can match 0 or more whitespace chars, ordinary character, or a
51  * conversion specification:
52  *
53  * %[*][a][max_field_width][type_modifier]<conversion_specifier>
54  *
55  * '*' suppresses assignment. No pointer is needed, input is discarded,
56  * and conversion isn't added to the successful conversion count.
57  *
58  * 'a' in GNU, this represents an extension that automatically allocates
59  * a string large enough to hold a converted string and assigns the
60  * address to the supplied char*. In C99, a is synonymous with e/E/f/g.
61  *
62  * max_field_width - scan stops stops reading for a conversion at the
63  * first non-matching character or when max_field_width is reached
64  * (not including discarded leading whitespace or the '\0' appended
65  * to string conversions).
66  *
67  * modifiers:
68  * h - expect (signed|unsigned) short int (not int) pointer
69  * hh - expect (signed|unsigned) char (not int) pointer
70  * l - expect (signed|unsigned) long int (not int) pointer, or double (not
71  * float), or wide char (not char) pointer
72  * L - expect long double (not double) pointer
73  *
74  * C99 modifiers:
75  * j - expect (signed|unsigned) intmax_t or uintmax_t (not int) pointer
76  * t - expect ptrdiff_t (not int) pointer
77  * z - expect size_t (not int) pointer
78  *
79  * Note: the "long long" type extension is specified with 'q' for 4.4BSD,
80  * and 'll' or 'L' for GNU.
81  *
82  * conversion specifiers:
83  * % - NOT A CONVERSION. Literal %.
84  * d - signed/unsigned decimal integer
85  * i - Signed/unsigned base 8/10/16 integer. Base 16 chose if 0x or 0X is
86  * read first, base 8 if 0 is read first, and base 10 otherwise. Only
87  * characters valid for the base are read.
88  * o - unsigned octal
89  * u - unsigned decimal
90  *x/X - unsigned hexadecimal
91  *a/e/E/f/g - signed/unsigned float (a is C99)
92  * s - Match a sequence of non-white-space characters. Pointer must be
93  * large enough for all characters plus the '\0' scanf appends.
94  * c - Match a sequence (1 by default) of characters. Pointer must be large
95  * enough for all characters. No '\0' is appended. Leading white space
96  * IS NOT discarded.
97  *[...] - Like c, but only accept the specified character class. Leading white
98  * space IS NOT discarded. To include ']' in the set, it must appear
99  * first after '[' or '^'. To include '-' in the set, it must appear
100  * last before ']'.
101  * p - pointer (printf format)
102  * n - NOT A CONVERSION. Stores number of characters read so far in the int
103  * pointer. PROBABLY shouldn't affect returned conversion count.
104  */
105 
106 /* Using these constants to identify pointer type instead of adding all the
107  * logic needed to parse the format string.
108  */
109 enum {
118 };
119 
120 static void
121 print_src_and_fmt(const char *src, const char *fmt)
122 {
123  printf("\"%s\", \"%s\"\n", src, fmt);
124 }
125 
126 /* This test is intended to catch errors in the tests themselves. If we make a
127  * typo in a test format string causing fewer assignments than expected, then
128  * we could end up silently comparing error behavior rather than the behavior
129  * we actually want to test.
130  */
131 static void
132 checkReturnVal(const char *funcStr, int actual, int expected)
133 {
134  if (actual != expected) {
135  bu_exit(1, "\tError: %s returned %d%s. Expected %d%s.\n", funcStr,
136  actual, (actual == EOF) ? " (EOF)" : "",
137  expected, (expected == EOF) ? " (EOF)" : "");
138  }
139 }
140 
141 /* Exit if returns from sscanf and bu_sscanf are not equal. */
142 static void
143 checkReturnsEqual(int ret, int bu_ret)
144 {
145  if (bu_ret != ret) {
146  bu_exit(1, "\t[FAIL] sscanf returned %d but bu_sscanf returned %d.\n",
147  ret, bu_ret);
148  }
149 }
150 
151 #define CHECK_INT_VALS_EQUAL(int_type, pfmt, valp, bu_valp) \
152 { \
153  int_type _val = *(int_type*)(valp); \
154  int_type _bu_val = *(int_type*)(bu_valp); \
155  if (_val != _bu_val) { \
156  bu_exit(1, "\t[FAIL] conversion value mismatch.\n" \
157  "\t(sscanf) %" bu_cpp_str(pfmt) " != " \
158  "%" bu_cpp_str(pfmt) " (bu_sscanf).\n", \
159  _val, _bu_val); \
160  } \
161 }
162 
163 #define CHECK_FLOAT_VALS_EQUAL(float_type, pfmt, valp, bu_valp) \
164 { \
165  float_type _val = *(float_type*)(valp); \
166  float_type _bu_val = *(float_type*)(bu_valp); \
167  if (!NEAR_EQUAL(_val, _bu_val, TS_FLOAT_TOL)) { \
168  bu_exit(1, "\t[FAIL] conversion value mismatch.\n" \
169  "\t(sscanf) %" bu_cpp_str(pfmt) " != " \
170  "%" bu_cpp_str(pfmt) " (bu_sscanf).\n", \
171  _val, _bu_val); \
172  } \
173 }
174 
175 static void
176 test_sscanf(int type, const char *src, const char *fmt) {
177  int ret, bu_ret;
178  void *val, *bu_val;
179 
180  ret = bu_ret = 0;
181  val = bu_val = NULL;
182 
183  print_src_and_fmt(src, fmt);
184 
185  /* call sscanf and bu_sscanf with appropriately cast pointers */
186 #define SSCANF_TYPE(type) \
187  BU_ALLOC(val, type); \
188  BU_ALLOC(bu_val, type); \
189 \
190  ret = sscanf(src, fmt, (type*)val); \
191  bu_ret = bu_sscanf(src, fmt, (type*)bu_val); \
192 \
193  checkReturnVal("sscanf", ret, 1); \
194  checkReturnsEqual(bu_ret, ret);
195 
196  switch (type) {
197  case SCAN_INT:
198  SSCANF_TYPE(int);
199  CHECK_INT_VALS_EQUAL(int, d, val, bu_val);
200  break;
201  case SCAN_UINT:
202  SSCANF_TYPE(unsigned);
203  CHECK_INT_VALS_EQUAL(unsigned, u, val, bu_val);
204  break;
205  case SCAN_SHORT:
206  SSCANF_TYPE(short);
207  CHECK_INT_VALS_EQUAL(short, hd, val, bu_val);
208  break;
209  case SCAN_USHORT:
210  SSCANF_TYPE(unsigned short);
211  CHECK_INT_VALS_EQUAL(unsigned short, hu, val, bu_val);
212  break;
213  case SCAN_SHORTSHORT:
214  SSCANF_TYPE(char);
215  CHECK_INT_VALS_EQUAL(char, hhd, val, bu_val);
216  break;
217  case SCAN_USHORTSHORT:
218  SSCANF_TYPE(unsigned char);
219  CHECK_INT_VALS_EQUAL(unsigned char, hhu, val, bu_val);
220  break;
221  case SCAN_LONG:
222  SSCANF_TYPE(long);
223  CHECK_INT_VALS_EQUAL(long, ld, val, bu_val);
224  break;
225  case SCAN_ULONG:
226  SSCANF_TYPE(unsigned long);
227  CHECK_INT_VALS_EQUAL(unsigned long, lu, val, bu_val);
228  break;
229  case SCAN_SIZE:
230  SSCANF_TYPE(size_t);
231  CHECK_INT_VALS_EQUAL(size_t, z, val, bu_val);
232  break;
233  case SCAN_PTRDIFF:
234  SSCANF_TYPE(ptrdiff_t);
235  CHECK_INT_VALS_EQUAL(ptrdiff_t, t, val, bu_val);
236  break;
237  case SCAN_POINTER:
238  ret = sscanf(src, fmt, &val);
239  bu_ret = bu_sscanf(src, fmt, &bu_val);
240 
241  checkReturnVal("sscanf", ret, 1);
242  checkReturnsEqual(bu_ret, ret);
243 
244  if (val != bu_val) {
245  bu_exit(1, "\t[FAIL] conversion value mismatch.\n"
246  "\t(sscanf) %p != %p (bu_sscanf).\n",
247  val, bu_val);
248  }
249  val = bu_val = NULL;
250  break;
251  case SCAN_FLOAT:
252  SSCANF_TYPE(float);
253  CHECK_FLOAT_VALS_EQUAL(float, e, val, bu_val);
254  break;
255  case SCAN_DOUBLE:
256  SSCANF_TYPE(double);
257  CHECK_FLOAT_VALS_EQUAL(double, le, val, bu_val);
258  break;
259  case SCAN_LDOUBLE:
260  SSCANF_TYPE(long double);
261  CHECK_FLOAT_VALS_EQUAL(long double, Le, val, bu_val);
262  break;
263  default:
264  bu_exit(1, "Error: test_sscanf was given an unrecognized pointer type.\n");
265  }
266  if (val != NULL) {
267  bu_free(val, "test_sscanf val");
268  val = NULL;
269  }
270  if (bu_val != NULL) {
271  bu_free(bu_val, "test_sscanf bu_val");
272  bu_val = NULL;
273  }
274 } /* test_sscanf */
275 
276 /* Here we define printable constants that should be safe on any platform.
277  *
278  * Note that we don't use the macros in limits.h or float.h, because they
279  * aren't necessarily printable, as in:
280  * #define INT_MIN (-INT_MAX - 1)
281  */
282 #define SIGNED_HH_DEC 127
283 #define UNSIGNED_HH_DEC 255
284 #define SIGNED_HH_OCT 0177
285 #define UNSIGNED_HH_OCT 0377
286 #define SIGNED_HH_HEX 0x7F
287 #define UNSIGNED_HH_HEX 0xFF
288 
289 #define SIGNED_DEC 32767
290 #define UNSIGNED_DEC 65535
291 #define SIGNED_OCT 0077777
292 #define UNSIGNED_OCT 0177777
293 #define SIGNED_HEX 0x7FFF
294 #define UNSIGNED_HEX 0xFFFF
295 
296 #define SIGNED_L_DEC 2147483647
297 #define UNSIGNED_L_DEC 4294967295
298 #define SIGNED_L_OCT 17777777777
299 #define UNSIGNED_L_OCT 37777777777
300 #define SIGNED_L_HEX 0x7FFFFFFF
301 #define UNSIGNED_L_HEX 0xFFFFFFFF
302 
303 #define SMALL_FLT 1.0e-37
304 #define LARGE_FLT 1.0e+37
305 
306 static void
307 doNumericTests(void)
308 {
309 #define TEST_SIGNED_CONSTANT(str, fmt) \
310  test_sscanf(SCAN_SHORTSHORT, str, "%hh" bu_cpp_str(fmt)); \
311  test_sscanf(SCAN_SIZE, str, "%z" bu_cpp_str(fmt)); \
312  test_sscanf(SCAN_SHORT, str, "%h" bu_cpp_str(fmt)); \
313  test_sscanf(SCAN_INT, str, "%" bu_cpp_str(fmt)); \
314  test_sscanf(SCAN_LONG, str, "%l" bu_cpp_str(fmt));
315 
316  TEST_SIGNED_CONSTANT("0", d);
317  TEST_SIGNED_CONSTANT("0", i);
318  TEST_SIGNED_CONSTANT("000", i);
319  TEST_SIGNED_CONSTANT("0x0", i);
320  TEST_SIGNED_CONSTANT("0X0", i);
321 
322 #define TEST_UNSIGNED_CONSTANT(str, fmt) \
323  test_sscanf(SCAN_USHORTSHORT, str, "%hh" bu_cpp_str(fmt)); \
324  test_sscanf(SCAN_SIZE, str, "%z" bu_cpp_str(fmt)); \
325  test_sscanf(SCAN_USHORT, str, "%h" bu_cpp_str(fmt)); \
326  test_sscanf(SCAN_UINT, str, "%" bu_cpp_str(fmt)); \
327  test_sscanf(SCAN_ULONG, str, "%l" bu_cpp_str(fmt));
328 
330  TEST_UNSIGNED_CONSTANT("0", o);
331  TEST_UNSIGNED_CONSTANT("000", o);
332  TEST_UNSIGNED_CONSTANT("0x0", x);
333  TEST_UNSIGNED_CONSTANT("0x0", X);
334  TEST_UNSIGNED_CONSTANT("0X0", x);
335  TEST_UNSIGNED_CONSTANT("0X0", X);
336 
337 #define TEST_SIGNED_CONSTANTS(small, med, large, fmt) \
338  test_sscanf(SCAN_SHORTSHORT, "+" bu_cpp_str(small), "%hh" bu_cpp_str(fmt)); \
339  test_sscanf(SCAN_SHORTSHORT, bu_cpp_str(small), "%hh" bu_cpp_str(fmt)); \
340  test_sscanf(SCAN_SHORTSHORT, "-" bu_cpp_str(small), "%hh" bu_cpp_str(fmt)); \
341  test_sscanf(SCAN_SIZE, bu_cpp_str(small), "%z" bu_cpp_str(fmt)); \
342  test_sscanf(SCAN_SHORT, "+" bu_cpp_str(med), "%h" bu_cpp_str(fmt)); \
343  test_sscanf(SCAN_SHORT, bu_cpp_str(med), "%h" bu_cpp_str(fmt)); \
344  test_sscanf(SCAN_SHORT, "-" bu_cpp_str(med), "%h" bu_cpp_str(fmt)); \
345  test_sscanf(SCAN_INT, "+" bu_cpp_str(med), "%" bu_cpp_str(fmt)); \
346  test_sscanf(SCAN_INT, bu_cpp_str(med), "%" bu_cpp_str(fmt)); \
347  test_sscanf(SCAN_INT, "-" bu_cpp_str(med), "%" bu_cpp_str(fmt)); \
348  test_sscanf(SCAN_LONG, "+" bu_cpp_str(large), "%l" bu_cpp_str(fmt)); \
349  test_sscanf(SCAN_LONG, bu_cpp_str(large), "%l" bu_cpp_str(fmt)); \
350  test_sscanf(SCAN_LONG, "-" bu_cpp_str(large), "%l" bu_cpp_str(fmt));
351 
356 
357 #define TEST_UNSIGNED_CONSTANTS(small, med, large, fmt) \
358  test_sscanf(SCAN_USHORTSHORT, bu_cpp_str(small), "%hh" bu_cpp_str(fmt)); \
359  test_sscanf(SCAN_SIZE, bu_cpp_str(small), "%z" bu_cpp_str(fmt)); \
360  test_sscanf(SCAN_USHORT, bu_cpp_str(med), "%h" bu_cpp_str(fmt)); \
361  test_sscanf(SCAN_UINT, bu_cpp_str(med), "%" bu_cpp_str(fmt)); \
362  test_sscanf(SCAN_ULONG, bu_cpp_str(large), "%l" bu_cpp_str(fmt));
363 
368 
369 #define TEST_FLOAT_CONSTANT(c, fmt) \
370  test_sscanf(SCAN_FLOAT, c, "%" bu_cpp_str(fmt)); \
371  test_sscanf(SCAN_DOUBLE, c, "%l" bu_cpp_str(fmt)); \
372  test_sscanf(SCAN_LDOUBLE, c, "%L" bu_cpp_str(fmt));
373 
374 #define TEST_FLOAT_VARIANT(fmt) \
375  TEST_FLOAT_CONSTANT("0.0", fmt); \
376  TEST_FLOAT_CONSTANT("0.0e0", fmt); \
377  TEST_FLOAT_CONSTANT("0.0e1", fmt); \
378  test_sscanf(SCAN_FLOAT, bu_cpp_xstr(SMALL_FLT), "%" bu_cpp_str(fmt)); \
379  test_sscanf(SCAN_FLOAT, bu_cpp_xstr(LARGE_FLT), "%" bu_cpp_str(fmt)); \
380  test_sscanf(SCAN_DOUBLE, bu_cpp_xstr(SMALL_FLT), "%l" bu_cpp_str(fmt)); \
381  test_sscanf(SCAN_DOUBLE, bu_cpp_xstr(LARGE_FLT), "%l" bu_cpp_str(fmt)); \
382  test_sscanf(SCAN_LDOUBLE, bu_cpp_xstr(SMALL_FLT), "%L" bu_cpp_str(fmt)); \
383  test_sscanf(SCAN_LDOUBLE, bu_cpp_xstr(LARGE_FLT), "%L" bu_cpp_str(fmt));
384 
389 
394 }
395 
396 /* string test routine */
397 static void
398 test_sscanf_s(const char *src, const char *fmt)
399 {
400  int ret, bu_ret;
401  char dest[TS_STR_SIZE], bu_dest[TS_STR_SIZE];
402 
403  ret = bu_ret = 0;
404 
405  /* ensure NULL termination even for c and [...] */
406  memset(dest, '\0', TS_STR_SIZE);
407  memset(bu_dest, '\0', TS_STR_SIZE);
408 
409  print_src_and_fmt(src, fmt);
410 
411  ret = sscanf(src, fmt, dest);
412  bu_ret = bu_sscanf(src, fmt, bu_dest);
413 
414  checkReturnVal("sscanf", ret, 1);
415  checkReturnsEqual(bu_ret, ret);
416 
417  if (!BU_STR_EQUAL(dest, bu_dest)) {
418  bu_exit(1, "\t[FAIL] conversion value mismatch.\n"
419  "\t(sscanf) %s != %s (bu_sscanf).\n",
420  dest, bu_dest);
421  }
422 }
423 
424 static void
425 doStringTests(void)
426 {
427  int bu_ret;
428  char buf[TS_STR_SIZE];
429  char *cp;
430 
431 #define TEST_STR_SPACE " \t\naBc\n"
432 #define TEST_STR_NOSPACE "aBc"
433 
434 #define TEST_TERMINATION(src, fmt) \
435  print_src_and_fmt(src, fmt); \
436  /* init so that 'X' appears after the last char written by bu_sscanf */ \
437  memset(buf, 'X', TS_STR_SIZE); \
438  bu_ret = bu_sscanf(src, fmt, buf); \
439  checkReturnVal("bu_sscanf", bu_ret, 1); \
440  cp = strchr(buf, 'X');
441 
442  /* %s should append '\0' */
444  if (cp != NULL) {
445  /* cp != NULL implies strchr found 'X' before finding '\0' */
446  bu_exit(1, "\t[FAIL] bu_sscanf didn't null-terminate %%s conversion.\n");
447  }
448 
449  /* %[...] should append '\0' */
451  if (cp != NULL) {
452  /* cp != NULL implies strchr found 'X' before finding '\0' */
453  bu_exit(1, "\t[FAIL] bu_sscanf null-terminated %%[...] conversion.\n");
454  }
455 
456  /* %c should NOT append '\0' */
458  if (cp == NULL) {
459  /* cp == NULL implies strchr found '\0' before finding 'X' */
460  bu_exit(1, "\t[FAIL] bu_sscanf null-terminated %%c conversion.\n");
461  }
462 
463  /* For %s sscanf should not include leading whitespace in the string, so
464  * the following should all be equivalent.
465  */
466  test_sscanf_s(TEST_STR_NOSPACE, "%" TS_STR_WIDTH "s");
467  test_sscanf_s(TEST_STR_NOSPACE, " %" TS_STR_WIDTH "s");
468  test_sscanf_s(TEST_STR_SPACE, "%" TS_STR_WIDTH "s");
469  test_sscanf_s(TEST_STR_SPACE, " %" TS_STR_WIDTH "s");
470 
471  /* For %c, leading whitespace should be included unless the conversion
472  * specifier is preceded by whitespace.
473  */
474  test_sscanf_s(TEST_STR_SPACE, "%c"); /* should assign ' ' */
475  test_sscanf_s(TEST_STR_SPACE, " %c"); /* should assign 'a' */
476 
477  /* For %[...], should act like %s, but should assign any/only characters
478  * in the class.
479  */
480  test_sscanf_s(TEST_STR_SPACE, "%" TS_STR_WIDTH "[^A-Z]"); /* should assign " \t\na" */
481  test_sscanf_s(TEST_STR_SPACE, " %" TS_STR_WIDTH "[^A-Z]"); /* should assign "a" */
482  test_sscanf_s(TEST_STR_SPACE, " %" TS_STR_WIDTH "[a-z]"); /* should assign "a" */
483 
484  /* should be able to include literal ] and - characters */
485  test_sscanf_s("[-[- ", "%" TS_STR_WIDTH "[[-]");
486  test_sscanf_s(" zZ [- ", "%" TS_STR_WIDTH "[^[-]");
487  test_sscanf_s(" zZ -[ ", "%" TS_STR_WIDTH "[^[-]");
488 }
489 
490 static void
491 doPointerTests(void)
492 {
493  test_sscanf(SCAN_POINTER, "0", "%p");
494  test_sscanf(SCAN_POINTER, bu_cpp_xstr(UNSIGNED_L_HEX), "%p");
495 
496  test_sscanf(SCAN_PTRDIFF, "0", "%ti");
497  test_sscanf(SCAN_PTRDIFF, "0", "%td");
498  test_sscanf(SCAN_PTRDIFF, "0", "%tu");
499  test_sscanf(SCAN_PTRDIFF, bu_cpp_xstr(SIGNED_L_DEC), "%ti");
500  test_sscanf(SCAN_PTRDIFF, bu_cpp_xstr(SIGNED_L_DEC), "%td");
501  test_sscanf(SCAN_PTRDIFF, bu_cpp_xstr(UNSIGNED_L_DEC), "%tu");
502 
503  test_sscanf(SCAN_PTRDIFF, "000", "%ti");
504  test_sscanf(SCAN_PTRDIFF, "000", "%to");
505  test_sscanf(SCAN_PTRDIFF, bu_cpp_xstr(SIGNED_L_OCT), "%ti");
506  test_sscanf(SCAN_PTRDIFF, bu_cpp_xstr(UNSIGNED_L_OCT), "%to");
507 
508  test_sscanf(SCAN_PTRDIFF, "0x0", "%ti");
509  test_sscanf(SCAN_PTRDIFF, "0x0", "%tx");
510  test_sscanf(SCAN_PTRDIFF, bu_cpp_xstr(SIGNED_L_HEX), "%ti");
511  test_sscanf(SCAN_PTRDIFF, bu_cpp_xstr(UNSIGNED_L_HEX), "%tx");
512 }
513 
514 static void
515 test_sscanf_noconv(const char *src, const char *fmt)
516 {
517  int count, bu_count, ret, bu_ret;
518 
519  count = bu_count = 0;
520 
521  print_src_and_fmt(src, fmt);
522 
523  ret = sscanf(src, fmt, &count);
524  bu_ret = bu_sscanf(src, fmt, &bu_count);
525 
526  checkReturnVal("sscanf", ret, 0);
527  checkReturnsEqual(bu_ret, ret);
528 
529  if (bu_count != count) {
530  bu_exit(1, "\t[FAIL] sscanf consumed %d chars, "
531  "but bu_sscanf consumed %d.\n", count, bu_count);
532  }
533 }
534 
535 static void
536 doNonConversionTests(void)
537 {
538  /* %n - don't convert/assign, but do store consumed char count */
539  test_sscanf_noconv(". \tg\t\tn i RTSA si sihT", ". g n i RTSA%n");
540  test_sscanf_noconv(" foo", "foo%n");
541 
542  /* %% - don't convert/assign, but do scan literal % */
543  test_sscanf_noconv("%%n %", "%%%%n %%%n");
544 
545  /* suppressed assignments */
546  test_sscanf_noconv(bu_cpp_xstr(SIGNED_DEC), "%*d%n");
547  test_sscanf_noconv(bu_cpp_xstr(UNSIGNED_DEC), "%*u%n");
548  test_sscanf_noconv(bu_cpp_xstr(LARGE_FLT), "%*f%n");
549  test_sscanf_noconv("42 42 4.2e1", "%*d %*u %*f%n");
550 }
551 
552 #define TS_NUM_ASSIGNMENTS 3
553 
554 static void
555 test_string_width(const char *src, const char *fmt)
556 {
557  int i, j, ret, bu_ret;
558  char str_vals[TS_NUM_ASSIGNMENTS][TS_STR_SIZE];
559  char bu_str_vals[TS_NUM_ASSIGNMENTS][TS_STR_SIZE];
560 
561  print_src_and_fmt(src, fmt);
562 
563  /* init so that 'X' appears after the last char written by bu_sscanf */
564  for (i = 0; i < TS_NUM_ASSIGNMENTS; ++i) {
565  memset(str_vals[i], 'X', TS_STR_SIZE);
566  memset(bu_str_vals[i], 'X', TS_STR_SIZE);
567  }
568 
569  ret = sscanf(src, fmt, str_vals[0], str_vals[1], str_vals[2]);
570  bu_ret = bu_sscanf(src, fmt, bu_str_vals[0], bu_str_vals[1],
571  bu_str_vals[2]);
572  checkReturnVal("sscanf", ret, 3);
573  checkReturnsEqual(bu_ret, ret);
574 
575  /* each str should be exactly equivalent to each bu_str */
576  for (i = 0; i < TS_NUM_ASSIGNMENTS; ++i) {
577  for (j = 0; j < TS_STR_SIZE - 1; ++j) {
578  if (str_vals[i][j] != bu_str_vals[i][j]) {
579 
580  /* terminate for printing */
581  str_vals[i][j + 1] = '\0';
582  bu_str_vals[i][j + 1] = '\0';
583 
584  bu_exit(1, "\t[FAIL] conversion value mismatch.\n"
585  "\t(sscanf) %s != %s (bu_sscanf)\n",
586  str_vals[i], bu_str_vals[i]);
587  }
588  }
589  }
590 }
591 
592 static void
593 doWidthTests(void)
594 {
595  int i;
596  int ret, bu_ret;
597  void *val, *bu_val, *vals, *bu_vals;
598 
599 #define SCAN_3_VALS(type, src, fmt) \
600  print_src_and_fmt(src, fmt); \
601  vals = bu_malloc(sizeof(type) * TS_NUM_ASSIGNMENTS, "test_sscanf vals"); \
602  bu_vals = bu_malloc(sizeof(type) * TS_NUM_ASSIGNMENTS, "test_sscanf bu_vals"); \
603  ret = sscanf(src, fmt, &((type*)vals)[0], &((type*)vals)[1], &((type*)vals)[2]); \
604  bu_ret = bu_sscanf(src, fmt, &((type*)bu_vals)[0], &((type*)bu_vals)[1], &((type*)bu_vals)[2]); \
605  checkReturnVal("sscanf", ret, 3); \
606  checkReturnsEqual(bu_ret, ret);
607 
608 #define TEST_INT_WIDTH(type, pfmt, src, fmt) \
609  SCAN_3_VALS(type, src, fmt); \
610  for (i = 0; i < TS_NUM_ASSIGNMENTS; ++i) { \
611  val = (void*)&((type*)vals)[i]; \
612  bu_val = (void*)&((type*)bu_vals)[i]; \
613  CHECK_INT_VALS_EQUAL(type, pfmt, val, bu_val); \
614  } \
615  bu_free(vals, "test_sscanf vals"); \
616  bu_free(bu_vals, "test_sscanf bu_vals");
617 
618 #define TEST_FLOAT_WIDTH(type, pfmt, src, fmt) \
619  SCAN_3_VALS(type, src, fmt); \
620  for (i = 0; i < TS_NUM_ASSIGNMENTS; ++i) { \
621  val = (void*)&((type*)vals)[i]; \
622  bu_val = (void*)&((type*)bu_vals)[i]; \
623  CHECK_FLOAT_VALS_EQUAL(type, pfmt, val, bu_val); \
624  } \
625  bu_free(vals, "test_sscanf vals"); \
626  bu_free(bu_vals, "test_sscanf bu_vals");
627 
628  /* Stop at non-matching even if width not met. */
629  TEST_INT_WIDTH(int, d, "12 34 5a6", "%5d %5d %5d");
630  TEST_INT_WIDTH(int, i, "12 0042 0x3z8", "%5i %5i %5i");
631  TEST_INT_WIDTH(unsigned, x, "0xC 0x22 0x38", "%5x %5x %5x");
632  TEST_FLOAT_WIDTH(float, f, ".0012 .34 56.0a0", "%10f %10f %10f");
633  TEST_FLOAT_WIDTH(double, f, ".0012 .34 56.0a0", "%10lf %10lf %10lf");
634  test_string_width("aa AA aa", "%5s %5s %5s");
635  test_string_width("1234512345123451", "%5c %5c %5c");
636  test_string_width("aaAA zzzzzz", "%5[a]%5[A] %5[z]");
637 
638  /* Stop at width even if there are more matching chars.
639  * Do not include discarded whitespace in count.
640  */
641  TEST_INT_WIDTH(int, d, " 123\t456", " %1d%2d %3d");
642  TEST_INT_WIDTH(int, i, " 10\t0x38", " %1i%1i %4i");
643  TEST_INT_WIDTH(unsigned, x, " 0xC00X22\t0x38", " %4x%3x %3x");
644  TEST_FLOAT_WIDTH(float, f, " .0012\t.3456", " %3f%2f %4f");
645  TEST_FLOAT_WIDTH(double, f, " .0012\t.3456", " %3lf%2lf %4lf");
646  test_string_width(" abc ABCDE", " %2s%1s %4s");
647  test_string_width("abc ABCD", "%2c%2c %4c");
648  test_string_width("aaAA 1%1%1%", "%2[aA]%3[A ] %5[1%]");
649 }
650 
651 static void
652 doErrorTests(void)
653 {
654  int i, ret, bu_ret;
655 
656 #define FMT_ASSIGN3(fmt) \
657  "%" bu_cpp_str(fmt) " %" bu_cpp_str(fmt) " %" bu_cpp_str(fmt)
658 
659 #define FMT_READ2_ASSIGN1(fmt) \
660  "%*" bu_cpp_str(fmt) " %*" bu_cpp_str(fmt) " %" bu_cpp_str(fmt)
661 
662  /* Attempt to assign 3 values from src.
663  * If src begins with an invalid input value, should return 0 to indicate
664  * a matching failure.
665  * If src is empty, should return EOF to indicate input failure.
666  */
667 #define TEST_FAILURE_1(type, type_fmt, type_init, src, expected_err) \
668 { \
669  type vals[3] = { type_init, type_init, type_init }; \
670  print_src_and_fmt(src, FMT_ASSIGN3(type_fmt)); \
671  ret = sscanf(src, FMT_ASSIGN3(type_fmt), &vals[0], &vals[1], &vals[2]); \
672  bu_ret = sscanf(src, FMT_ASSIGN3(type_fmt), &vals[0], &vals[1], &vals[2]); \
673  checkReturnVal("sscanf", ret, expected_err); \
674  checkReturnsEqual(ret, bu_ret); \
675  for (i = 0; i < 3; ++i) { \
676  if (vals[i] != type_init) { \
677  bu_exit(1, "\t[FAIL] No assignment expected, but vals[%d] " \
678  "changed from %" bu_cpp_str(type_fmt) " to %" bu_cpp_str(type_fmt) ".\n", \
679  i, type_init, vals[i]); \
680  } \
681  } \
682 }
683 
684  /* Attempt to read 2 values and assign 1 value from src.
685  * If src includes 2 valid and 1 invalid input value, should return 0 to
686  * indicate matching failure.
687  * If src includes 2 valid values and terminates, should return EOF to
688  * indicate input failure.
689  */
690 #define TEST_FAILURE_2(type, type_fmt, type_init, src, expected_err) \
691 { \
692  type val = type_init; \
693  print_src_and_fmt(src, FMT_READ2_ASSIGN1(type_fmt)); \
694  ret = sscanf(src, FMT_READ2_ASSIGN1(type_fmt), &val); \
695  bu_ret = sscanf(src, FMT_READ2_ASSIGN1(type_fmt), &val); \
696  checkReturnVal("sscanf", ret, expected_err); \
697  checkReturnsEqual(ret, bu_ret); \
698  if (val != type_init) { \
699  bu_exit(1, "\t[FAIL] No assignment expected, but val " \
700  "changed from %" bu_cpp_str(type_fmt) " to %" bu_cpp_str(type_fmt) ".\n", \
701  type_init, val); \
702  } \
703 }
704 
705 #define EXPECT_MATCH_FAILURE 0
706 #define EXPECT_INPUT_FAILURE EOF
707 
708  TEST_FAILURE_1(int, d, 0, "xx 34 56", EXPECT_MATCH_FAILURE);
709  TEST_FAILURE_2(int, d, 0, "12 34 xx", EXPECT_MATCH_FAILURE);
710  TEST_FAILURE_2(int, d, 0, "12 34", EXPECT_INPUT_FAILURE);
711  TEST_FAILURE_1(int, d, 0, "", EXPECT_INPUT_FAILURE);
712 
713  TEST_FAILURE_1(char, 1[123], 'a', "x 2 3", EXPECT_MATCH_FAILURE);
714  TEST_FAILURE_2(char, 1[123], 'a', "1 2 x", EXPECT_MATCH_FAILURE);
715  TEST_FAILURE_2(char, 1[123], 'a', "1 2", EXPECT_INPUT_FAILURE);
716  TEST_FAILURE_1(char, 1[123], 'a', "", EXPECT_INPUT_FAILURE);
717 }
718 
719 static void
720 test_vls(const char *src, const char *fmt, const char *expectedStr)
721 {
722  int bu_ret;
723  struct bu_vls vls = BU_VLS_INIT_ZERO;
724 
725  print_src_and_fmt(src, fmt);
726 
727  bu_ret = bu_sscanf(src, fmt, &vls);
728  checkReturnVal("bu_sscanf", bu_ret, 1);
729 
730  if (!BU_STR_EQUAL(bu_vls_addr(&vls), expectedStr)) {
731  bu_vls_free(&vls);
732  bu_exit(1, "\t[FAIL] \"%s\" was assigned to vls instead of \"%s\".\n",
733  bu_vls_addr(&vls), expectedStr);
734  }
735  bu_vls_free(&vls);
736 }
737 
738 static void
739 doVlsTests(void)
740 {
741  int bu_ret;
742  struct bu_vls vls = BU_VLS_INIT_ZERO;
743 
744  /* %V */
745  test_vls("de mus noc", "%V", "de mus noc");
746  test_vls(" de mus noc", "%6V", " de mu");
747  test_vls(" de mus noc", " %7V", "de mus ");
748  test_vls("de mus noc", "%11V", "de mus noc");
749 
750  print_src_and_fmt("de mus noc", "%*11V");
751  bu_ret = bu_sscanf("de mus noc", "%*11V", &vls);
752  checkReturnVal("bu_sscanf", bu_ret, 0);
753 
754  /* %#V */
755  test_vls(" \tabc ABC", "%#V", "abc");
756  test_vls(" \tabc ABC", "%#4V", "abc");
757  test_vls(" \tabc", "%#4Vs", "abc");
758 
759  print_src_and_fmt(" \tabc ABC", "%#*V");
760  bu_vls_trunc(&vls, 0);
761  bu_ret = bu_sscanf(" \tabc ABC", "%#*V", &vls);
762  checkReturnVal("bu_sscanf", bu_ret, 0);
763 
764  bu_vls_free(&vls);
765 }
766 
767 int
768 main(int argc, char *argv[])
769 {
770  if (argc > 1) {
771  printf("Warning: %s takes no arguments.\n", argv[0]);
772  }
773 
774  printf("bu_sscanf: running tests\n");
775 
776  doNumericTests();
777  doStringTests();
778  doPointerTests();
779  doNonConversionTests();
780  doWidthTests();
781  doErrorTests();
782  doVlsTests();
783 
784  printf("bu_sscanf: testing complete\n");
785  return 0;
786 }
787 
788 /*
789  * Local Variables:
790  * mode: C
791  * tab-width: 8
792  * indent-tabs-mode: t
793  * c-file-style: "stroustrup"
794  * End:
795  * ex: shiftwidth=4 tabstop=8
796  */
Definition: db_flip.c:35
#define UNSIGNED_HH_DEC
Definition: bu_sscanf.c:283
#define SIGNED_L_DEC
Definition: bu_sscanf.c:296
#define SSCANF_TYPE(type)
#define SIGNED_HH_HEX
Definition: bu_sscanf.c:286
#define SIGNED_OCT
Definition: bu_sscanf.c:291
lu
Definition: nmg_mod.c:3855
#define UNSIGNED_OCT
Definition: bu_sscanf.c:292
void bu_vls_trunc(struct bu_vls *vp, int len)
Definition: vls.c:198
#define UNSIGNED_DEC
Definition: bu_sscanf.c:290
#define CHECK_INT_VALS_EQUAL(int_type, pfmt, valp, bu_valp)
Definition: bu_sscanf.c:151
#define UNSIGNED_HH_OCT
Definition: bu_sscanf.c:285
Header file for the BRL-CAD common definitions.
#define CHECK_FLOAT_VALS_EQUAL(float_type, pfmt, valp, bu_valp)
Definition: bu_sscanf.c:163
#define TS_STR_SIZE
Definition: bu_sscanf.c:37
#define TS_STR_WIDTH
Definition: bu_sscanf.c:38
#define TEST_FAILURE_2(type, type_fmt, type_init, src, expected_err)
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
#define SIGNED_DEC
Definition: bu_sscanf.c:289
Definition: color.c:49
void * memset(void *s, int c, size_t n)
#define SIGNED_L_OCT
Definition: bu_sscanf.c:298
void bu_exit(int status, const char *fmt,...) _BU_ATTR_NORETURN _BU_ATTR_PRINTF23
Definition: bomb.c:195
#define SIGNED_HEX
Definition: bu_sscanf.c:293
#define TEST_FLOAT_WIDTH(type, pfmt, src, fmt)
#define UNSIGNED_L_DEC
Definition: bu_sscanf.c:297
#define UNSIGNED_L_OCT
Definition: bu_sscanf.c:299
int bu_sscanf(const char *src, const char *fmt,...) _BU_ATTR_SCANF23
Definition: sscanf.c:676
#define UNSIGNED_HEX
Definition: bu_sscanf.c:294
#define TEST_STR_SPACE
#define SIGNED_HH_OCT
Definition: bu_sscanf.c:284
int test_vls(const char *fmt,...)
#define TEST_INT_WIDTH(type, pfmt, src, fmt)
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
int main(int argc, char *argv[])
Definition: bu_sscanf.c:768
#define SIGNED_HH_DEC
Definition: bu_sscanf.c:282
#define UNSIGNED_HH_HEX
Definition: bu_sscanf.c:287
#define TS_NUM_ASSIGNMENTS
Definition: bu_sscanf.c:552
#define LARGE_FLT
Definition: bu_sscanf.c:304
#define SIGNED_L_HEX
Definition: bu_sscanf.c:300
#define EXPECT_MATCH_FAILURE
#define bu_cpp_xstr(s)
Definition: defines.h:139
#define TEST_UNSIGNED_CONSTANTS(small, med, large, fmt)
#define A
Definition: msr.c:51
#define TEST_SIGNED_CONSTANT(str, fmt)
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
#define TEST_FAILURE_1(type, type_fmt, type_init, src, expected_err)
#define TEST_FLOAT_VARIANT(fmt)
Definition: vls.h:56
#define UNSIGNED_L_HEX
Definition: bu_sscanf.c:301
#define TEST_TERMINATION(src, fmt)
#define EXPECT_INPUT_FAILURE
#define TEST_SIGNED_CONSTANTS(small, med, large, fmt)
#define TEST_UNSIGNED_CONSTANT(str, fmt)
#define TEST_STR_NOSPACE
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126