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