NEURON
nrn_vsscanf.cpp
Go to the documentation of this file.
1 /* modified for use for NrnFILEWrap */
2 /* now included by nrnfilewrap.cpp */
3 /*
4  * libslack - http://libslack.org/
5  *
6  * Copyright (C) 1999-2010 raf <raf@raf.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  * or visit http://www.gnu.org/copyleft/gpl.html
22  *
23  * 20100612 raf <raf@raf.org>
24  */
25 
26 /*
27 
28 =head1 NAME
29 
30 I<vsscanf(3)> - I<sscanf(3)> with a I<va_list> parameter
31 
32 =head1 SYNOPSIS
33 
34  #include <slack/std.h>
35  #ifndef HAVE_VSSCANF
36  #include <slack/vsscanf.h>
37  #endif
38 
39  int vsscanf(const char *str, const char *format, va_list args);
40 
41 =head1 DESCRIPTION
42 
43 Similar to I<sscanf(3)> with the variable argument list specified directly
44 as for I<vprintf(3)>.
45 
46 Note that this may not be identical in behaviour to the I<sscanf(3)> on your
47 system because this was implemented from scratch for systems that lack
48 I<vsscanf(3)>. So your I<sscanf(3)> and this I<vsscanf(3)> share no common
49 code. Your I<sscanf(3)> may support extensions that I<vsscanf(3)> does not
50 support. I<vsscanf(3)> complies with all of the relevant ISO C requirements
51 for I<sscanf(3)> except:
52 
53 =over 4
54 
55 =item *
56 
57 C<format> may not be a multibyte character string; and
58 
59 =item *
60 
61 Scanning a pointer (C<"%p">) may not exactly match the format that your
62 I<printf(3)> uses to print pointers on your system. This version accepts
63 pointers as a hexadecimal number with or without a preceding C<0x>.
64 
65 =back
66 
67 =head1 MT-Level
68 
69 MT-Safe if and only if no thread calls I<setlocale(3)>. Since locales are
70 inherently non-threadsafe as they are currently defined, this shouldn't be a
71 problem. Just call C<setlocale(LC_ALL, "")> once after program
72 initialisation and never again (at least not after creating any threads). If
73 it is a problem, just change C<localeconv()-E<gt>decimal_point[0]> in the source
74 to C<'.'> and it will be MT-Safe at the expense of losing locale support.
75 
76 =head1 EXAMPLE
77 
78  #include <slack/std.h>
79  #ifndef HAVE_VSSCANF
80  #include <slack/vsscanf.h>
81  #endif
82 
83  int fdscanf(int fd, const char *format, ...)
84  {
85  va_list args;
86  char buf[BUFSIZ];
87  ssize_t bytes;
88  int rc;
89 
90  if ((bytes = read(fd, buf, BUFSIZ)) <= 0)
91  return bytes;
92 
93  buf[bytes] = '\0';
94 
95  va_start(args, format);
96  rc = vsscanf(buf, format, args);
97  va_end(args);
98 
99  return rc;
100  }
101 
102  int main()
103  {
104  int rc, a = 0, b = 0;
105 
106  rc = fdscanf(STDIN_FILENO, "%d %d", &a, &b);
107  printf("rc = %d a = %d b = %d\n", rc, a, b);
108 
109  return (rc == 2) ? EXIT_SUCCESS : EXIT_FAILURE;
110  }
111 
112 =head1 NOTE
113 
114 I<gcc(1)> warns that:
115 
116  warning: ANSI C does not support the `L' length modifier
117  warning: use of `l' length character with `e' type character
118 
119 However, the ANSI C standard (Section 7.9.6.2) states that:
120 
121 "Finally, the conversion specifiers C<e>, C<f>, and C<g> shall be preceded
122 by C<l> if the corresponding argument is a pointer to I<double> rather than
123 a pointer to C<float>, or by C<L> if it is a pointer to I<long double>."
124 
125 I have chosen to disregard the I<gcc(1)> warnings in favour of the standard.
126 If you see the above warnings when compiling the unit tests for
127 I<vsscanf(3)>, just ignore them.
128 
129 =head1 SEE ALSO
130 
131 I<libslack(3)>,
132 I<sscanf(3)>
133 
134 =head1 AUTHOR
135 
136 20100612 raf <raf@raf.org>
137 
138 =cut
139 
140 */
141 
142 #if USE_NRNFILEWRAP
143 #else
144 #include "config.h"
145 #include "std.h"
146 
147 #include <locale.h>
148 #endif
149 
150 #if USE_NRNFILEWRAP
151 int nrn_vsscanf(const char* str, const char** rs, const char* format, va_list args)
152 #else
153 int vsscanf(const char* str, const char* format, va_list args)
154 #endif
155 {
156  const char *f, *s;
157 #if USE_NRNFILEWRAP
158  const char point = '.';
159 #else
160  const char point = localeconv()->decimal_point[0];
161 #endif
162  int cnv = 0;
163 
164  for (s = str, f = format; *f; ++f) {
165  if (*f == '%') {
166  int size = 0;
167  int width = 0;
168  int do_cnv = 1;
169 
170  if (*++f == '*')
171  ++f, do_cnv = 0;
172 
173  for (; isdigit((int) (unsigned int) *f); ++f)
174  width *= 10, width += *f - '0';
175 
176  if (*f == 'h' || *f == 'l' || *f == 'L')
177  size = *f++;
178 
179  if (*f != '[' && *f != 'c' && *f != 'n')
180  while (isspace((int) (unsigned int) *s))
181  ++s;
182 
183 #define COPY *b++ = *s++, --width
184 #define MATCH(cond) \
185  if (width && (cond)) \
186  COPY;
187 #define MATCH_ACTION(cond, action) \
188  if (width && (cond)) { \
189  COPY; \
190  action; \
191  }
192 #define MATCHES_ACTION(cond, action) \
193  while (width && (cond)) { \
194  COPY; \
195  action; \
196  }
197 #define FAIL (cnv) ? cnv : EOF
198  switch (*f) {
199  case 'd':
200  case 'i':
201  case 'o':
202  case 'u':
203  case 'x':
204  case 'X':
205  case 'p': {
206  static const char types[] = "diouxXp";
207  static const int bases[] = {10, 0, 8, 10, 16, 16, 16};
208  static const char digitset[] = "0123456789abcdefABCDEF";
209  static const int setsizes[] = {
210  10, 0, 0, 0, 0, 0, 0, 0, 8, 0, 10, 0, 0, 0, 0, 0, 22};
211  int base = bases[strchr(types, *f) - types];
212  int setsize;
213  char buf[513];
214  char* b = buf;
215  int digit = 0;
216  if (width <= 0 || width > 512)
217  width = 512;
218  MATCH(*s == '+' || *s == '-')
219  MATCH_ACTION(*s == '0', digit = 1;
220  MATCH_ACTION((*s == 'x' || *s == 'X') && (base == 0 || base == 16),
221  base = 16) else base = 8;)
222  setsize = setsizes[base];
223  MATCHES_ACTION(memchr(digitset, *s, setsize), digit = 1)
224  if (!digit) {
225  *rs = s;
226  return FAIL;
227  }
228  *b = '\0';
229  if (do_cnv) {
230  if (*f == 'd' || *f == 'i') {
231  long data = strtol(buf, NULL, base);
232  if (size == 'h')
233  *va_arg(args, short*) = (short) data;
234  else if (size == 'l')
235  *va_arg(args, long*) = data;
236  else
237  *va_arg(args, int*) = (int) data;
238  } else {
239  unsigned long data = strtoul(buf, NULL, base);
240  if (size == 'p')
241  *va_arg(args, void**) = (void*) data;
242  else if (size == 'h')
243  *va_arg(args, unsigned short*) = (unsigned short) data;
244  else if (size == 'l')
245  *va_arg(args, unsigned long*) = data;
246  else
247  *va_arg(args, unsigned int*) = (unsigned int) data;
248  }
249  ++cnv;
250  }
251  break;
252  }
253 
254  case 'e':
255  case 'E':
256  case 'f':
257  case 'g':
258  case 'G': {
259  char buf[513];
260  char* b = buf;
261  int digit = 0;
262  if (width <= 0 || width > 512)
263  width = 512;
264  MATCH(*s == '+' || *s == '-')
265  MATCHES_ACTION(isdigit((int) (unsigned int) *s), digit = 1)
266  MATCH(*s == point)
267  MATCHES_ACTION(isdigit((int) (unsigned int) *s), digit = 1)
268  MATCHES_ACTION(digit && (*s == 'e' || *s == 'E'),
269  MATCH(*s == '+' || *s == '-') digit = 0;
270  MATCHES_ACTION(isdigit((int) (unsigned int) *s), digit = 1))
271  if (!digit) {
272  *rs = s;
273  return FAIL;
274  }
275  *b = '\0';
276  if (do_cnv) {
277  double data = strtod(buf, NULL);
278  if (size == 'l')
279  *va_arg(args, double*) = data;
280  else if (size == 'L')
281  *va_arg(args, long double*) = (long double) data;
282  else
283  *va_arg(args, float*) = (float) data;
284  ++cnv;
285  }
286  break;
287  }
288 
289  case 's': {
290  char* arg = va_arg(args, char*);
291  if (width <= 0)
292  width = INT_MAX;
293  while (width-- && *s && !isspace((int) (unsigned int) *s))
294  if (do_cnv)
295  *arg++ = *s++;
296  if (do_cnv)
297  *arg = '\0', ++cnv;
298  break;
299  }
300 
301  case '[': {
302  char* arg = va_arg(args, char*);
303  int setcomp = 0;
304  size_t setsize;
305  const char* end;
306  if (width <= 0)
307  width = INT_MAX;
308  if (*++f == '^')
309  setcomp = 1, ++f;
310  end = strchr((*f == ']') ? f + 1 : f, ']');
311  if (!end) {
312  *rs = s;
313  return FAIL;
314  } /* Could be cnv to match glibc-2.2 */
315  setsize = end - f; /* But FAIL matches the C standard */
316  while (width-- && *s) {
317  if (!setcomp && !memchr(f, *s, setsize))
318  break;
319  if (setcomp && memchr(f, *s, setsize))
320  break;
321  if (do_cnv)
322  *arg++ = *s++;
323  }
324  if (do_cnv)
325  *arg = '\0', ++cnv;
326  f = end;
327  break;
328  }
329 
330  case 'c': {
331  char* arg = va_arg(args, char*);
332  if (width <= 0)
333  width = 1;
334  while (width--) {
335  if (!*s) {
336  *rs = s;
337  return FAIL;
338  }
339  if (do_cnv)
340  *arg++ = *s++;
341  }
342  if (do_cnv)
343  ++cnv;
344  break;
345  }
346 
347  case 'n': {
348  if (size == 'h')
349  *va_arg(args, short*) = (short) (s - str);
350  else if (size == 'l')
351  *va_arg(args, long*) = (long) (s - str);
352  else
353  *va_arg(args, int*) = (int) (s - str);
354  break;
355  }
356 
357  case '%': {
358  if (*s++ != '%') {
359  *rs = s;
360  return cnv;
361  }
362  break;
363  }
364 
365  default:
366  *rs = s;
367  return FAIL;
368  }
369  } else if (isspace((int) (unsigned int) *f)) {
370  while (isspace((int) (unsigned int) f[1]))
371  ++f;
372  while (isspace((int) (unsigned int) *s))
373  ++s;
374  } else {
375  if (*s++ != *f) {
376  *rs = s;
377  return cnv;
378  }
379  }
380  }
381 
382  *rs = s;
383  return cnv;
384 }
385 
386 #if 0
387 #ifdef TEST
388 
389 #undef _ISOC9X_SOURCE
390 #undef __USE_ISOC9X
391 #include <math.h>
392 #include <float.h>
393 
394 int test_sscanf(const char *str, const char *format, ...)
395 {
396  int rc;
397  va_list args;
398  va_start(args, format);
399  rc = vsscanf(str, format, args);
400  va_end(args);
401  return rc;
402 }
403 
404 int main(int ac, char **av)
405 {
406  int errors = 0;
407  short si1, si2;
408  int i1, i2;
409  long li1, li2;
410  float f1, f2;
411  double d1, d2;
412  long double ld1, ld2;
413  void *p1, *p2;
414  char b1[128], b2[128];
415  char c1[128], c2[128];
416  char s1[128], s2[128];
417  short sn1, sn2;
418  int in1, in2;
419  long ln1, ln2;
420  unsigned short su1, su2;
421  unsigned int u1, u2;
422  unsigned long lu1, lu2;
423  char str[512];
424  int rc1, rc2;
425 
426  if (ac == 2 && !strcmp(av[1], "help"))
427  {
428  printf("usage: %s [show]\n", *av);
429  return EXIT_SUCCESS;
430  }
431 
432  printf("Testing: %s\n", "vsscanf");
433 
434  /* Test one of everything */
435 
436  sprintf(str, " abc -12 37 101 3.4e-1 12.34 102.23 xyz %p def ghi jkl %% ",
437  p1 = (void *)0xdeadbeef
438  );
439 
440  if (ac >= 2 && !strcmp(av[1], "show"))
441  printf("%s\n", str);
442 
443  rc1 = sscanf(str,
444  " abc %hd %d %ld %e %le %Le xyz %p %[^abc ] %3c %s%hn %n%% %ln",
445  &si1, &i1, &li1, &f1, &d1, &ld1, &p1, b1, c1, s1, &sn1, &in1, &ln1
446  );
447 
448  rc2 = test_sscanf(str,
449  " abc %hd %d %ld %e %le %Le xyz %p %[^abc ] %3c %s%hn %n%% %ln",
450  &si2, &i2, &li2, &f2, &d2, &ld2, &p2, b2, c2, s2, &sn2, &in2, &ln2
451  );
452 
453  if (rc1 != rc2)
454  ++errors, printf("Test1: failed (returned %d, not %d)\n", rc2, rc1);
455  if (si1 != si2)
456  ++errors, printf("Test2: failed (%%hd scanned %hd, not %hd)\n", si2, si1);
457  if (i1 != i2)
458  ++errors, printf("Test3: failed (%%d scanned %d, not %d)\n", i2, i1);
459  if (li1 != li2)
460  ++errors, printf("Test4: failed (%%ld scanned %ld, not %ld)\n", li2, li1);
461  if (fabs(f2 - 3.4e-1) / 3.4e-1 >= 4 * FLT_EPSILON)
462  ++errors, printf("Test5: failed (%%e scanned %e, not %e)\n", f2, f1);
463  if (fabs(d2 - 12.34) / 12.34 >= 4 * DBL_EPSILON)
464  ++errors, printf("Test6: failed (%%le scanned %le, not %le)\n", d2, d1);
465  if (fabs(ld2 - 102.23) / 102.23 >= 4 * LDBL_EPSILON)
466  ++errors, printf("Test7: failed (%%Le scanned %Le, not %Le)\n", ld2, ld1);
467  if (p1 != p2)
468  ++errors, printf("Test8: failed (%%p scanned %p, not %p)\n", p2, p1);
469  if (strcmp(b1, b2))
470  ++errors, printf("Test9: failed (%%[^abc ] scanned \"%s\", not \"%s\")\n", b2, b1);
471  if (memcmp(c1, c2, 3))
472  ++errors, printf("Test10: failed (%%3c scanned \"%3.3s\", not \"%3.3s\")\n", c2, c1);
473  if (strcmp(s1, s2))
474  ++errors, printf("Test11: failed (%%s scanned \"%s\", not \"%s\")\n", s2, s1);
475  if (sn1 != sn2)
476  ++errors, printf("Test12: failed (%%hn scanned %hd, not %hd)\n", sn2, sn1);
477  if (in1 != in2)
478  ++errors, printf("Test13: failed (%%n scanned %d, not %d)\n", in2, in1);
479  if (ln1 != ln2)
480  ++errors, printf("Test14: failed (%%ln scanned %ld, not %ld)\n", ln2, ln1);
481 
482  /* Test different numeric bases */
483 
484 #define TEST_NUM(i, var, tst, str, format) \
485  rc1 = sscanf(str, format, &s##var##1, &var##1, &l##var##1); \
486  rc2 = test_sscanf(str, format, &s##var##2, &var##2, &l##var##2); \
487  if (rc1 != rc2) \
488  ++errors, printf("Test%d: failed (returned %d, not %d)\n", (i), rc2, rc1); \
489  if (s##var##1 != s##var##2) \
490  ++errors, printf("Test%d: failed (%%h%c scanned %hd, not %hd)\n", \
491  (i), \
492  tst, \
493  s##var##2, \
494  s##var##1); \
495  if (var##1 != var##2) \
496  ++errors, printf("Test%d: failed (%%%c scanned %d, not %d)\n", (i), tst, var##2, var##1); \
497  if (l##var##1 != l##var##2) \
498  ++errors, \
499  printf("Test%d: failed (%%l%c scanned %ld, not %ld)\n", (i), tst, l##var##2, l##var##1)
500 
501 #define TEST_STR(i, len, str, format) \
502  rc1 = sscanf(str, format, b1, c1, s1); \
503  rc2 = test_sscanf(str, format, b2, c2, s2); \
504  if (rc1 != rc2) \
505  ++errors, printf("Test%d: failed (returned %d, not %d)\n", (i), rc2, rc1); \
506  if (strcmp(b1, b2)) \
507  ++errors, \
508  printf("Test%d: failed (%%%d[ scanned \"%s\", not \"%s\")\n", (i), (len), b2, b1); \
509  if (memcmp(c1, c2, len)) \
510  ++errors, printf("Test%d: failed (%%%dc scanned \"%*.*s\", not \"%*.*s\")\n", \
511  (i), \
512  (len), \
513  (len), \
514  (len), \
515  c2, \
516  (len), \
517  (len), \
518  c1); \
519  if (strcmp(s1, s2)) \
520  ++errors, printf("Test%d: failed (%%%ds scanned \"%s\", not \"%s\")\n", (i), (len), s2, s1)
521 
522  TEST_NUM(15, i, 'i', "37 21 53", "%hi %i %li");
523  TEST_NUM(16, i, 'i', "037 021 053", "%hi %i %li");
524  TEST_NUM(17, i, 'i', "0x37 0x21 0x53", "%hi %i %li");
525  TEST_NUM(18, u, 'o', "037 021 053", "%ho %o %lo");
526  TEST_NUM(19, u, 'u', "37 21 53", "%hu %u %lu");
527  TEST_NUM(20, u, 'x', "0x37 0x21 0x53", "%hx %x %lx");
528  TEST_NUM(21, u, 'X', "0x37 0x21 0x53", "%hx %x %lx");
529 
530  /* Test field width handling */
531 
532  TEST_NUM(22, i, 'd', "123456789", "%3hd %2d %4ld");
533  TEST_NUM(23, i, 'i', "123456789", "%3hi %2i %4li");
534  TEST_NUM(24, i, 'i', "012340789", "%3hi %2i %4li");
535  TEST_NUM(25, i, 'x', "123456789", "%3hi %2i %4li");
536  TEST_NUM(26, u, 'o', "012340789", "%3ho %2o %4lo");
537  TEST_NUM(27, u, 'u', "123456789", "%3hu %2u %4lu");
538  TEST_NUM(28, u, 'x', "123456789", "%3hx %2x %4lx");
539  TEST_NUM(29, u, 'X', "123456789", "%3hx %2X %4lX");
540  TEST_STR(30, 1, "abcd", "%1[a]%c%1s");
541 
542  TEST_STR(31, 5, "abc\001d e f g h i", "%5[][abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_=+\\|{};':\",./<>? -]%5c%5s");
543  TEST_STR(32, 3, "abc\001d e f g h i", "%3[][abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_=+\\|{};':\",./<>? -]%3c%3s");
544  TEST_STR(33, 7, "abc\001d e f g h i", "%7[][abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_=+\\|{};':\",./<>? -]%7c%7s");
545  TEST_STR(34, 2, "abc\001d e f g h i", "%2[][abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_=+\\|{};':\",./<>? -]%2c%2s");
546 
547  /* Test error reporting */
548 
549 #define TEST_ERR(i, str, format) \
550  rc1 = sscanf(str, format); \
551  rc2 = test_sscanf(str, format); \
552  if (rc1 != rc2) \
553  ++errors, printf("Test%d: failed (returned %d, not %d)\n", (i), rc2, rc1)
554 
555 #define TEST_ERR_ARG(i, str, format, var) \
556  rc1 = sscanf(str, format, &var##1); \
557  rc2 = test_sscanf(str, format, &var##1); \
558  if (rc1 != rc2) \
559  ++errors, printf("Test%d: failed (returned %d, not %d)\n", (i), rc2, rc1)
560 
561  TEST_ERR_ARG(35, "", "%d", i);
562  TEST_ERR_ARG(36, "", "%i", i);
563  TEST_ERR_ARG(37, "", "%o", u);
564  TEST_ERR_ARG(38, "", "%u", u);
565  TEST_ERR_ARG(39, "", "%x", u);
566  TEST_ERR_ARG(40, "", "%X", u);
567  TEST_ERR_ARG(41, "", "%p", p);
568  TEST_ERR_ARG(42, "", "%e", f);
569  TEST_ERR_ARG(43, "", "%E", f);
570  TEST_ERR_ARG(44, "", "%f", f);
571  TEST_ERR_ARG(45, "", "%g", f);
572  TEST_ERR_ARG(46, "", "%G", f);
573  TEST_ERR_ARG(47, "", "%[^]", *b);
574  TEST_ERR_ARG(48, "", "%c", *c);
575  TEST_ERR(49, "a", "%%");
576  TEST_ERR(50, "a", "b");
577 
578  if (errors)
579  printf("%d/50 tests failed (This system's sscanf(3) is probably wrong)\n", errors);
580  else
581  printf("All tests passed\n");
582 
583  return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
584 }
585 
586 #endif
587 #endif
588 
589 /* vi:set ts=4 sw=4: */
double point
Definition: coord.h:49
sprintf(buf, " if (secondorder) {\n" " int _i;\n" " for (_i = 0; _i < %d; ++_i) {\n" " _p[_slist%d[_i]] += dt*_p[_dlist%d[_i]];\n" " }}\n", numeqn, listnum, listnum)
#define c
char buf[512]
Definition: init.cpp:13
int main()
Definition: macmain.cpp:5
static char * format
Definition: matrixio.c:386
#define i
Definition: md1redef.h:12
fabs
Definition: extdef.h:3
#define printf
Definition: mwprefix.h:26
int vsscanf(const char *str, const char *format, va_list args)
#define MATCH(cond)
#define MATCH_ACTION(cond, action)
#define MATCHES_ACTION(cond, action)
#define FAIL
size_t p
#define data
Definition: rbtqueue.cpp:49
#define arg
Definition: redef.h:28
#define NULL
Definition: sptree.h:16