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