📄 tclscan.c
字号:
if (totalVars > 0) { objs = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj*) * totalVars); for (i = 0; i < totalVars; i++) { objs[i] = NULL; } } string = Tcl_GetStringFromObj(objv[1], NULL); baseString = string; /* * Iterate over the format string filling in the result objects until * we reach the end of input, the end of the format string, or there * is a mismatch. */ objIndex = 0; nconversions = 0; while (*format != '\0') { format += Tcl_UtfToUniChar(format, &ch); flags = 0; /* * If we see whitespace in the format, skip whitespace in the string. */ if (Tcl_UniCharIsSpace(ch)) { offset = Tcl_UtfToUniChar(string, &sch); while (Tcl_UniCharIsSpace(sch)) { if (*string == '\0') { goto done; } string += offset; offset = Tcl_UtfToUniChar(string, &sch); } continue; } if (ch != '%') { literal: if (*string == '\0') { underflow = 1; goto done; } string += Tcl_UtfToUniChar(string, &sch); if (ch != sch) { goto done; } continue; } format += Tcl_UtfToUniChar(format, &ch); if (ch == '%') { goto literal; } /* * Check for assignment suppression ('*') or an XPG3-style * assignment ('%n$'). */ if (ch == '*') { flags |= SCAN_SUPPRESS; format += Tcl_UtfToUniChar(format, &ch); } else if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */ value = strtoul(format-1, &end, 10); /* INTL: "C" locale. */ if (*end == '$') { format = end+1; format += Tcl_UtfToUniChar(format, &ch); objIndex = (int) value - 1; } } /* * Parse any width specifier. */ if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */ width = strtoul(format-1, &format, 10); /* INTL: "C" locale. */ format += Tcl_UtfToUniChar(format, &ch); } else { width = 0; } /* * Handle any size specifier. */ switch (ch) { case 'l': case 'L':#ifndef TCL_WIDE_INT_IS_LONG flags |= SCAN_LONGER;#endif /* * Fall through so we skip to the next character. */ case 'h': format += Tcl_UtfToUniChar(format, &ch); } /* * Handle the various field types. */ switch (ch) { case 'n': if (!(flags & SCAN_SUPPRESS)) { objPtr = Tcl_NewIntObj(string - baseString); Tcl_IncrRefCount(objPtr); objs[objIndex++] = objPtr; } nconversions++; continue; case 'd': op = 'i'; base = 10; fn = (long (*)())strtol;#ifndef TCL_WIDE_INT_IS_LONG lfn = (Tcl_WideInt (*)())strtoll;#endif break; case 'i': op = 'i'; base = 0; fn = (long (*)())strtol;#ifndef TCL_WIDE_INT_IS_LONG lfn = (Tcl_WideInt (*)())strtoll;#endif break; case 'o': op = 'i'; base = 8; fn = (long (*)())strtoul;#ifndef TCL_WIDE_INT_IS_LONG lfn = (Tcl_WideInt (*)())strtoull;#endif break; case 'x': op = 'i'; base = 16; fn = (long (*)())strtoul;#ifndef TCL_WIDE_INT_IS_LONG lfn = (Tcl_WideInt (*)())strtoull;#endif break; case 'u': op = 'i'; base = 10; flags |= SCAN_UNSIGNED; fn = (long (*)())strtoul;#ifndef TCL_WIDE_INT_IS_LONG lfn = (Tcl_WideInt (*)())strtoull;#endif break; case 'f': case 'e': case 'g': op = 'f'; break; case 's': op = 's'; break; case 'c': op = 'c'; flags |= SCAN_NOSKIP; break; case '[': op = '['; flags |= SCAN_NOSKIP; break; } /* * At this point, we will need additional characters from the * string to proceed. */ if (*string == '\0') { underflow = 1; goto done; } /* * Skip any leading whitespace at the beginning of a field unless * the format suppresses this behavior. */ if (!(flags & SCAN_NOSKIP)) { while (*string != '\0') { offset = Tcl_UtfToUniChar(string, &sch); if (!Tcl_UniCharIsSpace(sch)) { break; } string += offset; } if (*string == '\0') { underflow = 1; goto done; } } /* * Perform the requested scanning operation. */ switch (op) { case 's': /* * Scan a string up to width characters or whitespace. */ if (width == 0) { width = (size_t) ~0; } end = string; while (*end != '\0') { offset = Tcl_UtfToUniChar(end, &sch); if (Tcl_UniCharIsSpace(sch)) { break; } end += offset; if (--width == 0) { break; } } if (!(flags & SCAN_SUPPRESS)) { objPtr = Tcl_NewStringObj(string, end-string); Tcl_IncrRefCount(objPtr); objs[objIndex++] = objPtr; } string = end; break; case '[': { CharSet cset; if (width == 0) { width = (size_t) ~0; } end = string; format = BuildCharSet(&cset, format); while (*end != '\0') { offset = Tcl_UtfToUniChar(end, &sch); if (!CharInSet(&cset, (int)sch)) { break; } end += offset; if (--width == 0) { break; } } ReleaseCharSet(&cset); if (string == end) { /* * Nothing matched the range, stop processing */ goto done; } if (!(flags & SCAN_SUPPRESS)) { objPtr = Tcl_NewStringObj(string, end-string); Tcl_IncrRefCount(objPtr); objs[objIndex++] = objPtr; } string = end; break; } case 'c': /* * Scan a single Unicode character. */ string += Tcl_UtfToUniChar(string, &sch); if (!(flags & SCAN_SUPPRESS)) { objPtr = Tcl_NewIntObj((int)sch); Tcl_IncrRefCount(objPtr); objs[objIndex++] = objPtr; } break; case 'i': /* * Scan an unsigned or signed integer. */ if ((width == 0) || (width > sizeof(buf) - 1)) { width = sizeof(buf) - 1; } flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO; for (end = buf; width > 0; width--) { switch (*string) { /* * The 0 digit has special meaning at the beginning of * a number. If we are unsure of the base, it * indicates that we are in base 8 or base 16 (if it is * followed by an 'x'). * * 8.1 - 8.3.4 incorrectly handled 0x... base-16 * cases for %x by not reading the 0x as the * auto-prelude for base-16. [Bug #495213] */ case '0': if (base == 0) { base = 8; flags |= SCAN_XOK; } if (base == 16) { flags |= SCAN_XOK; } if (flags & SCAN_NOZERO) { flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO); } else { flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); } goto addToInt; case '1': case '2': case '3': case '4': case '5': case '6': case '7': if (base == 0) { base = 10; } flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); goto addToInt; case '8': case '9': if (base == 0) { base = 10; } if (base <= 8) { break; } flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); goto addToInt; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': if (base <= 10) { break; } flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS); goto addToInt; case '+': case '-': if (flags & SCAN_SIGNOK) { flags &= ~SCAN_SIGNOK; goto addToInt; } break; case 'x': case 'X': if ((flags & SCAN_XOK) && (end == buf+1)) { base = 16; flags &= ~SCAN_XOK; goto addToInt; } break; } /* * We got an illegal character so we are done accumulating. */ break; addToInt: /* * Add the character to the temporary buffer. */ *end++ = *string++; if (*string == '\0') { break; } } /* * Check to see if we need to back up because we only got a * sign or a trailing x after a 0. */ if (flags & SCAN_NODIGITS) { if (*string == '\0') { underflow = 1; } goto done; } else if (end[-1] == 'x' || end[-1] == 'X') { end--; string--; } /* * Scan the value from the temporary buffer. If we are * returning a large unsigned value, we have to convert it back * to a string since Tcl only supports signed values. */ if (!(flags & SCAN_SUPPRESS)) { *end = '\0';#ifndef TCL_WIDE_INT_IS_LONG if (flags & SCAN_LONGER) { wideValue = (Tcl_WideInt) (*lfn)(buf, NULL, base); if ((flags & SCAN_UNSIGNED) && (wideValue < 0)) { /* INTL: ISO digit */ sprintf(buf, "%" TCL_LL_MODIFIER "u", (Tcl_WideUInt)wideValue); objPtr = Tcl_NewStringObj(buf, -1); } else { objPtr = Tcl_NewWideIntObj(wideValue); } } else {#endif /* !TCL_WIDE_INT_IS_LONG */ value = (long) (*fn)(buf, NULL, base); if ((flags & SCAN_UNSIGNED) && (value < 0)) { sprintf(buf, "%lu", value); /* INTL: ISO digit */ objPtr = Tcl_NewStringObj(buf, -1); } else { if ((unsigned long) value > UINT_MAX) { objPtr = Tcl_NewLongObj(value); } else { objPtr = Tcl_NewIntObj(value); } }#ifndef TCL_WIDE_INT_IS_LONG }#endif Tcl_IncrRefCount(objPtr); objs[objIndex++] = objPtr; } break; case 'f': /* * Scan a floating point number */ if ((width == 0) || (width > sizeof(buf) - 1)) { width = sizeof(buf) - 1; } flags &= ~SCAN_LONGER; flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_PTOK | SCAN_EXPOK; for (end = buf; width > 0; width--) { switch (*string) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS); goto addToFloat; case '+': case '-': if (flags & SCAN_SIGNOK) { flags &= ~SCAN_SIGNOK; goto addToFloat; } break; case '.': if (flags & SCAN_PTOK) { flags &= ~(SCAN_SIGNOK | SCAN_PTOK); goto addToFloat; } break; case 'e': case 'E': /* * An exponent is not allowed until there has * been at least one digit. */ if ((flags & (SCAN_NODIGITS | SCAN_EXPOK)) == SCAN_EXPOK) { flags = (flags & ~(SCAN_EXPOK|SCAN_PTOK)) | SCAN_SIGNOK | SCAN_NODIGITS; goto addToFloat; } break; } /* * We got an illegal character so we are done accumulating. */ break; addToFloat: /* * Add the character to the temporary buffer. */ *end++ = *string++; if (*string == '\0') { break; } } /* * Check to see if we need to back up because we saw a * trailing 'e' or sign. */ if (flags & SCAN_NODIGITS) { if (flags & SCAN_EXPOK) { /* * There were no digits at all so scanning has * failed and we are done. */ if (*string == '\0') { underflow = 1; } goto done; } /* * We got a bad exponent ('e' and maybe a sign). */ end--; string--; if (*end != 'e' && *end != 'E') { end--; string--; } } /* * Scan the value from the temporary buffer. */ if (!(flags & SCAN_SUPPRESS)) { double dvalue; *end = '\0'; dvalue = strtod(buf, NULL); objPtr = Tcl_NewDoubleObj(dvalue); Tcl_IncrRefCount(objPtr); objs[objIndex++] = objPtr; } break; } nconversions++; } done: result = 0; code = TCL_OK; if (numVars) { /* * In this case, variables were specified (classic scan) */ for (i = 0; i < totalVars; i++) { if (objs[i] != NULL) { result++; if (Tcl_ObjSetVar2(interp, objv[i+3], NULL, objs[i], 0) == NULL) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "couldn't set variable \"", Tcl_GetString(objv[i+3]), "\"", (char *) NULL); code = TCL_ERROR; } Tcl_DecrRefCount(objs[i]); } } } else { /* * Here no vars were specified, we want a list returned (inline scan) */ objPtr = Tcl_NewObj(); for (i = 0; i < totalVars; i++) { if (objs[i] != NULL) { Tcl_ListObjAppendElement(NULL, objPtr, objs[i]); Tcl_DecrRefCount(objs[i]); } else { /* * More %-specifiers than matching chars, so we * just spit out empty strings for these */ Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewObj()); } } } if (objs != NULL) { ckfree((char*) objs); } if (code == TCL_OK) { if (underflow && (nconversions == 0)) { if (numVars) { objPtr = Tcl_NewIntObj(-1); } else { if (objPtr) { Tcl_SetListObj(objPtr, 0, NULL); } else { objPtr = Tcl_NewObj(); } } } else if (numVars) { objPtr = Tcl_NewIntObj(result); } Tcl_SetObjResult(interp, objPtr); } return code;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -