tclcmdah.c

来自「tcl是工具命令语言」· C语言 代码 · 共 2,394 行 · 第 1/5 页

C
2,394
字号
    ClientData dummy;    	/* Not used. */    Tcl_Interp *interp;		/* Current interpreter. */    int objc;			/* Number of arguments. */    Tcl_Obj *CONST objv[];	/* Argument objects. */{    char *format;		/* Used to read characters from the format				 * string. */    int formatLen;		/* The length of the format string */    char *endPtr;		/* Points to the last char in format array */    char newFormat[43];		/* A new format specifier is generated here. */    int width;			/* Field width from field specifier, or 0 if				 * no width given. */    int precision;		/* Field precision from field specifier, or 0				 * if no precision given. */    int size;			/* Number of bytes needed for result of				 * conversion, based on type of conversion				 * ("e", "s", etc.), width, and precision. */    long intValue;		/* Used to hold value to pass to sprintf, if				 * it's a one-word integer or char value */    char *ptrValue = NULL;	/* Used to hold value to pass to sprintf, if				 * it's a one-word value. */    double doubleValue;		/* Used to hold value to pass to sprintf if				 * it's a double value. */#ifndef TCL_WIDE_INT_IS_LONG    Tcl_WideInt wideValue;	/* Used to hold value to pass to sprintf if				 * it's a 'long long' value. */#endif /* TCL_WIDE_INT_IS_LONG */    int whichValue;		/* Indicates which of intValue, ptrValue,				 * or doubleValue has the value to pass to				 * sprintf, according to the following				 * definitions: */#   define INT_VALUE 0#   define CHAR_VALUE 1#   define PTR_VALUE 2#   define DOUBLE_VALUE 3#   define STRING_VALUE 4#   define WIDE_VALUE 5#   define MAX_FLOAT_SIZE 320    Tcl_Obj *resultPtr;  	/* Where result is stored finally. */    char staticBuf[MAX_FLOAT_SIZE + 1];                                /* A static buffer to copy the format results 				 * into */    char *dst = staticBuf;      /* The buffer that sprintf writes into each				 * time the format processes a specifier */    int dstSize = MAX_FLOAT_SIZE;                                /* The size of the dst buffer */    int noPercent;		/* Special case for speed:  indicates there's				 * no field specifier, just a string to copy.*/    int objIndex;		/* Index of argument to substitute next. */    int gotXpg = 0;		/* Non-zero means that an XPG3 %n$-style				 * specifier has been seen. */    int gotSequential = 0;	/* Non-zero means that a regular sequential				 * (non-XPG3) conversion specifier has been				 * seen. */    int useShort;		/* Value to be printed is short (half word). */    char *end;			/* Used to locate end of numerical fields. */    int stringLen = 0;		/* Length of string in characters rather				 * than bytes.  Used for %s substitution. */    int gotMinus;		/* Non-zero indicates that a minus flag has				 * been seen in the current field. */    int gotPrecision;		/* Non-zero indicates that a precision has				 * been set for the current field. */    int gotZero;		/* Non-zero indicates that a zero flag has				 * been seen in the current field. */#ifndef TCL_WIDE_INT_IS_LONG    int useWide;		/* Value to be printed is Tcl_WideInt. */#endif /* TCL_WIDE_INT_IS_LONG */    /*     * This procedure is a bit nasty.  The goal is to use sprintf to     * do most of the dirty work.  There are several problems:     * 1. this procedure can't trust its arguments.     * 2. we must be able to provide a large enough result area to hold     *    whatever's generated.  This is hard to estimate.     * 3. there's no way to move the arguments from objv to the call     *    to sprintf in a reasonable way.  This is particularly nasty     *    because some of the arguments may be two-word values (doubles     *    and wide-ints).     * So, what happens here is to scan the format string one % group     * at a time, making many individual calls to sprintf.     */    if (objc < 2) {        Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg arg ...?");	return TCL_ERROR;    }    format = Tcl_GetStringFromObj(objv[1], &formatLen);    endPtr = format + formatLen;    resultPtr = Tcl_NewObj();    objIndex = 2;    while (format < endPtr) {	register char *newPtr = newFormat;	width = precision = noPercent = useShort = 0;	gotZero = gotMinus = gotPrecision = 0;#ifndef TCL_WIDE_INT_IS_LONG	useWide = 0;#endif /* TCL_WIDE_INT_IS_LONG */	whichValue = PTR_VALUE;	/*	 * Get rid of any characters before the next field specifier.	 */	if (*format != '%') {	    ptrValue = format;	    while ((*format != '%') && (format < endPtr)) {		format++;	    }	    size = format - ptrValue;	    noPercent = 1;	    goto doField;	}	if (format[1] == '%') {	    ptrValue = format;	    size = 1;	    noPercent = 1;	    format += 2;	    goto doField;	}	/*	 * Parse off a field specifier, compute how many characters	 * will be needed to store the result, and substitute for	 * "*" size specifiers.	 */	*newPtr = '%';	newPtr++;	format++;	if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */	    int tmp;	    /*	     * 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.	     */	    tmp = strtoul(format, &end, 10);	/* INTL: "C" locale. */	    if (*end != '$') {		goto notXpg;	    }	    format = end+1;	    gotXpg = 1;	    if (gotSequential) {		goto mixedXPG;	    }	    objIndex = tmp+1;	    if ((objIndex < 2) || (objIndex >= objc)) {		goto badIndex;	    }	    goto xpgCheckDone;	}	notXpg:	gotSequential = 1;	if (gotXpg) {	    goto mixedXPG;	}	xpgCheckDone:	while ((*format == '-') || (*format == '#') || (*format == '0')		|| (*format == ' ') || (*format == '+')) {	    if (*format == '-') {		gotMinus = 1;	    }	    if (*format == '0') {		/*		 * This will be handled by sprintf for numbers, but we		 * need to do the char/string ones ourselves		 */		gotZero = 1;	    }	    *newPtr = *format;	    newPtr++;	    format++;	}	if (isdigit(UCHAR(*format))) {		/* INTL: Tcl source. */	    width = strtoul(format, &end, 10);	/* INTL: Tcl source. */	    format = end;	} else if (*format == '*') {	    if (objIndex >= objc) {		goto badIndex;	    }	    if (Tcl_GetIntFromObj(interp,	/* INTL: Tcl source. */		    objv[objIndex], &width) != TCL_OK) {		goto fmtError;	    }	    if (width < 0) {		width = -width;		*newPtr = '-';		gotMinus = 1;		newPtr++;	    }	    objIndex++;	    format++;	}	if (width > 100000) {	    /*	     * Don't allow arbitrarily large widths:  could cause core	     * dump when we try to allocate a zillion bytes of memory	     * below.	     */	    width = 100000;	} else if (width < 0) {	    width = 0;	}	if (width != 0) {	    TclFormatInt(newPtr, width);	/* INTL: printf format. */	    while (*newPtr != 0) {		newPtr++;	    }	}	if (*format == '.') {	    *newPtr = '.';	    newPtr++;	    format++;	    gotPrecision = 1;	}	if (isdigit(UCHAR(*format))) {		/* INTL: Tcl source. */	    precision = strtoul(format, &end, 10);  /* INTL: "C" locale. */	    format = end;	} else if (*format == '*') {	    if (objIndex >= objc) {		goto badIndex;	    }	    if (Tcl_GetIntFromObj(interp,	/* INTL: Tcl source. */		    objv[objIndex], &precision) != TCL_OK) {		goto fmtError;	    }	    objIndex++;	    format++;	}	if (gotPrecision) {	    TclFormatInt(newPtr, precision);	/* INTL: printf format. */	    while (*newPtr != 0) {		newPtr++;	    }	}	if (*format == 'l') {#ifndef TCL_WIDE_INT_IS_LONG	    useWide = 1;	    strcpy(newPtr, TCL_LL_MODIFIER);	    newPtr += TCL_LL_MODIFIER_SIZE;#endif /* TCL_WIDE_INT_IS_LONG */	    format++;	} else if (*format == 'h') {	    useShort = 1;	    *newPtr = 'h';	    newPtr++;	    format++;	}	*newPtr = *format;	newPtr++;	*newPtr = 0;	if (objIndex >= objc) {	    goto badIndex;	}	switch (*format) {	    case 'i':		newPtr[-1] = 'd';	    case 'd':	    case 'o':	    case 'u':	    case 'x':	    case 'X':#ifndef TCL_WIDE_INT_IS_LONG		if (useWide) {		    if (Tcl_GetWideIntFromObj(interp, /* INTL: Tcl source. */			    objv[objIndex], &wideValue) != TCL_OK) {			goto fmtError;		    }		    whichValue = WIDE_VALUE;		    size = 40 + precision;		    break;		}#endif /* TCL_WIDE_INT_IS_LONG */		if (Tcl_GetLongFromObj(interp,	      /* INTL: Tcl source. */			objv[objIndex], &intValue) != TCL_OK) {		    goto fmtError;		}#if (LONG_MAX > INT_MAX)		/*		 * Add the 'l' for long format type because we are on		 * an LP64 archtecture and we are really going to pass		 * a long argument to sprintf.		 */		newPtr++;		*newPtr = 0;		newPtr[-1] = newPtr[-2];		newPtr[-2] = 'l';#endif /* LONG_MAX > INT_MAX */		whichValue = INT_VALUE;		size = 40 + precision;		break;	    case 's':		/*		 * Compute the length of the string in characters and add		 * any additional space required by the field width.  All of		 * the extra characters will be spaces, so one byte per		 * character is adequate.		 */		whichValue = STRING_VALUE;		ptrValue = Tcl_GetStringFromObj(objv[objIndex], &size);		stringLen = Tcl_NumUtfChars(ptrValue, size);		if (gotPrecision && (precision < stringLen)) {		    stringLen = precision;		}		size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue;		if (width > stringLen) {		    size += (width - stringLen);		}		break;	    case 'c':		if (Tcl_GetLongFromObj(interp,	/* INTL: Tcl source. */			objv[objIndex], &intValue) != TCL_OK) {		    goto fmtError;		}		whichValue = CHAR_VALUE;		size = width + TCL_UTF_MAX;		break;	    case 'e':	    case 'E':	    case 'f':	    case 'g':	    case 'G':		if (Tcl_GetDoubleFromObj(interp, /* INTL: Tcl source. */			objv[objIndex], &doubleValue) != TCL_OK) {		    goto fmtError;		}		whichValue = DOUBLE_VALUE;		size = MAX_FLOAT_SIZE;		if (precision > 10) {		    size += precision;		}		break;	    case 0:		Tcl_SetResult(interp,		        "format string ended in middle of field specifier",			TCL_STATIC);		goto fmtError;	    default: {		char buf[40];		sprintf(buf, "bad field specifier \"%c\"", *format);		Tcl_SetResult(interp, buf, TCL_VOLATILE);		goto fmtError;	    }	}	objIndex++;	format++;	/*	 * Make sure that there's enough space to hold the formatted	 * result, then format it.	 */	doField:	if (width > size) {	    size = width;	}	if (noPercent) {	    Tcl_AppendToObj(resultPtr, ptrValue, size);	} else {	    if (size > dstSize) {	        if (dst != staticBuf) {		    ckfree(dst);		}		dst = (char *) ckalloc((unsigned) (size + 1));		dstSize = size;	    }	    switch (whichValue) {		case DOUBLE_VALUE: {		    sprintf(dst, newFormat, doubleValue); /* INTL: user locale. */		    break;		}#ifndef TCL_WIDE_INT_IS_LONG		case WIDE_VALUE: {		    sprintf(dst, newFormat, wideValue);		    break;		}#endif /* TCL_WIDE_INT_IS_LONG */		case INT_VALUE: {		    if (useShort) {			sprintf(dst, newFormat, (short) intValue);		    } else {			sprintf(dst, newFormat, intValue);		    }		    break;		}		case CHAR_VALUE: {		    char *ptr;		    char padChar = (gotZero ? '0' : ' ');		    ptr = dst;		    if (!gotMinus) {			for ( ; --width > 0; ptr++) {			    *ptr = padChar;			}		    }		    ptr += Tcl_UniCharToUtf(intValue, ptr);		    for ( ; --width > 0; ptr++) {			*ptr = padChar;		    }		    *ptr = '\0';		    break;		}		case STRING_VALUE: {		    char *ptr;		    char padChar = (gotZero ? '0' : ' ');		    int pad;		    ptr = dst;		    if (width > stringLen) {			pad = width - stringLen;		    } else {			pad = 0;		    }		    if (!gotMinus) {			while (pad > 0) {			    *ptr++ = padChar;			    pad--;			}		    }		    size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue; 		    if (size) {			memcpy(ptr, ptrValue, (size_t) size);			ptr += size;		    }		    while (pad > 0) {			*ptr++ = padChar;			pad--;		    }		    *ptr = '\0';		    break;		}		default: {		    sprintf(dst, newFormat, ptrValue);		    break;		}	    }	    Tcl_AppendToObj(resultPtr, dst, -1);	}    }    Tcl_SetObjResult(interp, resultPtr);    if(dst != staticBuf) {        ckfree(dst);    }    return TCL_OK;    mixedXPG:    Tcl_SetResult(interp,             "cannot mix \"%\" and \"%n$\" conversion specifiers", TCL_STATIC);    goto fmtError;    badIndex:    if (gotXpg) {        Tcl_SetResult(interp,                 "\"%n$\" argument index out of range", TCL_STATIC);    } else {        Tcl_SetResult(interp,                 "not enough arguments for all format specifiers", TCL_STATIC);    }    fmtError:    if(dst != staticBuf) {        ckfree(dst);    }    Tcl_DecrRefCount(resultPtr);    return TCL_ERROR;}

⌨️ 快捷键说明

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