vfscanf.c revision 92889
1/*-
2 * Copyright (c) 1990, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#if defined(LIBC_SCCS) && !defined(lint)
38#if 0
39static char sccsid[] = "@(#)vfscanf.c	8.1 (Berkeley) 6/4/93";
40#endif
41static const char rcsid[] =
42  "$FreeBSD: head/lib/libc/stdio/vfscanf.c 92889 2002-03-21 18:49:23Z obrien $";
43#endif /* LIBC_SCCS and not lint */
44
45#include "namespace.h"
46#include <stdio.h>
47#include <stdlib.h>
48#include <ctype.h>
49#if __STDC__
50#include <stdarg.h>
51#else
52#include <varargs.h>
53#endif
54#include <string.h>
55#include "un-namespace.h"
56
57#include "collate.h"
58#include "libc_private.h"
59#include "local.h"
60
61#define FLOATING_POINT
62
63#ifdef FLOATING_POINT
64#include <locale.h>
65#include "floatio.h"
66#endif
67
68#define	BUF		513	/* Maximum length of numeric string. */
69
70/*
71 * Flags used during conversion.
72 */
73#define	LONG		0x01	/* l: long or double */
74#define	LONGDBL		0x02	/* L: long double */
75#define	SHORT		0x04	/* h: short */
76#define	SUPPRESS	0x08	/* suppress assignment */
77#define	POINTER		0x10	/* weird %p pointer (`fake hex') */
78#define	NOSKIP		0x20	/* do not skip blanks */
79#define	QUAD		0x400
80
81/*
82 * The following are used in numeric conversions only:
83 * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
84 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
85 */
86#define	SIGNOK		0x40	/* +/- is (still) legal */
87#define	NDIGITS		0x80	/* no digits detected */
88
89#define	DPTOK		0x100	/* (float) decimal point is still legal */
90#define	EXPOK		0x200	/* (float) exponent (e+3, etc) still legal */
91
92#define	PFXOK		0x100	/* 0x prefix is (still) legal */
93#define	NZDIGITS	0x200	/* no zero digits detected */
94
95/*
96 * Conversion types.
97 */
98#define	CT_CHAR		0	/* %c conversion */
99#define	CT_CCL		1	/* %[...] conversion */
100#define	CT_STRING	2	/* %s conversion */
101#define	CT_INT		3	/* integer, i.e., strtoq or strtouq */
102#define	CT_FLOAT	4	/* floating, i.e., strtod */
103
104#define u_char unsigned char
105#define u_long unsigned long
106
107static u_char *__sccl(char *, u_char *);
108
109/*
110 * __vfscanf - MT-safe version
111 */
112int
113__vfscanf(FILE *fp, char const *fmt0, va_list ap)
114{
115	int ret;
116
117	FLOCKFILE(fp);
118	ret = __svfscanf(fp, fmt0, ap);
119	FUNLOCKFILE(fp);
120	return (ret);
121}
122
123/*
124 * __svfscanf - non-MT-safe version of __vfscanf
125 */
126int
127__svfscanf(FILE *fp, char const *fmt0, va_list ap)
128{
129	u_char *fmt = (u_char *)fmt0;
130	int c;			/* character from format, or conversion */
131	size_t width;		/* field width, or 0 */
132	char *p;		/* points into all kinds of strings */
133	int n;			/* handy integer */
134	int flags;		/* flags as defined above */
135	char *p0;		/* saves original value of p when necessary */
136	int nassigned;		/* number of fields assigned */
137	int nconversions;	/* number of conversions */
138	int nread;		/* number of characters consumed from fp */
139	int base;		/* base argument to strtoq/strtouq */
140	u_quad_t(*ccfn)();	/* conversion function (strtoq/strtouq) */
141	char ccltab[256];	/* character class table for %[...] */
142	char buf[BUF];		/* buffer for numeric conversions */
143
144	/* `basefix' is used to avoid `if' tests in the integer scanner */
145	static short basefix[17] =
146		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
147#ifdef FLOATING_POINT
148	char decimal_point = localeconv()->decimal_point[0];
149#endif
150
151	nassigned = 0;
152	nconversions = 0;
153	nread = 0;
154	base = 0;		/* XXX just to keep gcc happy */
155	ccfn = NULL;		/* XXX just to keep gcc happy */
156	for (;;) {
157		c = *fmt++;
158		if (c == 0)
159			return (nassigned);
160		if (isspace(c)) {
161			while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p))
162				nread++, fp->_r--, fp->_p++;
163			continue;
164		}
165		if (c != '%')
166			goto literal;
167		width = 0;
168		flags = 0;
169		/*
170		 * switch on the format.  continue if done;
171		 * break once format type is derived.
172		 */
173again:		c = *fmt++;
174		switch (c) {
175		case '%':
176literal:
177			if (fp->_r <= 0 && __srefill(fp))
178				goto input_failure;
179			if (*fp->_p != c)
180				goto match_failure;
181			fp->_r--, fp->_p++;
182			nread++;
183			continue;
184
185		case '*':
186			flags |= SUPPRESS;
187			goto again;
188		case 'l':
189			flags |= LONG;
190			goto again;
191		case 'q':
192			flags |= QUAD;
193			goto again;
194		case 'L':
195			flags |= LONGDBL;
196			goto again;
197		case 'h':
198			flags |= SHORT;
199			goto again;
200
201		case '0': case '1': case '2': case '3': case '4':
202		case '5': case '6': case '7': case '8': case '9':
203			width = width * 10 + c - '0';
204			goto again;
205
206		/*
207		 * Conversions.
208		 * Those marked `compat' are for 4.[123]BSD compatibility.
209		 *
210		 * (According to ANSI, E and X formats are supposed
211		 * to the same as e and x.  Sorry about that.)
212		 */
213		case 'D':	/* compat */
214			flags |= LONG;
215			/* FALLTHROUGH */
216		case 'd':
217			c = CT_INT;
218			ccfn = (u_quad_t (*)())strtoq;
219			base = 10;
220			break;
221
222		case 'i':
223			c = CT_INT;
224			ccfn = (u_quad_t (*)())strtoq;
225			base = 0;
226			break;
227
228		case 'O':	/* compat */
229			flags |= LONG;
230			/* FALLTHROUGH */
231		case 'o':
232			c = CT_INT;
233			ccfn = strtouq;
234			base = 8;
235			break;
236
237		case 'u':
238			c = CT_INT;
239			ccfn = strtouq;
240			base = 10;
241			break;
242
243		case 'X':	/* compat   XXX */
244			flags |= LONG;
245			/* FALLTHROUGH */
246		case 'x':
247			flags |= PFXOK;	/* enable 0x prefixing */
248			c = CT_INT;
249			ccfn = strtouq;
250			base = 16;
251			break;
252
253#ifdef FLOATING_POINT
254		case 'E':	/* compat   XXX */
255		case 'F':	/* compat */
256			flags |= LONG;
257			/* FALLTHROUGH */
258		case 'e': case 'f': case 'g':
259			c = CT_FLOAT;
260			break;
261#endif
262
263		case 's':
264			c = CT_STRING;
265			break;
266
267		case '[':
268			fmt = __sccl(ccltab, fmt);
269			flags |= NOSKIP;
270			c = CT_CCL;
271			break;
272
273		case 'c':
274			flags |= NOSKIP;
275			c = CT_CHAR;
276			break;
277
278		case 'p':	/* pointer format is like hex */
279			flags |= POINTER | PFXOK;
280			c = CT_INT;
281			ccfn = strtouq;
282			base = 16;
283			break;
284
285		case 'n':
286			nconversions++;
287			if (flags & SUPPRESS)	/* ??? */
288				continue;
289			if (flags & SHORT)
290				*va_arg(ap, short *) = nread;
291			else if (flags & LONG)
292				*va_arg(ap, long *) = nread;
293			else if (flags & QUAD)
294				*va_arg(ap, quad_t *) = nread;
295			else
296				*va_arg(ap, int *) = nread;
297			continue;
298
299		/*
300		 * Disgusting backwards compatibility hacks.	XXX
301		 */
302		case '\0':	/* compat */
303			return (EOF);
304
305		default:	/* compat */
306			if (isupper(c))
307				flags |= LONG;
308			c = CT_INT;
309			ccfn = (u_quad_t (*)())strtoq;
310			base = 10;
311			break;
312		}
313
314		/*
315		 * We have a conversion that requires input.
316		 */
317		if (fp->_r <= 0 && __srefill(fp))
318			goto input_failure;
319
320		/*
321		 * Consume leading white space, except for formats
322		 * that suppress this.
323		 */
324		if ((flags & NOSKIP) == 0) {
325			while (isspace(*fp->_p)) {
326				nread++;
327				if (--fp->_r > 0)
328					fp->_p++;
329				else if (__srefill(fp))
330					goto input_failure;
331			}
332			/*
333			 * Note that there is at least one character in
334			 * the buffer, so conversions that do not set NOSKIP
335			 * ca no longer result in an input failure.
336			 */
337		}
338
339		/*
340		 * Do the conversion.
341		 */
342		switch (c) {
343
344		case CT_CHAR:
345			/* scan arbitrary characters (sets NOSKIP) */
346			if (width == 0)
347				width = 1;
348			if (flags & SUPPRESS) {
349				size_t sum = 0;
350				for (;;) {
351					if ((n = fp->_r) < width) {
352						sum += n;
353						width -= n;
354						fp->_p += n;
355						if (__srefill(fp)) {
356							if (sum == 0)
357							    goto input_failure;
358							break;
359						}
360					} else {
361						sum += width;
362						fp->_r -= width;
363						fp->_p += width;
364						break;
365					}
366				}
367				nread += sum;
368			} else {
369				size_t r = fread((void *)va_arg(ap, char *), 1,
370				    width, fp);
371
372				if (r == 0)
373					goto input_failure;
374				nread += r;
375				nassigned++;
376			}
377			nconversions++;
378			break;
379
380		case CT_CCL:
381			/* scan a (nonempty) character class (sets NOSKIP) */
382			if (width == 0)
383				width = (size_t)~0;	/* `infinity' */
384			/* take only those things in the class */
385			if (flags & SUPPRESS) {
386				n = 0;
387				while (ccltab[*fp->_p]) {
388					n++, fp->_r--, fp->_p++;
389					if (--width == 0)
390						break;
391					if (fp->_r <= 0 && __srefill(fp)) {
392						if (n == 0)
393							goto input_failure;
394						break;
395					}
396				}
397				if (n == 0)
398					goto match_failure;
399			} else {
400				p0 = p = va_arg(ap, char *);
401				while (ccltab[*fp->_p]) {
402					fp->_r--;
403					*p++ = *fp->_p++;
404					if (--width == 0)
405						break;
406					if (fp->_r <= 0 && __srefill(fp)) {
407						if (p == p0)
408							goto input_failure;
409						break;
410					}
411				}
412				n = p - p0;
413				if (n == 0)
414					goto match_failure;
415				*p = 0;
416				nassigned++;
417			}
418			nread += n;
419			nconversions++;
420			break;
421
422		case CT_STRING:
423			/* like CCL, but zero-length string OK, & no NOSKIP */
424			if (width == 0)
425				width = (size_t)~0;
426			if (flags & SUPPRESS) {
427				n = 0;
428				while (!isspace(*fp->_p)) {
429					n++, fp->_r--, fp->_p++;
430					if (--width == 0)
431						break;
432					if (fp->_r <= 0 && __srefill(fp))
433						break;
434				}
435				nread += n;
436			} else {
437				p0 = p = va_arg(ap, char *);
438				while (!isspace(*fp->_p)) {
439					fp->_r--;
440					*p++ = *fp->_p++;
441					if (--width == 0)
442						break;
443					if (fp->_r <= 0 && __srefill(fp))
444						break;
445				}
446				*p = 0;
447				nread += p - p0;
448				nassigned++;
449			}
450			nconversions++;
451			continue;
452
453		case CT_INT:
454			/* scan an integer as if by strtoq/strtouq */
455#ifdef hardway
456			if (width == 0 || width > sizeof(buf) - 1)
457				width = sizeof(buf) - 1;
458#else
459			/* size_t is unsigned, hence this optimisation */
460			if (--width > sizeof(buf) - 2)
461				width = sizeof(buf) - 2;
462			width++;
463#endif
464			flags |= SIGNOK | NDIGITS | NZDIGITS;
465			for (p = buf; width; width--) {
466				c = *fp->_p;
467				/*
468				 * Switch on the character; `goto ok'
469				 * if we accept it as a part of number.
470				 */
471				switch (c) {
472
473				/*
474				 * The digit 0 is always legal, but is
475				 * special.  For %i conversions, if no
476				 * digits (zero or nonzero) have been
477				 * scanned (only signs), we will have
478				 * base==0.  In that case, we should set
479				 * it to 8 and enable 0x prefixing.
480				 * Also, if we have not scanned zero digits
481				 * before this, do not turn off prefixing
482				 * (someone else will turn it off if we
483				 * have scanned any nonzero digits).
484				 */
485				case '0':
486					if (base == 0) {
487						base = 8;
488						flags |= PFXOK;
489					}
490					if (flags & NZDIGITS)
491					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
492					else
493					    flags &= ~(SIGNOK|PFXOK|NDIGITS);
494					goto ok;
495
496				/* 1 through 7 always legal */
497				case '1': case '2': case '3':
498				case '4': case '5': case '6': case '7':
499					base = basefix[base];
500					flags &= ~(SIGNOK | PFXOK | NDIGITS);
501					goto ok;
502
503				/* digits 8 and 9 ok iff decimal or hex */
504				case '8': case '9':
505					base = basefix[base];
506					if (base <= 8)
507						break;	/* not legal here */
508					flags &= ~(SIGNOK | PFXOK | NDIGITS);
509					goto ok;
510
511				/* letters ok iff hex */
512				case 'A': case 'B': case 'C':
513				case 'D': case 'E': case 'F':
514				case 'a': case 'b': case 'c':
515				case 'd': case 'e': case 'f':
516					/* no need to fix base here */
517					if (base <= 10)
518						break;	/* not legal here */
519					flags &= ~(SIGNOK | PFXOK | NDIGITS);
520					goto ok;
521
522				/* sign ok only as first character */
523				case '+': case '-':
524					if (flags & SIGNOK) {
525						flags &= ~SIGNOK;
526						goto ok;
527					}
528					break;
529
530				/* x ok iff flag still set & 2nd char */
531				case 'x': case 'X':
532					if (flags & PFXOK && p == buf + 1) {
533						base = 16;	/* if %i */
534						flags &= ~PFXOK;
535						goto ok;
536					}
537					break;
538				}
539
540				/*
541				 * If we got here, c is not a legal character
542				 * for a number.  Stop accumulating digits.
543				 */
544				break;
545		ok:
546				/*
547				 * c is legal: store it and look at the next.
548				 */
549				*p++ = c;
550				if (--fp->_r > 0)
551					fp->_p++;
552				else if (__srefill(fp))
553					break;		/* EOF */
554			}
555			/*
556			 * If we had only a sign, it is no good; push
557			 * back the sign.  If the number ends in `x',
558			 * it was [sign] '0' 'x', so push back the x
559			 * and treat it as [sign] '0'.
560			 */
561			if (flags & NDIGITS) {
562				if (p > buf)
563					(void) __ungetc(*(u_char *)--p, fp);
564				goto match_failure;
565			}
566			c = ((u_char *)p)[-1];
567			if (c == 'x' || c == 'X') {
568				--p;
569				(void) __ungetc(c, fp);
570			}
571			if ((flags & SUPPRESS) == 0) {
572				u_quad_t res;
573
574				*p = 0;
575				res = (*ccfn)(buf, (char **)NULL, base);
576				if (flags & POINTER)
577					*va_arg(ap, void **) =
578						(void *)(u_long)res;
579				else if (flags & SHORT)
580					*va_arg(ap, short *) = res;
581				else if (flags & LONG)
582					*va_arg(ap, long *) = res;
583				else if (flags & QUAD)
584					*va_arg(ap, quad_t *) = res;
585				else
586					*va_arg(ap, int *) = res;
587				nassigned++;
588			}
589			nread += p - buf;
590			nconversions++;
591			break;
592
593#ifdef FLOATING_POINT
594		case CT_FLOAT:
595			/* scan a floating point number as if by strtod */
596#ifdef hardway
597			if (width == 0 || width > sizeof(buf) - 1)
598				width = sizeof(buf) - 1;
599#else
600			/* size_t is unsigned, hence this optimisation */
601			if (--width > sizeof(buf) - 2)
602				width = sizeof(buf) - 2;
603			width++;
604#endif
605			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
606			for (p = buf; width; width--) {
607				c = *fp->_p;
608				/*
609				 * This code mimicks the integer conversion
610				 * code, but is much simpler.
611				 */
612				switch (c) {
613
614				case '0': case '1': case '2': case '3':
615				case '4': case '5': case '6': case '7':
616				case '8': case '9':
617					flags &= ~(SIGNOK | NDIGITS);
618					goto fok;
619
620				case '+': case '-':
621					if (flags & SIGNOK) {
622						flags &= ~SIGNOK;
623						goto fok;
624					}
625					break;
626				case 'e': case 'E':
627					/* no exponent without some digits */
628					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
629						flags =
630						    (flags & ~(EXPOK|DPTOK)) |
631						    SIGNOK | NDIGITS;
632						goto fok;
633					}
634					break;
635				default:
636					if ((char)c == decimal_point &&
637					    (flags & DPTOK)) {
638						flags &= ~(SIGNOK | DPTOK);
639						goto fok;
640					}
641					break;
642				}
643				break;
644		fok:
645				*p++ = c;
646				if (--fp->_r > 0)
647					fp->_p++;
648				else if (__srefill(fp))
649					break;	/* EOF */
650			}
651			/*
652			 * If no digits, might be missing exponent digits
653			 * (just give back the exponent) or might be missing
654			 * regular digits, but had sign and/or decimal point.
655			 */
656			if (flags & NDIGITS) {
657				if (flags & EXPOK) {
658					/* no digits at all */
659					while (p > buf)
660						__ungetc(*(u_char *)--p, fp);
661					goto match_failure;
662				}
663				/* just a bad exponent (e and maybe sign) */
664				c = *(u_char *)--p;
665				if (c != 'e' && c != 'E') {
666					(void) __ungetc(c, fp);/* sign */
667					c = *(u_char *)--p;
668				}
669				(void) __ungetc(c, fp);
670			}
671			if ((flags & SUPPRESS) == 0) {
672				double res;
673
674				*p = 0;
675				/* XXX this loses precision for long doubles. */
676				res = strtod(buf, (char **) NULL);
677				if (flags & LONGDBL)
678					*va_arg(ap, long double *) = res;
679				else if (flags & LONG)
680					*va_arg(ap, double *) = res;
681				else
682					*va_arg(ap, float *) = res;
683				nassigned++;
684			}
685			nread += p - buf;
686			nconversions++;
687			break;
688#endif /* FLOATING_POINT */
689		}
690	}
691input_failure:
692	return (nconversions != 0 ? nassigned : EOF);
693match_failure:
694	return (nassigned);
695}
696
697/*
698 * Fill in the given table from the scanset at the given format
699 * (just after `[').  Return a pointer to the character past the
700 * closing `]'.  The table has a 1 wherever characters should be
701 * considered part of the scanset.
702 */
703static u_char *
704__sccl(tab, fmt)
705	char *tab;
706	u_char *fmt;
707{
708	int c, n, v, i;
709
710	/* first `clear' the whole table */
711	c = *fmt++;		/* first char hat => negated scanset */
712	if (c == '^') {
713		v = 1;		/* default => accept */
714		c = *fmt++;	/* get new first char */
715	} else
716		v = 0;		/* default => reject */
717
718	/* XXX: Will not work if sizeof(tab*) > sizeof(char) */
719	(void) memset(tab, v, 256);
720
721	if (c == 0)
722		return (fmt - 1);/* format ended before closing ] */
723
724	/*
725	 * Now set the entries corresponding to the actual scanset
726	 * to the opposite of the above.
727	 *
728	 * The first character may be ']' (or '-') without being special;
729	 * the last character may be '-'.
730	 */
731	v = 1 - v;
732	for (;;) {
733		tab[c] = v;		/* take character c */
734doswitch:
735		n = *fmt++;		/* and examine the next */
736		switch (n) {
737
738		case 0:			/* format ended too soon */
739			return (fmt - 1);
740
741		case '-':
742			/*
743			 * A scanset of the form
744			 *	[01+-]
745			 * is defined as `the digit 0, the digit 1,
746			 * the character +, the character -', but
747			 * the effect of a scanset such as
748			 *	[a-zA-Z0-9]
749			 * is implementation defined.  The V7 Unix
750			 * scanf treats `a-z' as `the letters a through
751			 * z', but treats `a-a' as `the letter a, the
752			 * character -, and the letter a'.
753			 *
754			 * For compatibility, the `-' is not considerd
755			 * to define a range if the character following
756			 * it is either a close bracket (required by ANSI)
757			 * or is not numerically greater than the character
758			 * we just stored in the table (c).
759			 */
760			n = *fmt;
761			if (n == ']'
762			    || (__collate_load_error ? n < c :
763				__collate_range_cmp (n, c) < 0
764			       )
765			   ) {
766				c = '-';
767				break;	/* resume the for(;;) */
768			}
769			fmt++;
770			/* fill in the range */
771			if (__collate_load_error) {
772				do {
773					tab[++c] = v;
774				} while (c < n);
775			} else {
776				for (i = 0; i < 256; i ++)
777					if (   __collate_range_cmp (c, i) < 0
778					    && __collate_range_cmp (i, n) <= 0
779					   )
780						tab[i] = v;
781			}
782#if 1	/* XXX another disgusting compatibility hack */
783			c = n;
784			/*
785			 * Alas, the V7 Unix scanf also treats formats
786			 * such as [a-c-e] as `the letters a through e'.
787			 * This too is permitted by the standard....
788			 */
789			goto doswitch;
790#else
791			c = *fmt++;
792			if (c == 0)
793				return (fmt - 1);
794			if (c == ']')
795				return (fmt);
796#endif
797			break;
798
799		case ']':		/* end of scanset */
800			return (fmt);
801
802		default:		/* just another character */
803			c = n;
804			break;
805		}
806	}
807	/* NOTREACHED */
808}
809