doscan.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. * doscan.c - scan formatted input
  3. */
  4. /* $Header$ */
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <ctype.h>
  8. #include <stdarg.h>
  9. #include "loc_incl.h"
  10. #define NUMLEN 512
  11. #define NR_CHARS 256
  12. static char Xtable[NR_CHARS];
  13. static char inp_buf[NUMLEN];
  14. /* Collect a number of characters which constitite an ordinal number.
  15. * When the type is 'i', the base can be 8, 10, or 16, depending on the
  16. * first 1 or 2 characters. This means that the base must be adjusted
  17. * according to the format of the number. At the end of the function, base
  18. * is then set to 0, so strtol() will get the right argument.
  19. */
  20. static char *
  21. o_collect(register int c, register FILE *stream, char type,
  22. int width, int *basep)
  23. {
  24. register char *bufp = inp_buf;
  25. register int base;
  26. switch (type) {
  27. case 'i': /* i means octal, decimal or hexadecimal */
  28. case 'p':
  29. case 'x':
  30. case 'X': base = 16; break;
  31. case 'd':
  32. case 'u': base = 10; break;
  33. case 'o': base = 8; break;
  34. case 'b': base = 2; break;
  35. }
  36. if (c == '-' || c == '+') {
  37. *bufp++ = c;
  38. if (--width);
  39. c = getc(stream);
  40. }
  41. if (width && c == '0' && base == 16) {
  42. *bufp++ = c;
  43. if (--width)
  44. c = getc(stream);
  45. if (c != 'x' && c != 'X') {
  46. if (type == 'i') base = 8;
  47. }
  48. else if (width) {
  49. *bufp++ = c;
  50. if (--width)
  51. c = getc(stream);
  52. }
  53. }
  54. else if (type == 'i') base = 10;
  55. while (width) {
  56. if (((base == 10) && isdigit(c))
  57. || ((base == 16) && isxdigit(c))
  58. || ((base == 8) && isdigit(c) && (c < '8'))
  59. || ((base == 2) && isdigit(c) && (c < '2'))) {
  60. *bufp++ = c;
  61. if (--width)
  62. c = getc(stream);
  63. }
  64. else break;
  65. }
  66. if (width && c != EOF) ungetc(c, stream);
  67. if (type == 'i') base = 0;
  68. *basep = base;
  69. *bufp = '\0';
  70. return bufp - 1;
  71. }
  72. #ifndef NOFLOAT
  73. /* The function f_collect() reads a string that has the format of a
  74. * floating-point number. The function returns as soon as a format-error
  75. * is encountered, leaving the offending character in the input. This means
  76. * that 1.el leaves the 'l' in the input queue. Since all detection of
  77. * format errors is done here, _doscan() doesn't call strtod() when it's
  78. * not necessary, although the use of the width field can cause incomplete
  79. * numbers to be passed to strtod(). (e.g. 1.3e+)
  80. */
  81. static char *
  82. f_collect(register int c, register FILE *stream, register int width)
  83. {
  84. register char *bufp = inp_buf;
  85. int digit_seen = 0;
  86. if (c == '-' || c == '+') {
  87. *bufp++ = c;
  88. if (--width)
  89. c = getc(stream);
  90. }
  91. while (width && isdigit(c)) {
  92. digit_seen++;
  93. *bufp++ = c;
  94. if (--width)
  95. c = getc(stream);
  96. }
  97. if (width && c == '.') {
  98. *bufp++ = c;
  99. if(--width)
  100. c = getc(stream);
  101. while (width && isdigit(c)) {
  102. digit_seen++;
  103. *bufp++ = c;
  104. if (--width)
  105. c = getc(stream);
  106. }
  107. }
  108. if (!digit_seen) {
  109. if (width && c != EOF) ungetc(c, stream);
  110. return inp_buf - 1;
  111. }
  112. else digit_seen = 0;
  113. if (width && (c == 'e' || c == 'E')) {
  114. *bufp++ = c;
  115. if (--width)
  116. c = getc(stream);
  117. if (width && (c == '+' || c == '-')) {
  118. *bufp++ = c;
  119. if (--width)
  120. c = getc(stream);
  121. }
  122. while (width && isdigit(c)) {
  123. digit_seen++;
  124. *bufp++ = c;
  125. if (--width)
  126. c = getc(stream);
  127. }
  128. if (!digit_seen) {
  129. if (width && c != EOF) ungetc(c,stream);
  130. return inp_buf - 1;
  131. }
  132. }
  133. if (width && c != EOF) ungetc(c, stream);
  134. *bufp = '\0';
  135. return bufp - 1;
  136. }
  137. #endif /* NOFLOAT */
  138. /*
  139. * the routine that does the scanning
  140. */
  141. int
  142. _doscan(register FILE *stream, const char *format, va_list ap)
  143. {
  144. int done = 0; /* number of items done */
  145. int nrchars = 0; /* number of characters read */
  146. int conv = 0; /* # of conversions */
  147. int base; /* conversion base */
  148. unsigned long val; /* an integer value */
  149. char *str, *tmp_string; /* temporary pointers */
  150. unsigned width; /* width of field */
  151. int flags; /* some flags */
  152. int reverse; /* reverse the checking in [...] */
  153. int kind;
  154. register int ic; /* the input character */
  155. #ifndef NOFLOAT
  156. long double ld_val;
  157. #endif
  158. while (1) {
  159. if (isspace(*format)) {
  160. while (isspace(*format))
  161. ++format; /* skip whitespace */
  162. ic = getc(stream);
  163. nrchars++;
  164. while (isspace (ic)) {
  165. ic = getc(stream);
  166. nrchars++;
  167. }
  168. if (ic != EOF) ungetc(ic,stream);
  169. nrchars--;
  170. }
  171. if (!*format) break; /* end of format */
  172. ic = getc(stream);
  173. nrchars++;
  174. if (ic == EOF)
  175. return conv ? done : EOF;
  176. if (*format != '%') {
  177. if (ic != *format)
  178. break; /* matching error */
  179. ++format;
  180. continue;
  181. }
  182. ++format;
  183. if (*format == '%') {
  184. if (ic == '%') {
  185. format++;
  186. continue;
  187. }
  188. else break;
  189. }
  190. flags = 0;
  191. if (*format == '*') {
  192. ++format;
  193. flags |= FL_NOASSIGN;
  194. }
  195. if (isdigit (*format)) {
  196. flags |= FL_WIDTHSPEC;
  197. for (width = 0; isdigit (*format);)
  198. width = width * 10 + *format++ - '0';
  199. }
  200. switch (*format) {
  201. case 'h': flags |= FL_SHORT; format++; break;
  202. case 'l': flags |= FL_LONG; format++; break;
  203. case 'L': flags |= FL_LONGDOUBLE; format++; break;
  204. }
  205. kind = *format;
  206. if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
  207. while (ic != EOF && isspace(ic)) {
  208. ic = getc(stream);
  209. nrchars++;
  210. }
  211. if (ic == EOF) return conv ? done : EOF;
  212. }
  213. conv++;
  214. switch (kind) {
  215. default:
  216. if (kind == ic) continue;
  217. break;
  218. case 'n':
  219. if (ic != EOF) ungetc(ic, stream);
  220. nrchars--;
  221. if (flags & FL_SHORT)
  222. *va_arg(ap, short *) = (short) nrchars;
  223. else if (flags & FL_LONG)
  224. *va_arg(ap, long *) = (long) nrchars;
  225. else
  226. *va_arg(ap, int *) = (int) nrchars;
  227. break;
  228. case 'b': /* binary */
  229. case 'd': /* decimal */
  230. case 'i': /* general integer */
  231. case 'o': /* octal */
  232. case 'p': /* pointer */
  233. case 'u': /* unsigned */
  234. case 'x': /* hexadecimal */
  235. case 'X': /* ditto */
  236. if (!(flags & FL_WIDTHSPEC))
  237. width = NUMLEN;
  238. if (!width) {
  239. if (ic != EOF) ungetc(ic, stream);
  240. return done;
  241. }
  242. str = o_collect(ic, stream, kind, width, &base);
  243. if (str < inp_buf) return done;
  244. nrchars += str - inp_buf + 1;
  245. if (!(flags & FL_NOASSIGN)) {
  246. if (kind == 'd' || kind == 'i')
  247. val = strtol(inp_buf, &tmp_string, base);
  248. else
  249. val = strtoul(inp_buf, &tmp_string, base);
  250. if (flags & FL_LONG)
  251. *va_arg(ap, unsigned long *) = (unsigned long) val;
  252. else if (flags & FL_SHORT)
  253. *va_arg(ap, unsigned short *) = (unsigned short) val;
  254. else
  255. *va_arg(ap, unsigned *) = (unsigned) val;
  256. done++;
  257. }
  258. break;
  259. case 'c':
  260. if (!(flags & FL_WIDTHSPEC))
  261. width = 1;
  262. if (!(flags & FL_NOASSIGN))
  263. tmp_string = va_arg(ap, char *);
  264. if (!width) {
  265. if (ic != EOF) ungetc(ic, stream);
  266. return done;
  267. }
  268. if (!(flags & FL_NOASSIGN) && (ic != EOF))
  269. ++done;
  270. while (width && ic != EOF) {
  271. if (!(flags & FL_NOASSIGN))
  272. *tmp_string++ = (char) ic;
  273. if (--width) {
  274. ic = getc(stream);
  275. nrchars++;
  276. }
  277. }
  278. if (width) {
  279. if (ic != EOF) ungetc(ic,stream);
  280. nrchars--;
  281. }
  282. break;
  283. case 's':
  284. if (!(flags & FL_WIDTHSPEC))
  285. width = 0xffff;
  286. if (!(flags & FL_NOASSIGN))
  287. tmp_string = va_arg(ap, char *);
  288. if (!width) {
  289. if (ic != EOF) ungetc(ic,stream);
  290. return done;
  291. }
  292. if (!(flags & FL_NOASSIGN))
  293. ++done;
  294. while (width && ic != EOF && !isspace(ic)) {
  295. if (!(flags & FL_NOASSIGN))
  296. *tmp_string++ = (char) ic;
  297. if (--width) {
  298. ic = getc(stream);
  299. nrchars++;
  300. }
  301. }
  302. /* terminate the string */
  303. if (!(flags & FL_NOASSIGN))
  304. *tmp_string = '\0';
  305. if (width) {
  306. if (ic != EOF) ungetc(ic,stream);
  307. nrchars--;
  308. }
  309. break;
  310. case '[':
  311. { int old_nrchars;
  312. if (!(flags & FL_WIDTHSPEC))
  313. width = 0xffff;
  314. if (!width) {
  315. if (ic != EOF) ungetc(ic, stream);
  316. return done;
  317. }
  318. if ( *(++format) == '^' ) {
  319. reverse = 1;
  320. format++;
  321. } else
  322. reverse = 0;
  323. for (tmp_string = Xtable; tmp_string < &Xtable[NR_CHARS]
  324. ; tmp_string++)
  325. *tmp_string = 0;
  326. if (*format == ']') Xtable[*format++] = 1;
  327. while (*format && *format != ']') {
  328. Xtable[*format++] = 1;
  329. if (*format == '-') {
  330. format++;
  331. if (*format
  332. && *format != ']'
  333. && *(format) >= *(format -2)) {
  334. int c;
  335. for( c = *(format -2) + 1
  336. ; c <= *format ; c++)
  337. Xtable[c] = 1;
  338. format++;
  339. }
  340. else Xtable['-'] = 1;
  341. }
  342. }
  343. if (!*format) {
  344. if (ic != EOF) ungetc(ic,stream);
  345. return done;
  346. }
  347. old_nrchars = nrchars;
  348. if (!(flags & FL_NOASSIGN))
  349. tmp_string = va_arg(ap, char *);
  350. if (ic == EOF || !(Xtable[ic] ^ reverse)) {
  351. if (ic != EOF) ungetc(ic, stream);
  352. return done;
  353. }
  354. while (width && ic != EOF && (Xtable[ic] ^ reverse)) {
  355. if (!(flags & FL_NOASSIGN))
  356. *tmp_string++ = (char) ic;
  357. if (--width) {
  358. ic = getc(stream);
  359. nrchars++;
  360. }
  361. }
  362. if (width) {
  363. if (ic != EOF) ungetc(ic, stream);
  364. nrchars--;
  365. }
  366. if (!(flags & FL_NOASSIGN)) { /* terminate string */
  367. *tmp_string = '\0';
  368. ++done;
  369. }
  370. break;
  371. }
  372. #ifndef NOFLOAT:
  373. case 'e':
  374. case 'E':
  375. case 'f':
  376. case 'g':
  377. case 'G':
  378. if (!(flags & FL_WIDTHSPEC)) width = NUMLEN;
  379. if (width > NUMLEN) width = NUMLEN;
  380. if (!width) return done;
  381. str = f_collect(ic, stream, width);
  382. if (str < inp_buf) return done;
  383. nrchars += str - inp_buf + 1;
  384. if (!(flags & FL_NOASSIGN)) {
  385. ld_val = strtod(inp_buf, &tmp_string);
  386. if (flags & FL_LONGDOUBLE)
  387. *va_arg(ap, long double *) = (long double) ld_val;
  388. else
  389. if (flags & FL_LONG)
  390. *va_arg(ap, double *) = (double) ld_val;
  391. else
  392. *va_arg(ap, float *) = (float) ld_val;
  393. done++;
  394. }
  395. break;
  396. #endif
  397. } /* end switch */
  398. ++format;
  399. }
  400. return conv ? done : EOF;
  401. }