⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tclscan.c

📁 tcl是工具命令语言
💻 C
📖 第 1 页 / 共 2 页
字号:
/*  * tclScan.c -- * *	This file contains the implementation of the "scan" command. * * Copyright (c) 1998 by Scriptics Corporation. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tclScan.c,v 1.12 2002/02/25 15:23:02 dkf Exp $ */#include "tclInt.h"/* * For strtoll() and strtoull() declarations on some platforms... */#include "tclPort.h"/* * Flag values used by Tcl_ScanObjCmd. */#define SCAN_NOSKIP	0x1		  /* Don't skip blanks. */#define SCAN_SUPPRESS	0x2		  /* Suppress assignment. */#define SCAN_UNSIGNED	0x4		  /* Read an unsigned value. */#define SCAN_WIDTH	0x8		  /* A width value was supplied. */#define SCAN_SIGNOK	0x10		  /* A +/- character is allowed. */#define SCAN_NODIGITS	0x20		  /* No digits have been scanned. */#define SCAN_NOZERO	0x40		  /* No zero digits have been scanned. */#define SCAN_XOK	0x80		  /* An 'x' is allowed. */#define SCAN_PTOK	0x100		  /* Decimal point is allowed. */#define SCAN_EXPOK	0x200		  /* An exponent is allowed. */#define SCAN_LONGER	0x400		  /* Asked for a wide value. *//* * The following structure contains the information associated with * a character set. */typedef struct CharSet {    int exclude;		/* 1 if this is an exclusion set. */    int nchars;    Tcl_UniChar *chars;    int nranges;    struct Range {	Tcl_UniChar start;	Tcl_UniChar end;    } *ranges;} CharSet;/* * Declarations for functions used only in this file. */static char *	BuildCharSet _ANSI_ARGS_((CharSet *cset, char *format));static int	CharInSet _ANSI_ARGS_((CharSet *cset, int ch));static void	ReleaseCharSet _ANSI_ARGS_((CharSet *cset));static int	ValidateFormat _ANSI_ARGS_((Tcl_Interp *interp, char *format,		    int numVars, int *totalVars));/* *---------------------------------------------------------------------- * * BuildCharSet -- * *	This function examines a character set format specification *	and builds a CharSet containing the individual characters and *	character ranges specified. * * Results: *	Returns the next format position. * * Side effects: *	Initializes the charset. * *---------------------------------------------------------------------- */static char *BuildCharSet(cset, format)    CharSet *cset;    char *format;		/* Points to first char of set. */{    Tcl_UniChar ch, start;    int offset, nranges;    char *end;    memset(cset, 0, sizeof(CharSet));        offset = Tcl_UtfToUniChar(format, &ch);    if (ch == '^') {	cset->exclude = 1;	format += offset;	offset = Tcl_UtfToUniChar(format, &ch);    }    end = format + offset;    /*     * Find the close bracket so we can overallocate the set.     */    if (ch == ']') {	end += Tcl_UtfToUniChar(end, &ch);    }    nranges = 0;    while (ch != ']') {	if (ch == '-') {	    nranges++;	}	end += Tcl_UtfToUniChar(end, &ch);    }    cset->chars = (Tcl_UniChar *) ckalloc(sizeof(Tcl_UniChar)	    * (end - format - 1));    if (nranges > 0) {	cset->ranges = (struct Range *) ckalloc(sizeof(struct Range)*nranges);    } else {	cset->ranges = NULL;    }    /*     * Now build the character set.     */    cset->nchars = cset->nranges = 0;    format += Tcl_UtfToUniChar(format, &ch);    start = ch;    if (ch == ']' || ch == '-') {	cset->chars[cset->nchars++] = ch;	format += Tcl_UtfToUniChar(format, &ch);    }    while (ch != ']') {	if (*format == '-') {	    /*	     * This may be the first character of a range, so don't add	     * it yet.	     */	    start = ch;	} else if (ch == '-') {	    /*	     * Check to see if this is the last character in the set, in which	     * case it is not a range and we should add the previous character	     * as well as the dash.	     */	    if (*format == ']') {		cset->chars[cset->nchars++] = start;		cset->chars[cset->nchars++] = ch;	    } else {		format += Tcl_UtfToUniChar(format, &ch);		/*		 * Check to see if the range is in reverse order.		 */		if (start < ch) {		    cset->ranges[cset->nranges].start = start;		    cset->ranges[cset->nranges].end = ch;		} else {		    cset->ranges[cset->nranges].start = ch;		    cset->ranges[cset->nranges].end = start;		}		    		cset->nranges++;	    }	} else {	    cset->chars[cset->nchars++] = ch;	}	format += Tcl_UtfToUniChar(format, &ch);    }    return format;}/* *---------------------------------------------------------------------- * * CharInSet -- * *	Check to see if a character matches the given set. * * Results: *	Returns non-zero if the character matches the given set. * * Side effects: *	None. * *---------------------------------------------------------------------- */static intCharInSet(cset, c)    CharSet *cset;    int c;			/* Character to test, passed as int because				 * of non-ANSI prototypes. */{    Tcl_UniChar ch = (Tcl_UniChar) c;    int i, match = 0;    for (i = 0; i < cset->nchars; i++) {	if (cset->chars[i] == ch) {	    match = 1;	    break;	}    }    if (!match) {	for (i = 0; i < cset->nranges; i++) {	    if ((cset->ranges[i].start <= ch)		    && (ch <= cset->ranges[i].end)) {		match = 1;		break;	    }	}    }    return (cset->exclude ? !match : match);    }/* *---------------------------------------------------------------------- * * ReleaseCharSet -- * *	Free the storage associated with a character set. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */static voidReleaseCharSet(cset)    CharSet *cset;{    ckfree((char *)cset->chars);    if (cset->ranges) {	ckfree((char *)cset->ranges);    }}/* *---------------------------------------------------------------------- * * ValidateFormat -- * *	Parse the format string and verify that it is properly formed *	and that there are exactly enough variables on the command line. * * Results: *	A standard Tcl result. * * Side effects: *	May place an error in the interpreter result. * *---------------------------------------------------------------------- */static intValidateFormat(interp, format, numVars, totalSubs)    Tcl_Interp *interp;		/* Current interpreter. */    char *format;		/* The format string. */    int numVars;		/* The number of variables passed to the				 * scan command. */    int *totalSubs;		/* The number of variables that will be				 * required. */{#define STATIC_LIST_SIZE 16    int gotXpg, gotSequential, value, i, flags;    char *end;    Tcl_UniChar ch;    int staticAssign[STATIC_LIST_SIZE];    int *nassign = staticAssign;    int objIndex, xpgSize, nspace = STATIC_LIST_SIZE;    char buf[TCL_UTF_MAX+1];    /*     * Initialize an array that records the number of times a variable     * is assigned to by the format string.  We use this to detect if     * a variable is multiply assigned or left unassigned.     */    if (numVars > nspace) {	nassign = (int*)ckalloc(sizeof(int) * numVars);	nspace = numVars;    }    for (i = 0; i < nspace; i++) {	nassign[i] = 0;    }    xpgSize = objIndex = gotXpg = gotSequential = 0;    while (*format != '\0') {	format += Tcl_UtfToUniChar(format, &ch);	flags = 0;	if (ch != '%') {	    continue;	}	format += Tcl_UtfToUniChar(format, &ch);	if (ch == '%') {	    continue;	}	if (ch == '*') {	    flags |= SCAN_SUPPRESS;	    format += Tcl_UtfToUniChar(format, &ch);	    goto xpgCheckDone;	}	if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */	    /*	     * Check for an XPG3-style %n$ specification.  Note: there	     * must not be a mixture of XPG3 specs and non-XPG3 specs	     * in the same format string.	     */	    value = strtoul(format-1, &end, 10); /* INTL: "C" locale. */	    if (*end != '$') {		goto notXpg;	    }	    format = end+1;	    format += Tcl_UtfToUniChar(format, &ch);	    gotXpg = 1;	    if (gotSequential) {		goto mixedXPG;	    }	    objIndex = value - 1;	    if ((objIndex < 0) || (numVars && (objIndex >= numVars))) {		goto badIndex;	    } else if (numVars == 0) {		/*		 * In the case where no vars are specified, the user can		 * specify %9999$ legally, so we have to consider special		 * rules for growing the assign array.  'value' is		 * guaranteed to be > 0.		 */		xpgSize = (xpgSize > value) ? xpgSize : value;	    }	    goto xpgCheckDone;	}	notXpg:	gotSequential = 1;	if (gotXpg) {	    mixedXPG:	    Tcl_SetResult(interp,		    "cannot mix \"%\" and \"%n$\" conversion specifiers",		    TCL_STATIC);	    goto error;	}	xpgCheckDone:	/*	 * Parse any width specifier.	 */	if ((ch < 0x80) && isdigit(UCHAR(ch))) { /* INTL: "C" locale. */	    value = strtoul(format-1, &format, 10); /* INTL: "C" locale. */	    flags |= SCAN_WIDTH;	    format += Tcl_UtfToUniChar(format, &ch);	}	/*	 * Handle any size specifier.	 */	switch (ch) {	case 'l':	case 'L':#ifndef TCL_WIDE_INT_IS_LONG	    flags |= SCAN_LONGER;#endif	case 'h':	    format += Tcl_UtfToUniChar(format, &ch);	}	if (!(flags & SCAN_SUPPRESS) && numVars && (objIndex >= numVars)) {	    goto badIndex;	}	/*	 * Handle the various field types.	 */	switch (ch) {	    case 'c':                if (flags & SCAN_WIDTH) {		    Tcl_SetResult(interp,			    "field width may not be specified in %c conversion",			    TCL_STATIC);		    goto error;                }		/*		 * Fall through!		 */	    case 'n':	    case 's':		if (flags & SCAN_LONGER) {		invalidLonger:		    buf[Tcl_UniCharToUtf(ch, buf)] = '\0';		    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),			   "'l' modifier may not be specified in %", buf,			   " conversion", NULL);		    goto error;		}		/*		 * Fall through!		 */	    case 'd':	    case 'e':	    case 'f':	    case 'g':	    case 'i':	    case 'o':	    case 'u':	    case 'x': 		break;		/*		 * Bracket terms need special checking		 */	    case '[':		if (flags & SCAN_LONGER) {		    goto invalidLonger;		}		if (*format == '\0') {		    goto badSet;		}		format += Tcl_UtfToUniChar(format, &ch);		if (ch == '^') {		    if (*format == '\0') {			goto badSet;		    }		    format += Tcl_UtfToUniChar(format, &ch);		}		if (ch == ']') {		    if (*format == '\0') {			goto badSet;		    }		    format += Tcl_UtfToUniChar(format, &ch);		}		while (ch != ']') {		    if (*format == '\0') {			goto badSet;		    }		    format += Tcl_UtfToUniChar(format, &ch);		}		break;	    badSet:		Tcl_SetResult(interp, "unmatched [ in format string",			TCL_STATIC);		goto error;	    default:	    {		char buf[TCL_UTF_MAX+1];		buf[Tcl_UniCharToUtf(ch, buf)] = '\0';		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),			"bad scan conversion character \"", buf, "\"", NULL);		goto error;	    }	}	if (!(flags & SCAN_SUPPRESS)) {	    if (objIndex >= nspace) {		/*		 * Expand the nassign buffer.  If we are using XPG specifiers,		 * make sure that we grow to a large enough size.  xpgSize is		 * guaranteed to be at least one larger than objIndex.		 */		value = nspace;		if (xpgSize) {		    nspace = xpgSize;		} else {		    nspace += STATIC_LIST_SIZE;		}		if (nassign == staticAssign) {		    nassign = (void *)ckalloc(nspace * sizeof(int));		    for (i = 0; i < STATIC_LIST_SIZE; ++i) {			nassign[i] = staticAssign[i];		    }		} else {		    nassign = (void *)ckrealloc((void *)nassign,			    nspace * sizeof(int));		}		for (i = value; i < nspace; i++) {		    nassign[i] = 0;		}	    }	    nassign[objIndex]++;	    objIndex++;	}    }    /*     * Verify that all of the variable were assigned exactly once.     */    if (numVars == 0) {	if (xpgSize) {	    numVars = xpgSize;	} else {	    numVars = objIndex;	}    }    if (totalSubs) {	*totalSubs = numVars;    }    for (i = 0; i < numVars; i++) {	if (nassign[i] > 1) {	    Tcl_SetResult(interp, "variable is assigned by multiple \"%n$\" conversion specifiers", TCL_STATIC);	    goto error;	} else if (!xpgSize && (nassign[i] == 0)) {	    /*	     * If the space is empty, and xpgSize is 0 (means XPG wasn't	     * used, and/or numVars != 0), then too many vars were given	     */	    Tcl_SetResult(interp, "variable is not assigned by any conversion specifiers", TCL_STATIC);	    goto error;	}    }    if (nassign != staticAssign) {	ckfree((char *)nassign);    }    return TCL_OK;    badIndex:    if (gotXpg) {	Tcl_SetResult(interp, "\"%n$\" argument index out of range",		TCL_STATIC);    } else {	Tcl_SetResult(interp, 		"different numbers of variable names and field specifiers",		TCL_STATIC);    }    error:    if (nassign != staticAssign) {	ckfree((char *)nassign);    }    return TCL_ERROR;#undef STATIC_LIST_SIZE}/* *---------------------------------------------------------------------- * * Tcl_ScanObjCmd -- * *	This procedure is invoked to process the "scan" Tcl command. *	See the user documentation for details on what it does. * * Results: *	A standard Tcl result. * * Side effects: *	See the user documentation. * *---------------------------------------------------------------------- */	/* ARGSUSED */intTcl_ScanObjCmd(dummy, interp, objc, objv)    ClientData dummy;    	/* Not used. */    Tcl_Interp *interp;		/* Current interpreter. */    int objc;			/* Number of arguments. */    Tcl_Obj *CONST objv[];	/* Argument objects. */{    char *format;    int numVars, nconversions, totalVars = -1;    int objIndex, offset, i, result, code;    long value;    char *string, *end, *baseString;    char op = 0;    int base = 0;    int underflow = 0;    size_t width;    long (*fn)() = NULL;#ifndef TCL_WIDE_INT_IS_LONG    Tcl_WideInt (*lfn)() = NULL;    Tcl_WideInt wideValue;#endif    Tcl_UniChar ch, sch;    Tcl_Obj **objs = NULL, *objPtr = NULL;    int flags;    char buf[513];			  /* Temporary buffer to hold scanned					   * number strings before they are					   * passed to strtoul. */    if (objc < 3) {        Tcl_WrongNumArgs(interp, 1, objv,		"string format ?varName varName ...?");	return TCL_ERROR;    }    format = Tcl_GetStringFromObj(objv[2], NULL);    numVars = objc-3;    /*     * Check for errors in the format string.     */        if (ValidateFormat(interp, format, numVars, &totalVars) == TCL_ERROR) {	return TCL_ERROR;    }    /*     * Allocate space for the result objects.     */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -