Merge from vendor branch FILE:
[dragonfly.git] / contrib / sendmail / libsm / vfscanf.c
1 /*
2  * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1990, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Chris Torek.
9  *
10  * By using this file, you agree to the terms and conditions set
11  * forth in the LICENSE file which can be found at the top level of
12  * the sendmail distribution.
13  */
14
15 #include <sm/gen.h>
16 SM_IDSTR(id, "@(#)$Id: vfscanf.c,v 1.51 2001/09/11 04:04:49 gshapiro Exp $")
17
18 #include <ctype.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <setjmp.h>
22 #include <sys/time.h>
23 #include <sm/varargs.h>
24 #include <sm/config.h>
25 #include <sm/io.h>
26 #include <sm/signal.h>
27 #include <sm/clock.h>
28 #include <sm/string.h>
29 #include "local.h"
30
31 #define BUF             513     /* Maximum length of numeric string. */
32
33 /* Flags used during conversion. */
34 #define LONG            0x01    /* l: long or double */
35 #define SHORT           0x04    /* h: short */
36 #define QUAD            0x08    /* q: quad (same as ll) */
37 #define SUPPRESS        0x10    /* suppress assignment */
38 #define POINTER         0x20    /* weird %p pointer (`fake hex') */
39 #define NOSKIP          0x40    /* do not skip blanks */
40
41 /*
42 **  The following are used in numeric conversions only:
43 **  SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
44 **  SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
45 */
46
47 #define SIGNOK          0x080   /* +/- is (still) legal */
48 #define NDIGITS         0x100   /* no digits detected */
49
50 #define DPTOK           0x200   /* (float) decimal point is still legal */
51 #define EXPOK           0x400   /* (float) exponent (e+3, etc) still legal */
52
53 #define PFXOK           0x200   /* 0x prefix is (still) legal */
54 #define NZDIGITS        0x400   /* no zero digits detected */
55
56 /* Conversion types. */
57 #define CT_CHAR         0       /* %c conversion */
58 #define CT_CCL          1       /* %[...] conversion */
59 #define CT_STRING       2       /* %s conversion */
60 #define CT_INT          3       /* integer, i.e., strtoll or strtoull */
61 #define CT_FLOAT        4       /* floating, i.e., strtod */
62
63 static unsigned char *sm_sccl __P((char *, unsigned char *));
64 static jmp_buf ScanTimeOut;
65
66 /*
67 **  SCANALRM -- handler when timeout activated for sm_io_vfscanf()
68 **
69 **  Returns flow of control to where setjmp(ScanTimeOut) was set.
70 **
71 **      Parameters:
72 **              sig -- unused
73 **
74 **      Returns:
75 **              does not return
76 **
77 **      Side Effects:
78 **              returns flow of control to setjmp(ScanTimeOut).
79 **
80 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
81 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
82 **              DOING.
83 */
84
85 /* ARGSUSED0 */
86 static void
87 scanalrm(sig)
88         int sig;
89 {
90         longjmp(ScanTimeOut, 1);
91 }
92
93 /*
94 **  SM_VFSCANF -- convert input into data units
95 **
96 **      Parameters:
97 **              fp -- file pointer for input data
98 **              timeout -- time intvl allowed to complete (milliseconds)
99 **              fmt0 -- format for finding data units
100 **              ap -- vectors for memory location for storing data units
101 **
102 **      Results:
103 **              Success: number of data units assigned
104 **              Failure: SM_IO_EOF
105 */
106
107 int
108 sm_vfscanf(fp, timeout, fmt0, ap)
109         register SM_FILE_T *fp;
110         int SM_NONVOLATILE timeout;
111         char const *fmt0;
112         va_list SM_NONVOLATILE ap;
113 {
114         register unsigned char *SM_NONVOLATILE fmt = (unsigned char *) fmt0;
115         register int c;         /* character from format, or conversion */
116         register size_t width;  /* field width, or 0 */
117         register char *p;       /* points into all kinds of strings */
118         register int n;         /* handy integer */
119         register int flags;     /* flags as defined above */
120         register char *p0;      /* saves original value of p when necessary */
121         int nassigned;          /* number of fields assigned */
122         int nread;              /* number of characters consumed from fp */
123         int base;               /* base argument to strtoll/strtoull */
124         ULONGLONG_T (*ccfn)();  /* conversion function (strtoll/strtoull) */
125         char ccltab[256];       /* character class table for %[...] */
126         char buf[BUF];          /* buffer for numeric conversions */
127         SM_EVENT *evt = NULL;
128
129         /* `basefix' is used to avoid `if' tests in the integer scanner */
130         static short basefix[17] =
131                 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
132
133         if (timeout == SM_TIME_DEFAULT)
134                 timeout = fp->f_timeout;
135         if (timeout == SM_TIME_IMMEDIATE)
136         {
137                 /*
138                 **  Filling the buffer will take time and we are wanted to
139                 **  return immediately. So...
140                 */
141
142                 errno = EAGAIN;
143                 return SM_IO_EOF;
144         }
145
146         if (timeout != SM_TIME_FOREVER)
147         {
148                 if (setjmp(ScanTimeOut) != 0)
149                 {
150                         errno = EAGAIN;
151                         return SM_IO_EOF;
152                 }
153
154                 evt = sm_seteventm(timeout, scanalrm, 0);
155         }
156
157         nassigned = 0;
158         nread = 0;
159         base = 0;               /* XXX just to keep gcc happy */
160         ccfn = NULL;            /* XXX just to keep gcc happy */
161         for (;;)
162         {
163                 c = *fmt++;
164                 if (c == 0)
165                 {
166                         if (evt != NULL)
167                                 sm_clrevent(evt); /*  undo our timeout */
168                         return nassigned;
169                 }
170                 if (isspace(c))
171                 {
172                         while ((fp->f_r > 0 || sm_refill(fp, SM_TIME_FOREVER)
173                                                 == 0) &&
174                             isspace(*fp->f_p))
175                                 nread++, fp->f_r--, fp->f_p++;
176                         continue;
177                 }
178                 if (c != '%')
179                         goto literal;
180                 width = 0;
181                 flags = 0;
182
183                 /*
184                 **  switch on the format.  continue if done;
185                 **  break once format type is derived.
186                 */
187
188 again:          c = *fmt++;
189                 switch (c)
190                 {
191                   case '%':
192 literal:
193                         if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER))
194                                 goto input_failure;
195                         if (*fp->f_p != c)
196                                 goto match_failure;
197                         fp->f_r--, fp->f_p++;
198                         nread++;
199                         continue;
200
201                   case '*':
202                         flags |= SUPPRESS;
203                         goto again;
204                   case 'h':
205                         flags |= SHORT;
206                         goto again;
207                   case 'l':
208                         if (*fmt == 'l')
209                         {
210                                 fmt++;
211                                 flags |= QUAD;
212                         }
213                         else
214                         {
215                                 flags |= LONG;
216                         }
217                         goto again;
218                   case 'q':
219                         flags |= QUAD;
220                         goto again;
221
222                   case '0': case '1': case '2': case '3': case '4':
223                   case '5': case '6': case '7': case '8': case '9':
224                         width = width * 10 + c - '0';
225                         goto again;
226
227                 /*
228                 **  Conversions.
229                 **  Those marked `compat' are for 4.[123]BSD compatibility.
230                 **
231                 **  (According to ANSI, E and X formats are supposed
232                 **  to the same as e and x.  Sorry about that.)
233                 */
234
235                   case 'D':     /* compat */
236                         flags |= LONG;
237                         /* FALLTHROUGH */
238                   case 'd':
239                         c = CT_INT;
240                         ccfn = (ULONGLONG_T (*)())sm_strtoll;
241                         base = 10;
242                         break;
243
244                   case 'i':
245                         c = CT_INT;
246                         ccfn = (ULONGLONG_T (*)())sm_strtoll;
247                         base = 0;
248                         break;
249
250                   case 'O':     /* compat */
251                         flags |= LONG;
252                         /* FALLTHROUGH */
253                   case 'o':
254                         c = CT_INT;
255                         ccfn = sm_strtoull;
256                         base = 8;
257                         break;
258
259                   case 'u':
260                         c = CT_INT;
261                         ccfn = sm_strtoull;
262                         base = 10;
263                         break;
264
265                   case 'X':
266                   case 'x':
267                         flags |= PFXOK; /* enable 0x prefixing */
268                         c = CT_INT;
269                         ccfn = sm_strtoull;
270                         base = 16;
271                         break;
272
273                   case 'E':
274                   case 'G':
275                   case 'e':
276                   case 'f':
277                   case 'g':
278                         c = CT_FLOAT;
279                         break;
280
281                   case 's':
282                         c = CT_STRING;
283                         break;
284
285                   case '[':
286                         fmt = sm_sccl(ccltab, fmt);
287                         flags |= NOSKIP;
288                         c = CT_CCL;
289                         break;
290
291                   case 'c':
292                         flags |= NOSKIP;
293                         c = CT_CHAR;
294                         break;
295
296                   case 'p':     /* pointer format is like hex */
297                         flags |= POINTER | PFXOK;
298                         c = CT_INT;
299                         ccfn = sm_strtoull;
300                         base = 16;
301                         break;
302
303                   case 'n':
304                         if (flags & SUPPRESS)   /* ??? */
305                                 continue;
306                         if (flags & SHORT)
307                                 *SM_VA_ARG(ap, short *) = nread;
308                         else if (flags & LONG)
309                                 *SM_VA_ARG(ap, long *) = nread;
310                         else
311                                 *SM_VA_ARG(ap, int *) = nread;
312                         continue;
313
314                 /* Disgusting backwards compatibility hacks.    XXX */
315                   case '\0':    /* compat */
316                         if (evt != NULL)
317                                 sm_clrevent(evt); /*  undo our timeout */
318                         return SM_IO_EOF;
319
320                   default:      /* compat */
321                         if (isupper(c))
322                                 flags |= LONG;
323                         c = CT_INT;
324                         ccfn = (ULONGLONG_T (*)()) sm_strtoll;
325                         base = 10;
326                         break;
327                 }
328
329                 /* We have a conversion that requires input. */
330                 if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER))
331                         goto input_failure;
332
333                 /*
334                 **  Consume leading white space, except for formats
335                 **  that suppress this.
336                 */
337
338                 if ((flags & NOSKIP) == 0)
339                 {
340                         while (isspace(*fp->f_p))
341                         {
342                                 nread++;
343                                 if (--fp->f_r > 0)
344                                         fp->f_p++;
345                                 else if (sm_refill(fp, SM_TIME_FOREVER))
346                                         goto input_failure;
347                         }
348                         /*
349                         **  Note that there is at least one character in
350                         **  the buffer, so conversions that do not set NOSKIP
351                         **  can no longer result in an input failure.
352                         */
353                 }
354
355                 /* Do the conversion. */
356                 switch (c)
357                 {
358                   case CT_CHAR:
359                         /* scan arbitrary characters (sets NOSKIP) */
360                         if (width == 0)
361                                 width = 1;
362                         if (flags & SUPPRESS)
363                         {
364                                 size_t sum = 0;
365                                 for (;;)
366                                 {
367                                         if ((size_t) (n = fp->f_r) < width)
368                                         {
369                                                 sum += n;
370                                                 width -= n;
371                                                 fp->f_p += n;
372                                                 if (sm_refill(fp,
373                                                               SM_TIME_FOREVER))
374                                                 {
375                                                         if (sum == 0)
376                                                                 goto input_failure;
377                                                         break;
378                                                 }
379                                         }
380                                         else
381                                         {
382                                                 sum += width;
383                                                 fp->f_r -= width;
384                                                 fp->f_p += width;
385                                                 break;
386                                         }
387                                 }
388                                 nread += sum;
389                         }
390                         else
391                         {
392                                 size_t r;
393
394                                 r = sm_io_read(fp, SM_TIME_FOREVER,
395                                                 (void *) SM_VA_ARG(ap, char *),
396                                                 width);
397                                 if (r == 0)
398                                         goto input_failure;
399                                 nread += r;
400                                 nassigned++;
401                         }
402                         break;
403
404                   case CT_CCL:
405                         /* scan a (nonempty) character class (sets NOSKIP) */
406                         if (width == 0)
407                                 width = (size_t)~0;     /* `infinity' */
408
409                         /* take only those things in the class */
410                         if (flags & SUPPRESS)
411                         {
412                                 n = 0;
413                                 while (ccltab[*fp->f_p] != '\0')
414                                 {
415                                         n++, fp->f_r--, fp->f_p++;
416                                         if (--width == 0)
417                                                 break;
418                                         if (fp->f_r <= 0 &&
419                                             sm_refill(fp, SM_TIME_FOREVER))
420                                         {
421                                                 if (n == 0) /* XXX how? */
422                                                         goto input_failure;
423                                                 break;
424                                         }
425                                 }
426                                 if (n == 0)
427                                         goto match_failure;
428                         }
429                         else
430                         {
431                                 p0 = p = SM_VA_ARG(ap, char *);
432                                 while (ccltab[*fp->f_p] != '\0')
433                                 {
434                                         fp->f_r--;
435                                         *p++ = *fp->f_p++;
436                                         if (--width == 0)
437                                                 break;
438                                         if (fp->f_r <= 0 &&
439                                             sm_refill(fp, SM_TIME_FOREVER))
440                                         {
441                                                 if (p == p0)
442                                                         goto input_failure;
443                                                 break;
444                                         }
445                                 }
446                                 n = p - p0;
447                                 if (n == 0)
448                                         goto match_failure;
449                                 *p = 0;
450                                 nassigned++;
451                         }
452                         nread += n;
453                         break;
454
455                   case CT_STRING:
456                         /* like CCL, but zero-length string OK, & no NOSKIP */
457                         if (width == 0)
458                                 width = (size_t)~0;
459                         if (flags & SUPPRESS)
460                         {
461                                 n = 0;
462                                 while (!isspace(*fp->f_p))
463                                 {
464                                         n++, fp->f_r--, fp->f_p++;
465                                         if (--width == 0)
466                                                 break;
467                                         if (fp->f_r <= 0 &&
468                                             sm_refill(fp, SM_TIME_FOREVER))
469                                                 break;
470                                 }
471                                 nread += n;
472                         }
473                         else
474                         {
475                                 p0 = p = SM_VA_ARG(ap, char *);
476                                 while (!isspace(*fp->f_p))
477                                 {
478                                         fp->f_r--;
479                                         *p++ = *fp->f_p++;
480                                         if (--width == 0)
481                                                 break;
482                                         if (fp->f_r <= 0 &&
483                                             sm_refill(fp, SM_TIME_FOREVER))
484                                                 break;
485                                 }
486                                 *p = 0;
487                                 nread += p - p0;
488                                 nassigned++;
489                         }
490                         continue;
491
492                   case CT_INT:
493                         /* scan an integer as if by strtoll/strtoull */
494 #if SM_CONF_BROKEN_SIZE_T
495                         if (width == 0 || width > sizeof(buf) - 1)
496                                 width = sizeof(buf) - 1;
497 #else /* SM_CONF_BROKEN_SIZE_T */
498                         /* size_t is unsigned, hence this optimisation */
499                         if (--width > sizeof(buf) - 2)
500                                 width = sizeof(buf) - 2;
501                         width++;
502 #endif /* SM_CONF_BROKEN_SIZE_T */
503                         flags |= SIGNOK | NDIGITS | NZDIGITS;
504                         for (p = buf; width > 0; width--)
505                         {
506                                 c = *fp->f_p;
507
508                                 /*
509                                 **  Switch on the character; `goto ok'
510                                 **  if we accept it as a part of number.
511                                 */
512
513                                 switch (c)
514                                 {
515
516                                 /*
517                                 **  The digit 0 is always legal, but is
518                                 **  special.  For %i conversions, if no
519                                 **  digits (zero or nonzero) have been
520                                 **  scanned (only signs), we will have
521                                 **  base==0.  In that case, we should set
522                                 **  it to 8 and enable 0x prefixing.
523                                 **  Also, if we have not scanned zero digits
524                                 **  before this, do not turn off prefixing
525                                 **  (someone else will turn it off if we
526                                 **  have scanned any nonzero digits).
527                                 */
528
529                                   case '0':
530                                         if (base == 0)
531                                         {
532                                                 base = 8;
533                                                 flags |= PFXOK;
534                                         }
535                                         if (flags & NZDIGITS)
536                                             flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
537                                         else
538                                             flags &= ~(SIGNOK|PFXOK|NDIGITS);
539                                         goto ok;
540
541                                 /* 1 through 7 always legal */
542                                   case '1': case '2': case '3':
543                                   case '4': case '5': case '6': case '7':
544                                         base = basefix[base];
545                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
546                                         goto ok;
547
548                                 /* digits 8 and 9 ok iff decimal or hex */
549                                   case '8': case '9':
550                                         base = basefix[base];
551                                         if (base <= 8)
552                                                 break;  /* not legal here */
553                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
554                                         goto ok;
555
556                                 /* letters ok iff hex */
557                                   case 'A': case 'B': case 'C':
558                                   case 'D': case 'E': case 'F':
559                                   case 'a': case 'b': case 'c':
560                                   case 'd': case 'e': case 'f':
561
562                                         /* no need to fix base here */
563                                         if (base <= 10)
564                                                 break;  /* not legal here */
565                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
566                                         goto ok;
567
568                                 /* sign ok only as first character */
569                                   case '+': case '-':
570                                         if (flags & SIGNOK)
571                                         {
572                                                 flags &= ~SIGNOK;
573                                                 goto ok;
574                                         }
575                                         break;
576
577                                 /* x ok iff flag still set & 2nd char */
578                                   case 'x': case 'X':
579                                         if (flags & PFXOK && p == buf + 1)
580                                         {
581                                                 base = 16;      /* if %i */
582                                                 flags &= ~PFXOK;
583                                                 goto ok;
584                                         }
585                                         break;
586                                 }
587
588                                 /*
589                                 **  If we got here, c is not a legal character
590                                 **  for a number.  Stop accumulating digits.
591                                 */
592
593                                 break;
594                 ok:
595                                 /* c is legal: store it and look at the next. */
596                                 *p++ = c;
597                                 if (--fp->f_r > 0)
598                                         fp->f_p++;
599                                 else if (sm_refill(fp, SM_TIME_FOREVER))
600                                         break;          /* SM_IO_EOF */
601                         }
602
603                         /*
604                         **  If we had only a sign, it is no good; push
605                         **  back the sign.  If the number ends in `x',
606                         **  it was [sign] '0' 'x', so push back the x
607                         **  and treat it as [sign] '0'.
608                         */
609
610                         if (flags & NDIGITS)
611                         {
612                                 if (p > buf)
613                                         (void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
614                                                             *(unsigned char *)--p);
615                                 goto match_failure;
616                         }
617                         c = ((unsigned char *)p)[-1];
618                         if (c == 'x' || c == 'X')
619                         {
620                                 --p;
621                                 (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
622                         }
623                         if ((flags & SUPPRESS) == 0)
624                         {
625                                 ULONGLONG_T res;
626
627                                 *p = 0;
628                                 res = (*ccfn)(buf, (char **)NULL, base);
629                                 if (flags & POINTER)
630                                         *SM_VA_ARG(ap, void **) =
631                                             (void *)(long) res;
632                                 else if (flags & QUAD)
633                                         *SM_VA_ARG(ap, LONGLONG_T *) = res;
634                                 else if (flags & LONG)
635                                         *SM_VA_ARG(ap, long *) = res;
636                                 else if (flags & SHORT)
637                                         *SM_VA_ARG(ap, short *) = res;
638                                 else
639                                         *SM_VA_ARG(ap, int *) = res;
640                                 nassigned++;
641                         }
642                         nread += p - buf;
643                         break;
644
645                   case CT_FLOAT:
646                         /* scan a floating point number as if by strtod */
647                         if (width == 0 || width > sizeof(buf) - 1)
648                                 width = sizeof(buf) - 1;
649                         flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
650                         for (p = buf; width; width--)
651                         {
652                                 c = *fp->f_p;
653
654                                 /*
655                                 **  This code mimicks the integer conversion
656                                 **  code, but is much simpler.
657                                 */
658
659                                 switch (c)
660                                 {
661
662                                   case '0': case '1': case '2': case '3':
663                                   case '4': case '5': case '6': case '7':
664                                   case '8': case '9':
665                                         flags &= ~(SIGNOK | NDIGITS);
666                                         goto fok;
667
668                                   case '+': case '-':
669                                         if (flags & SIGNOK)
670                                         {
671                                                 flags &= ~SIGNOK;
672                                                 goto fok;
673                                         }
674                                         break;
675                                   case '.':
676                                         if (flags & DPTOK)
677                                         {
678                                                 flags &= ~(SIGNOK | DPTOK);
679                                                 goto fok;
680                                         }
681                                         break;
682                                   case 'e': case 'E':
683
684                                         /* no exponent without some digits */
685                                         if ((flags&(NDIGITS|EXPOK)) == EXPOK)
686                                         {
687                                                 flags =
688                                                     (flags & ~(EXPOK|DPTOK)) |
689                                                     SIGNOK | NDIGITS;
690                                                 goto fok;
691                                         }
692                                         break;
693                                 }
694                                 break;
695                 fok:
696                                 *p++ = c;
697                                 if (--fp->f_r > 0)
698                                         fp->f_p++;
699                                 else if (sm_refill(fp, SM_TIME_FOREVER))
700                                         break;  /* SM_IO_EOF */
701                         }
702
703                         /*
704                         **  If no digits, might be missing exponent digits
705                         **  (just give back the exponent) or might be missing
706                         **  regular digits, but had sign and/or decimal point.
707                         */
708
709                         if (flags & NDIGITS)
710                         {
711                                 if (flags & EXPOK)
712                                 {
713                                         /* no digits at all */
714                                         while (p > buf)
715                                                 (void) sm_io_ungetc(fp,
716                                                              SM_TIME_DEFAULT,
717                                                              *(unsigned char *)--p);
718                                         goto match_failure;
719                                 }
720
721                                 /* just a bad exponent (e and maybe sign) */
722                                 c = *(unsigned char *) --p;
723                                 if (c != 'e' && c != 'E')
724                                 {
725                                         (void) sm_io_ungetc(fp, SM_TIME_DEFAULT,
726                                                             c); /* sign */
727                                         c = *(unsigned char *)--p;
728                                 }
729                                 (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c);
730                         }
731                         if ((flags & SUPPRESS) == 0)
732                         {
733                                 double res;
734
735                                 *p = 0;
736                                 res = strtod(buf, (char **) NULL);
737                                 if (flags & LONG)
738                                         *SM_VA_ARG(ap, double *) = res;
739                                 else
740                                         *SM_VA_ARG(ap, float *) = res;
741                                 nassigned++;
742                         }
743                         nread += p - buf;
744                         break;
745                 }
746         }
747 input_failure:
748         if (evt != NULL)
749                 sm_clrevent(evt); /*  undo our timeout */
750         return nassigned ? nassigned : -1;
751 match_failure:
752         if (evt != NULL)
753                 sm_clrevent(evt); /*  undo our timeout */
754         return nassigned;
755 }
756
757 /*
758 **  SM_SCCL -- sequenced character comparison list
759 **
760 **  Fill in the given table from the scanset at the given format
761 **  (just after `[').  Return a pointer to the character past the
762 **  closing `]'.  The table has a 1 wherever characters should be
763 **  considered part of the scanset.
764 **
765 **      Parameters:
766 **              tab -- array flagging "active" char's to match (returned)
767 **              fmt -- character list (within "[]")
768 **
769 **      Results:
770 */
771
772 static unsigned char *
773 sm_sccl(tab, fmt)
774         register char *tab;
775         register unsigned char *fmt;
776 {
777         register int c, n, v;
778
779         /* first `clear' the whole table */
780         c = *fmt++;             /* first char hat => negated scanset */
781         if (c == '^')
782         {
783                 v = 1;          /* default => accept */
784                 c = *fmt++;     /* get new first char */
785         }
786         else
787                 v = 0;          /* default => reject */
788
789         /* should probably use memset here */
790         for (n = 0; n < 256; n++)
791                 tab[n] = v;
792         if (c == 0)
793                 return fmt - 1; /* format ended before closing ] */
794
795         /*
796         **  Now set the entries corresponding to the actual scanset
797         **  to the opposite of the above.
798         **
799         **  The first character may be ']' (or '-') without being special;
800         **  the last character may be '-'.
801         */
802
803         v = 1 - v;
804         for (;;)
805         {
806                 tab[c] = v;             /* take character c */
807 doswitch:
808                 n = *fmt++;             /* and examine the next */
809                 switch (n)
810                 {
811
812                   case 0:                       /* format ended too soon */
813                         return fmt - 1;
814
815                   case '-':
816                         /*
817                         **  A scanset of the form
818                         **      [01+-]
819                         **  is defined as `the digit 0, the digit 1,
820                         **  the character +, the character -', but
821                         **  the effect of a scanset such as
822                         **      [a-zA-Z0-9]
823                         **  is implementation defined.  The V7 Unix
824                         **  scanf treats `a-z' as `the letters a through
825                         **  z', but treats `a-a' as `the letter a, the
826                         **  character -, and the letter a'.
827                         **
828                         **  For compatibility, the `-' is not considerd
829                         **  to define a range if the character following
830                         **  it is either a close bracket (required by ANSI)
831                         **  or is not numerically greater than the character
832                         **  we just stored in the table (c).
833                         */
834
835                         n = *fmt;
836                         if (n == ']' || n < c)
837                         {
838                                 c = '-';
839                                 break;  /* resume the for(;;) */
840                         }
841                         fmt++;
842                         do
843                         {
844                                 /* fill in the range */
845                                 tab[++c] = v;
846                         } while (c < n);
847 #if 1   /* XXX another disgusting compatibility hack */
848
849                         /*
850                         **  Alas, the V7 Unix scanf also treats formats
851                         **  such as [a-c-e] as `the letters a through e'.
852                         **  This too is permitted by the standard....
853                         */
854
855                         goto doswitch;
856 #else
857                         c = *fmt++;
858                         if (c == 0)
859                                 return fmt - 1;
860                         if (c == ']')
861                                 return fmt;
862                         break;
863 #endif
864
865                   case ']':             /* end of scanset */
866                         return fmt;
867
868                   default:              /* just another character */
869                         c = n;
870                         break;
871                 }
872         }
873         /* NOTREACHED */
874 }