/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclscan.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclscan.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 71 - (hide annotations) (download)
Sat Nov 5 11:07:06 2016 UTC (7 years, 7 months ago) by dashley
File MIME type: text/plain
File size: 25001 byte(s)
Set EOL properties appropriately to facilitate simultaneous Linux and Windows development.
1 dashley 71 /* $Header$ */
2     /*
3     * tclScan.c --
4     *
5     * This file contains the implementation of the "scan" command.
6     *
7     * Copyright (c) 1998 by Scriptics Corporation.
8     *
9     * See the file "license.terms" for information on usage and redistribution
10     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11     *
12     * RCS: @(#) $Id: tclscan.c,v 1.1.1.1 2001/06/13 04:46:00 dtashley Exp $
13     */
14    
15     #include "tclInt.h"
16    
17     /*
18     * Flag values used by Tcl_ScanObjCmd.
19     */
20    
21     #define SCAN_NOSKIP 0x1 /* Don't skip blanks. */
22     #define SCAN_SUPPRESS 0x2 /* Suppress assignment. */
23     #define SCAN_UNSIGNED 0x4 /* Read an unsigned value. */
24     #define SCAN_WIDTH 0x8 /* A width value was supplied. */
25    
26     #define SCAN_SIGNOK 0x10 /* A +/- character is allowed. */
27     #define SCAN_NODIGITS 0x20 /* No digits have been scanned. */
28     #define SCAN_NOZERO 0x40 /* No zero digits have been scanned. */
29     #define SCAN_XOK 0x80 /* An 'x' is allowed. */
30     #define SCAN_PTOK 0x100 /* Decimal point is allowed. */
31     #define SCAN_EXPOK 0x200 /* An exponent is allowed. */
32    
33    
34     /*
35     * The following structure contains the information associated with
36     * a character set.
37     */
38    
39     typedef struct CharSet {
40     int exclude; /* 1 if this is an exclusion set. */
41     int nchars;
42     Tcl_UniChar *chars;
43     int nranges;
44     struct Range {
45     Tcl_UniChar start;
46     Tcl_UniChar end;
47     } *ranges;
48     } CharSet;
49    
50     /*
51     * Declarations for functions used only in this file.
52     */
53    
54     static char * BuildCharSet _ANSI_ARGS_((CharSet *cset, char *format));
55     static int CharInSet _ANSI_ARGS_((CharSet *cset, int ch));
56     static void ReleaseCharSet _ANSI_ARGS_((CharSet *cset));
57     static int ValidateFormat _ANSI_ARGS_((Tcl_Interp *interp, char *format,
58     int numVars, int *totalVars));
59    
60     /*
61     *----------------------------------------------------------------------
62     *
63     * BuildCharSet --
64     *
65     * This function examines a character set format specification
66     * and builds a CharSet containing the individual characters and
67     * character ranges specified.
68     *
69     * Results:
70     * Returns the next format position.
71     *
72     * Side effects:
73     * Initializes the charset.
74     *
75     *----------------------------------------------------------------------
76     */
77    
78     static char *
79     BuildCharSet(cset, format)
80     CharSet *cset;
81     char *format; /* Points to first char of set. */
82     {
83     Tcl_UniChar ch, start;
84     int offset, nranges;
85     char *end;
86    
87     memset(cset, 0, sizeof(CharSet));
88    
89     offset = Tcl_UtfToUniChar(format, &ch);
90     if (ch == '^') {
91     cset->exclude = 1;
92     format += offset;
93     offset = Tcl_UtfToUniChar(format, &ch);
94     }
95     end = format + offset;
96    
97     /*
98     * Find the close bracket so we can overallocate the set.
99     */
100    
101     if (ch == ']') {
102     end += Tcl_UtfToUniChar(end, &ch);
103     }
104     nranges = 0;
105     while (ch != ']') {
106     if (ch == '-') {
107     nranges++;
108     }
109     end += Tcl_UtfToUniChar(end, &ch);
110     }
111    
112     cset->chars = (Tcl_UniChar *) ckalloc(sizeof(Tcl_UniChar)
113     * (end - format - 1));
114     if (nranges > 0) {
115     cset->ranges = (struct Range *) ckalloc(sizeof(struct Range)*nranges);
116     } else {
117     cset->ranges = NULL;
118     }
119    
120     /*
121     * Now build the character set.
122     */
123    
124     cset->nchars = cset->nranges = 0;
125     format += Tcl_UtfToUniChar(format, &ch);
126     start = ch;
127     if (ch == ']' || ch == '-') {
128     cset->chars[cset->nchars++] = ch;
129     format += Tcl_UtfToUniChar(format, &ch);
130     }
131     while (ch != ']') {
132     if (*format == '-') {
133     /*
134     * This may be the first character of a range, so don't add
135     * it yet.
136     */
137    
138     start = ch;
139     } else if (ch == '-') {
140     /*
141     * Check to see if this is the last character in the set, in which
142     * case it is not a range and we should add the previous character
143     * as well as the dash.
144     */
145    
146     if (*format == ']') {
147     cset->chars[cset->nchars++] = start;
148     cset->chars[cset->nchars++] = ch;
149     } else {
150     format += Tcl_UtfToUniChar(format, &ch);
151    
152     /*
153     * Check to see if the range is in reverse order.
154     */
155    
156     if (start < ch) {
157     cset->ranges[cset->nranges].start = start;
158     cset->ranges[cset->nranges].end = ch;
159     } else {
160     cset->ranges[cset->nranges].start = ch;
161     cset->ranges[cset->nranges].end = start;
162     }
163     cset->nranges++;
164     }
165     } else {
166     cset->chars[cset->nchars++] = ch;
167     }
168     format += Tcl_UtfToUniChar(format, &ch);
169     }
170     return format;
171     }
172    
173     /*
174     *----------------------------------------------------------------------
175     *
176     * CharInSet --
177     *
178     * Check to see if a character matches the given set.
179     *
180     * Results:
181     * Returns non-zero if the character matches the given set.
182     *
183     * Side effects:
184     * None.
185     *
186     *----------------------------------------------------------------------
187     */
188    
189     static int
190     CharInSet(cset, c)
191     CharSet *cset;
192     int c; /* Character to test, passed as int because
193     * of non-ANSI prototypes. */
194     {
195     Tcl_UniChar ch = (Tcl_UniChar) c;
196     int i, match = 0;
197     for (i = 0; i < cset->nchars; i++) {
198     if (cset->chars[i] == ch) {
199     match = 1;
200     break;
201     }
202     }
203     if (!match) {
204     for (i = 0; i < cset->nranges; i++) {
205     if ((cset->ranges[i].start <= ch)
206     && (ch <= cset->ranges[i].end)) {
207     match = 1;
208     break;
209     }
210     }
211     }
212     return (cset->exclude ? !match : match);
213     }
214    
215     /*
216     *----------------------------------------------------------------------
217     *
218     * ReleaseCharSet --
219     *
220     * Free the storage associated with a character set.
221     *
222     * Results:
223     * None.
224     *
225     * Side effects:
226     * None.
227     *
228     *----------------------------------------------------------------------
229     */
230    
231     static void
232     ReleaseCharSet(cset)
233     CharSet *cset;
234     {
235     ckfree((char *)cset->chars);
236     if (cset->ranges) {
237     ckfree((char *)cset->ranges);
238     }
239     }
240    
241     /*
242     *----------------------------------------------------------------------
243     *
244     * ValidateFormat --
245     *
246     * Parse the format string and verify that it is properly formed
247     * and that there are exactly enough variables on the command line.
248     *
249     * Results:
250     * A standard Tcl result.
251     *
252     * Side effects:
253     * May place an error in the interpreter result.
254     *
255     *----------------------------------------------------------------------
256     */
257    
258     static int
259     ValidateFormat(interp, format, numVars, totalSubs)
260     Tcl_Interp *interp; /* Current interpreter. */
261     char *format; /* The format string. */
262     int numVars; /* The number of variables passed to the
263     * scan command. */
264     int *totalSubs; /* The number of variables that will be
265     * required. */
266     {
267     #define STATIC_LIST_SIZE 16
268     int gotXpg, gotSequential, value, i, flags;
269     char *end;
270     Tcl_UniChar ch;
271     int staticAssign[STATIC_LIST_SIZE];
272     int *nassign = staticAssign;
273     int objIndex, xpgSize, nspace = STATIC_LIST_SIZE;
274    
275     /*
276     * Initialize an array that records the number of times a variable
277     * is assigned to by the format string. We use this to detect if
278     * a variable is multiply assigned or left unassigned.
279     */
280    
281     if (numVars > nspace) {
282     nassign = (int*)ckalloc(sizeof(int) * numVars);
283     nspace = numVars;
284     }
285     for (i = 0; i < nspace; i++) {
286     nassign[i] = 0;
287     }
288    
289     xpgSize = objIndex = gotXpg = gotSequential = 0;
290    
291     while (*format != '\0') {
292     format += Tcl_UtfToUniChar(format, &ch);
293    
294     flags = 0;
295    
296     if (ch != '%') {
297     continue;
298     }
299     format += Tcl_UtfToUniChar(format, &ch);
300     if (ch == '%') {
301     continue;
302     }
303     if (ch == '*') {
304     flags |= SCAN_SUPPRESS;
305     format += Tcl_UtfToUniChar(format, &ch);
306     goto xpgCheckDone;
307     }
308    
309     if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */
310     /*
311     * Check for an XPG3-style %n$ specification. Note: there
312     * must not be a mixture of XPG3 specs and non-XPG3 specs
313     * in the same format string.
314     */
315    
316     value = strtoul(format-1, &end, 10); /* INTL: "C" locale. */
317     if (*end != '$') {
318     goto notXpg;
319     }
320     format = end+1;
321     format += Tcl_UtfToUniChar(format, &ch);
322     gotXpg = 1;
323     if (gotSequential) {
324     goto mixedXPG;
325     }
326     objIndex = value - 1;
327     if ((objIndex < 0) || (numVars && (objIndex >= numVars))) {
328     goto badIndex;
329     } else if (numVars == 0) {
330     /*
331     * In the case where no vars are specified, the user can
332     * specify %9999$ legally, so we have to consider special
333     * rules for growing the assign array. 'value' is
334     * guaranteed to be > 0.
335     */
336     xpgSize = (xpgSize > value) ? xpgSize : value;
337     }
338     goto xpgCheckDone;
339     }
340    
341     notXpg:
342     gotSequential = 1;
343     if (gotXpg) {
344     mixedXPG:
345     Tcl_SetResult(interp,
346     "cannot mix \"%\" and \"%n$\" conversion specifiers",
347     TCL_STATIC);
348     goto error;
349     }
350    
351     xpgCheckDone:
352     /*
353     * Parse any width specifier.
354     */
355    
356     if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */
357     value = strtoul(format-1, &format, 10); /* INTL: "C" locale. */
358     flags |= SCAN_WIDTH;
359     format += Tcl_UtfToUniChar(format, &ch);
360     }
361    
362     /*
363     * Ignore size specifier.
364     */
365    
366     if ((ch == 'l') || (ch == 'L') || (ch == 'h')) {
367     format += Tcl_UtfToUniChar(format, &ch);
368     }
369    
370     if (!(flags & SCAN_SUPPRESS) && numVars && (objIndex >= numVars)) {
371     goto badIndex;
372     }
373    
374     /*
375     * Handle the various field types.
376     */
377    
378     switch (ch) {
379     case 'n':
380     case 'd':
381     case 'i':
382     case 'o':
383     case 'x':
384     case 'u':
385     case 'f':
386     case 'e':
387     case 'g':
388     case 's':
389     break;
390     case 'c':
391     if (flags & SCAN_WIDTH) {
392     Tcl_SetResult(interp, "field width may not be specified in %c conversion", TCL_STATIC);
393     goto error;
394     }
395     break;
396     case '[':
397     if (*format == '\0') {
398     goto badSet;
399     }
400     format += Tcl_UtfToUniChar(format, &ch);
401     if (ch == '^') {
402     if (*format == '\0') {
403     goto badSet;
404     }
405     format += Tcl_UtfToUniChar(format, &ch);
406     }
407     if (ch == ']') {
408     if (*format == '\0') {
409     goto badSet;
410     }
411     format += Tcl_UtfToUniChar(format, &ch);
412     }
413     while (ch != ']') {
414     if (*format == '\0') {
415     goto badSet;
416     }
417     format += Tcl_UtfToUniChar(format, &ch);
418     }
419     break;
420     badSet:
421     Tcl_SetResult(interp, "unmatched [ in format string",
422     TCL_STATIC);
423     goto error;
424     default:
425     {
426     char buf[TCL_UTF_MAX+1];
427    
428     buf[Tcl_UniCharToUtf(ch, buf)] = '\0';
429     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
430     "bad scan conversion character \"", buf, "\"", NULL);
431     goto error;
432     }
433     }
434     if (!(flags & SCAN_SUPPRESS)) {
435     if (objIndex >= nspace) {
436     /*
437     * Expand the nassign buffer. If we are using XPG specifiers,
438     * make sure that we grow to a large enough size. xpgSize is
439     * guaranteed to be at least one larger than objIndex.
440     */
441     value = nspace;
442     if (xpgSize) {
443     nspace = xpgSize;
444     } else {
445     nspace += STATIC_LIST_SIZE;
446     }
447     if (nassign == staticAssign) {
448     nassign = (void *)ckalloc(nspace * sizeof(int));
449     for (i = 0; i < STATIC_LIST_SIZE; ++i) {
450     nassign[i] = staticAssign[i];
451     }
452     } else {
453     nassign = (void *)ckrealloc((void *)nassign,
454     nspace * sizeof(int));
455     }
456     for (i = value; i < nspace; i++) {
457     nassign[i] = 0;
458     }
459     }
460     nassign[objIndex]++;
461     objIndex++;
462     }
463     }
464    
465     /*
466     * Verify that all of the variable were assigned exactly once.
467     */
468    
469     if (numVars == 0) {
470     if (xpgSize) {
471     numVars = xpgSize;
472     } else {
473     numVars = objIndex;
474     }
475     }
476     if (totalSubs) {
477     *totalSubs = numVars;
478     }
479     for (i = 0; i < numVars; i++) {
480     if (nassign[i] > 1) {
481     Tcl_SetResult(interp, "variable is assigned by multiple \"%n$\" conversion specifiers", TCL_STATIC);
482     goto error;
483     } else if (!xpgSize && (nassign[i] == 0)) {
484     /*
485     * If the space is empty, and xpgSize is 0 (means XPG wasn't
486     * used, and/or numVars != 0), then too many vars were given
487     */
488     Tcl_SetResult(interp, "variable is not assigned by any conversion specifiers", TCL_STATIC);
489     goto error;
490     }
491     }
492    
493     if (nassign != staticAssign) {
494     ckfree((char *)nassign);
495     }
496     return TCL_OK;
497    
498     badIndex:
499     if (gotXpg) {
500     Tcl_SetResult(interp, "\"%n$\" argument index out of range",
501     TCL_STATIC);
502     } else {
503     Tcl_SetResult(interp,
504     "different numbers of variable names and field specifiers",
505     TCL_STATIC);
506     }
507    
508     error:
509     if (nassign != staticAssign) {
510     ckfree((char *)nassign);
511     }
512     return TCL_ERROR;
513     #undef STATIC_LIST_SIZE
514     }
515    
516     /*
517     *----------------------------------------------------------------------
518     *
519     * Tcl_ScanObjCmd --
520     *
521     * This procedure is invoked to process the "scan" Tcl command.
522     * See the user documentation for details on what it does.
523     *
524     * Results:
525     * A standard Tcl result.
526     *
527     * Side effects:
528     * See the user documentation.
529     *
530     *----------------------------------------------------------------------
531     */
532    
533     /* ARGSUSED */
534     int
535     Tcl_ScanObjCmd(dummy, interp, objc, objv)
536     ClientData dummy; /* Not used. */
537     Tcl_Interp *interp; /* Current interpreter. */
538     int objc; /* Number of arguments. */
539     Tcl_Obj *CONST objv[]; /* Argument objects. */
540     {
541     char *format;
542     int numVars, nconversions, totalVars = -1;
543     int objIndex, offset, i, value, result, code;
544     char *string, *end, *baseString;
545     char op = 0;
546     int base = 0;
547     int underflow = 0;
548     size_t width;
549     long (*fn)() = NULL;
550     Tcl_UniChar ch, sch;
551     Tcl_Obj **objs = NULL, *objPtr = NULL;
552     int flags;
553     char buf[513]; /* Temporary buffer to hold scanned
554     * number strings before they are
555     * passed to strtoul. */
556    
557     if (objc < 3) {
558     Tcl_WrongNumArgs(interp, 1, objv,
559     "string format ?varName varName ...?");
560     return TCL_ERROR;
561     }
562    
563     format = Tcl_GetStringFromObj(objv[2], NULL);
564     numVars = objc-3;
565    
566     /*
567     * Check for errors in the format string.
568     */
569    
570     if (ValidateFormat(interp, format, numVars, &totalVars) == TCL_ERROR) {
571     return TCL_ERROR;
572     }
573    
574     /*
575     * Allocate space for the result objects.
576     */
577    
578     if (totalVars > 0) {
579     objs = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj*) * totalVars);
580     for (i = 0; i < totalVars; i++) {
581     objs[i] = NULL;
582     }
583     }
584    
585     string = Tcl_GetStringFromObj(objv[1], NULL);
586     baseString = string;
587    
588     /*
589     * Iterate over the format string filling in the result objects until
590     * we reach the end of input, the end of the format string, or there
591     * is a mismatch.
592     */
593    
594     objIndex = 0;
595     nconversions = 0;
596     while (*format != '\0') {
597     format += Tcl_UtfToUniChar(format, &ch);
598    
599     flags = 0;
600    
601     /*
602     * If we see whitespace in the format, skip whitespace in the string.
603     */
604    
605     if (Tcl_UniCharIsSpace(ch)) {
606     offset = Tcl_UtfToUniChar(string, &sch);
607     while (Tcl_UniCharIsSpace(sch)) {
608     if (*string == '\0') {
609     goto done;
610     }
611     string += offset;
612     offset = Tcl_UtfToUniChar(string, &sch);
613     }
614     continue;
615     }
616    
617     if (ch != '%') {
618     literal:
619     if (*string == '\0') {
620     underflow = 1;
621     goto done;
622     }
623     string += Tcl_UtfToUniChar(string, &sch);
624     if (ch != sch) {
625     goto done;
626     }
627     continue;
628     }
629    
630     format += Tcl_UtfToUniChar(format, &ch);
631     if (ch == '%') {
632     goto literal;
633     }
634    
635     /*
636     * Check for assignment suppression ('*') or an XPG3-style
637     * assignment ('%n$').
638     */
639    
640     if (ch == '*') {
641     flags |= SCAN_SUPPRESS;
642     format += Tcl_UtfToUniChar(format, &ch);
643     } else if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */
644     value = strtoul(format-1, &end, 10); /* INTL: "C" locale. */
645     if (*end == '$') {
646     format = end+1;
647     format += Tcl_UtfToUniChar(format, &ch);
648     objIndex = value - 1;
649     }
650     }
651    
652     /*
653     * Parse any width specifier.
654     */
655    
656     if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */
657     width = strtoul(format-1, &format, 10); /* INTL: "C" locale. */
658     format += Tcl_UtfToUniChar(format, &ch);
659     } else {
660     width = 0;
661     }
662    
663     /*
664     * Ignore size specifier.
665     */
666    
667     if ((ch == 'l') || (ch == 'L') || (ch == 'h')) {
668     format += Tcl_UtfToUniChar(format, &ch);
669     }
670    
671     /*
672     * Handle the various field types.
673     */
674    
675     switch (ch) {
676     case 'n':
677     if (!(flags & SCAN_SUPPRESS)) {
678     objPtr = Tcl_NewIntObj(string - baseString);
679     Tcl_IncrRefCount(objPtr);
680     objs[objIndex++] = objPtr;
681     }
682     nconversions++;
683     continue;
684    
685     case 'd':
686     op = 'i';
687     base = 10;
688     fn = (long (*)())strtol;
689     break;
690     case 'i':
691     op = 'i';
692     base = 0;
693     fn = (long (*)())strtol;
694     break;
695     case 'o':
696     op = 'i';
697     base = 8;
698     fn = (long (*)())strtol;
699     break;
700     case 'x':
701     op = 'i';
702     base = 16;
703     fn = (long (*)())strtol;
704     break;
705     case 'u':
706     op = 'i';
707     base = 10;
708     flags |= SCAN_UNSIGNED;
709     fn = (long (*)())strtoul;
710     break;
711    
712     case 'f':
713     case 'e':
714     case 'g':
715     op = 'f';
716     break;
717    
718     case 's':
719     op = 's';
720     break;
721    
722     case 'c':
723     op = 'c';
724     flags |= SCAN_NOSKIP;
725     break;
726     case '[':
727     op = '[';
728     flags |= SCAN_NOSKIP;
729     break;
730     }
731    
732     /*
733     * At this point, we will need additional characters from the
734     * string to proceed.
735     */
736    
737     if (*string == '\0') {
738     underflow = 1;
739     goto done;
740     }
741    
742     /*
743     * Skip any leading whitespace at the beginning of a field unless
744     * the format suppresses this behavior.
745     */
746    
747     if (!(flags & SCAN_NOSKIP)) {
748     while (*string != '\0') {
749     offset = Tcl_UtfToUniChar(string, &sch);
750     if (!Tcl_UniCharIsSpace(sch)) {
751     break;
752     }
753     string += offset;
754     }
755     if (*string == '\0') {
756     underflow = 1;
757     goto done;
758     }
759     }
760    
761     /*
762     * Perform the requested scanning operation.
763     */
764    
765     switch (op) {
766     case 's':
767     /*
768     * Scan a string up to width characters or whitespace.
769     */
770    
771     if (width == 0) {
772     width = (size_t) ~0;
773     }
774     end = string;
775     while (*end != '\0') {
776     offset = Tcl_UtfToUniChar(end, &sch);
777     if (Tcl_UniCharIsSpace(sch)) {
778     break;
779     }
780     end += offset;
781     if (--width == 0) {
782     break;
783     }
784     }
785     if (!(flags & SCAN_SUPPRESS)) {
786     objPtr = Tcl_NewStringObj(string, end-string);
787     Tcl_IncrRefCount(objPtr);
788     objs[objIndex++] = objPtr;
789     }
790     string = end;
791     break;
792    
793     case '[': {
794     CharSet cset;
795    
796     if (width == 0) {
797     width = (size_t) ~0;
798     }
799     end = string;
800    
801     format = BuildCharSet(&cset, format);
802     while (*end != '\0') {
803     offset = Tcl_UtfToUniChar(end, &sch);
804     if (!CharInSet(&cset, (int)sch)) {
805     break;
806     }
807     end += offset;
808     if (--width == 0) {
809     break;
810     }
811     }
812     ReleaseCharSet(&cset);
813    
814     if (string == end) {
815     /*
816     * Nothing matched the range, stop processing
817     */
818     goto done;
819     }
820     if (!(flags & SCAN_SUPPRESS)) {
821     objPtr = Tcl_NewStringObj(string, end-string);
822     Tcl_IncrRefCount(objPtr);
823     objs[objIndex++] = objPtr;
824     }
825     string = end;
826    
827     break;
828     }
829     case 'c':
830     /*
831     * Scan a single Unicode character.
832     */
833    
834     string += Tcl_UtfToUniChar(string, &sch);
835     if (!(flags & SCAN_SUPPRESS)) {
836     objPtr = Tcl_NewIntObj((int)sch);
837     Tcl_IncrRefCount(objPtr);
838     objs[objIndex++] = objPtr;
839     }
840     break;
841    
842     case 'i':
843     /*
844     * Scan an unsigned or signed integer.
845     */
846    
847     if ((width == 0) || (width > sizeof(buf) - 1)) {
848     width = sizeof(buf) - 1;
849     }
850     flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO;
851     for (end = buf; width > 0; width--) {
852     switch (*string) {
853     /*
854     * The 0 digit has special meaning at the beginning of
855     * a number. If we are unsure of the base, it
856     * indicates that we are in base 8 or base 16 (if it is
857     * followed by an 'x').
858     */
859     case '0':
860     if (base == 0) {
861     base = 8;
862     flags |= SCAN_XOK;
863     }
864     if (flags & SCAN_NOZERO) {
865     flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS
866     | SCAN_NOZERO);
867     } else {
868     flags &= ~(SCAN_SIGNOK | SCAN_XOK
869     | SCAN_NODIGITS);
870     }
871     goto addToInt;
872    
873     case '1': case '2': case '3': case '4':
874     case '5': case '6': case '7':
875     if (base == 0) {
876     base = 10;
877     }
878     flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS);
879     goto addToInt;
880    
881     case '8': case '9':
882     if (base == 0) {
883     base = 10;
884     }
885     if (base <= 8) {
886     break;
887     }
888     flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS);
889     goto addToInt;
890    
891     case 'A': case 'B': case 'C':
892     case 'D': case 'E': case 'F':
893     case 'a': case 'b': case 'c':
894     case 'd': case 'e': case 'f':
895     if (base <= 10) {
896     break;
897     }
898     flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS);
899     goto addToInt;
900    
901     case '+': case '-':
902     if (flags & SCAN_SIGNOK) {
903     flags &= ~SCAN_SIGNOK;
904     goto addToInt;
905     }
906     break;
907    
908     case 'x': case 'X':
909     if ((flags & SCAN_XOK) && (end == buf+1)) {
910     base = 16;
911     flags &= ~SCAN_XOK;
912     goto addToInt;
913     }
914     break;
915     }
916    
917     /*
918     * We got an illegal character so we are done accumulating.
919     */
920    
921     break;
922    
923     addToInt:
924     /*
925     * Add the character to the temporary buffer.
926     */
927    
928     *end++ = *string++;
929     if (*string == '\0') {
930     break;
931     }
932     }
933    
934     /*
935     * Check to see if we need to back up because we only got a
936     * sign or a trailing x after a 0.
937     */
938    
939     if (flags & SCAN_NODIGITS) {
940     if (*string == '\0') {
941     underflow = 1;
942     }
943     goto done;
944     } else if (end[-1] == 'x' || end[-1] == 'X') {
945     end--;
946     string--;
947     }
948    
949    
950     /*
951     * Scan the value from the temporary buffer. If we are
952     * returning a large unsigned value, we have to convert it back
953     * to a string since Tcl only supports signed values.
954     */
955    
956     if (!(flags & SCAN_SUPPRESS)) {
957     *end = '\0';
958     value = (int) (*fn)(buf, NULL, base);
959     if ((flags & SCAN_UNSIGNED) && (value < 0)) {
960     sprintf(buf, "%u", value); /* INTL: ISO digit */
961     objPtr = Tcl_NewStringObj(buf, -1);
962     } else {
963     objPtr = Tcl_NewIntObj(value);
964     }
965     Tcl_IncrRefCount(objPtr);
966     objs[objIndex++] = objPtr;
967     }
968    
969     break;
970    
971     case 'f':
972     /*
973     * Scan a floating point number
974     */
975    
976     if ((width == 0) || (width > sizeof(buf) - 1)) {
977     width = sizeof(buf) - 1;
978     }
979     flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_PTOK | SCAN_EXPOK;
980     for (end = buf; width > 0; width--) {
981     switch (*string) {
982     case '0': case '1': case '2': case '3':
983     case '4': case '5': case '6': case '7':
984     case '8': case '9':
985     flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS);
986     goto addToFloat;
987     case '+': case '-':
988     if (flags & SCAN_SIGNOK) {
989     flags &= ~SCAN_SIGNOK;
990     goto addToFloat;
991     }
992     break;
993     case '.':
994     if (flags & SCAN_PTOK) {
995     flags &= ~(SCAN_SIGNOK | SCAN_PTOK);
996     goto addToFloat;
997     }
998     break;
999     case 'e': case 'E':
1000     /*
1001     * An exponent is not allowed until there has
1002     * been at least one digit.
1003     */
1004    
1005     if ((flags & (SCAN_NODIGITS | SCAN_EXPOK))
1006     == SCAN_EXPOK) {
1007     flags = (flags & ~(SCAN_EXPOK|SCAN_PTOK))
1008     | SCAN_SIGNOK | SCAN_NODIGITS;
1009     goto addToFloat;
1010     }
1011     break;
1012     }
1013    
1014     /*
1015     * We got an illegal character so we are done accumulating.
1016     */
1017    
1018     break;
1019    
1020     addToFloat:
1021     /*
1022     * Add the character to the temporary buffer.
1023     */
1024    
1025     *end++ = *string++;
1026     if (*string == '\0') {
1027     break;
1028     }
1029     }
1030    
1031     /*
1032     * Check to see if we need to back up because we saw a
1033     * trailing 'e' or sign.
1034     */
1035    
1036     if (flags & SCAN_NODIGITS) {
1037     if (flags & SCAN_EXPOK) {
1038     /*
1039     * There were no digits at all so scanning has
1040     * failed and we are done.
1041     */
1042     if (*string == '\0') {
1043     underflow = 1;
1044     }
1045     goto done;
1046     }
1047    
1048     /*
1049     * We got a bad exponent ('e' and maybe a sign).
1050     */
1051    
1052     end--;
1053     string--;
1054     if (*end != 'e' && *end != 'E') {
1055     end--;
1056     string--;
1057     }
1058     }
1059    
1060     /*
1061     * Scan the value from the temporary buffer.
1062     */
1063    
1064     if (!(flags & SCAN_SUPPRESS)) {
1065     double dvalue;
1066     *end = '\0';
1067     dvalue = strtod(buf, NULL);
1068     objPtr = Tcl_NewDoubleObj(dvalue);
1069     Tcl_IncrRefCount(objPtr);
1070     objs[objIndex++] = objPtr;
1071     }
1072     break;
1073     }
1074     nconversions++;
1075     }
1076    
1077     done:
1078     result = 0;
1079     code = TCL_OK;
1080    
1081     if (numVars) {
1082     /*
1083     * In this case, variables were specified (classic scan)
1084     */
1085     for (i = 0; i < totalVars; i++) {
1086     if (objs[i] != NULL) {
1087     result++;
1088     if (Tcl_ObjSetVar2(interp, objv[i+3], NULL,
1089     objs[i], 0) == NULL) {
1090     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1091     "couldn't set variable \"",
1092     Tcl_GetString(objv[i+3]), "\"", (char *) NULL);
1093     code = TCL_ERROR;
1094     }
1095     Tcl_DecrRefCount(objs[i]);
1096     }
1097     }
1098     } else {
1099     /*
1100     * Here no vars were specified, we want a list returned (inline scan)
1101     */
1102     objPtr = Tcl_NewObj();
1103     for (i = 0; i < totalVars; i++) {
1104     if (objs[i] != NULL) {
1105     Tcl_ListObjAppendElement(NULL, objPtr, objs[i]);
1106     Tcl_DecrRefCount(objs[i]);
1107     } else {
1108     /*
1109     * More %-specifiers than matching chars, so we
1110     * just spit out empty strings for these
1111     */
1112     Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewObj());
1113     }
1114     }
1115     }
1116     ckfree((char*) objs);
1117     if (code == TCL_OK) {
1118     if (underflow && (nconversions == 0)) {
1119     if (numVars) {
1120     objPtr = Tcl_NewIntObj(-1);
1121     } else {
1122     if (objPtr) {
1123     Tcl_SetListObj(objPtr, 0, NULL);
1124     } else {
1125     objPtr = Tcl_NewObj();
1126     }
1127     }
1128     } else if (numVars) {
1129     objPtr = Tcl_NewIntObj(result);
1130     }
1131     Tcl_SetObjResult(interp, objPtr);
1132     }
1133     return code;
1134     }
1135    
1136     /* End of tclscan.c */

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25