tclScan.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:26k
源码类别:

通讯编程

开发平台:

Visual C++

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