tclcmdmz.c

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

C
2,428
字号
			    "\": must be ok, error, return, break, ",			    "continue, or an integer", (char *) NULL);		    return result;		}	    }	} else if (strcmp(option, "-errorinfo") == 0) {	    iPtr->errorInfo =		(char *) ckalloc((unsigned) (strlen(arg) + 1));	    strcpy(iPtr->errorInfo, arg);	} else if (strcmp(option, "-errorcode") == 0) {	    iPtr->errorCode =		(char *) ckalloc((unsigned) (strlen(arg) + 1));	    strcpy(iPtr->errorCode, arg);	} else {	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),		    "bad option \"", option,		    "\": must be -code, -errorcode, or -errorinfo",		    (char *) NULL);	    return TCL_ERROR;	}    }        if (objc == 1) {	/*	 * Set the interpreter's object result. An inline version of	 * Tcl_SetObjResult.	 */	Tcl_SetObjResult(interp, objv[0]);    }    iPtr->returnCode = code;    return TCL_RETURN;}/* *---------------------------------------------------------------------- * * Tcl_SourceObjCmd -- * *	This procedure is invoked to process the "source" Tcl command. *	See the user documentation for details on what it does. * * Results: *	A standard Tcl object result. * * Side effects: *	See the user documentation. * *---------------------------------------------------------------------- */	/* ARGSUSED */intTcl_SourceObjCmd(dummy, interp, objc, objv)    ClientData dummy;		/* Not used. */    Tcl_Interp *interp;		/* Current interpreter. */    int objc;			/* Number of arguments. */    Tcl_Obj *CONST objv[];	/* Argument objects. */{    if (objc != 2) {	Tcl_WrongNumArgs(interp, 1, objv, "fileName");	return TCL_ERROR;    }    return Tcl_FSEvalFile(interp, objv[1]);}/* *---------------------------------------------------------------------- * * Tcl_SplitObjCmd -- * *	This procedure is invoked to process the "split" 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_SplitObjCmd(dummy, interp, objc, objv)    ClientData dummy;		/* Not used. */    Tcl_Interp *interp;		/* Current interpreter. */    int objc;			/* Number of arguments. */    Tcl_Obj *CONST objv[];	/* Argument objects. */{    Tcl_UniChar ch;    int len;    char *splitChars, *string, *end;    int splitCharLen, stringLen;    Tcl_Obj *listPtr, *objPtr;    if (objc == 2) {	splitChars = " \n\t\r";	splitCharLen = 4;    } else if (objc == 3) {	splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);    } else {	Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");	return TCL_ERROR;    }    string = Tcl_GetStringFromObj(objv[1], &stringLen);    end = string + stringLen;    listPtr = Tcl_GetObjResult(interp);        if (stringLen == 0) {	/*	 * Do nothing.	 */    } else if (splitCharLen == 0) {	Tcl_HashTable charReuseTable;	Tcl_HashEntry *hPtr;	int isNew;	/*	 * Handle the special case of splitting on every character.	 *	 * Uses a hash table to ensure that each kind of character has	 * only one Tcl_Obj instance (multiply-referenced) in the	 * final list.  This is a *major* win when splitting on a long	 * string (especially in the megabyte range!) - DKF	 */	Tcl_InitHashTable(&charReuseTable, TCL_ONE_WORD_KEYS);	for ( ; string < end; string += len) {	    len = TclUtfToUniChar(string, &ch);	    /* Assume Tcl_UniChar is an integral type... */	    hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0 + ch, &isNew);	    if (isNew) {		objPtr = Tcl_NewStringObj(string, len);		/* Don't need to fiddle with refcount... */		Tcl_SetHashValue(hPtr, (ClientData) objPtr);	    } else {		objPtr = (Tcl_Obj*) Tcl_GetHashValue(hPtr);	    }	    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);	}	Tcl_DeleteHashTable(&charReuseTable);    } else if (splitCharLen == 1) {	char *p;	/*	 * Handle the special case of splitting on a single character.	 * This is only true for the one-char ASCII case, as one unicode	 * char is > 1 byte in length.	 */	while (*string && (p = strchr(string, (int) *splitChars)) != NULL) {	    objPtr = Tcl_NewStringObj(string, p - string);	    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);	    string = p + 1;	}	objPtr = Tcl_NewStringObj(string, end - string);	Tcl_ListObjAppendElement(NULL, listPtr, objPtr);    } else {	char *element, *p, *splitEnd;	int splitLen;	Tcl_UniChar splitChar;		/*	 * Normal case: split on any of a given set of characters.	 * Discard instances of the split characters.	 */	splitEnd = splitChars + splitCharLen;	for (element = string; string < end; string += len) {	    len = TclUtfToUniChar(string, &ch);	    for (p = splitChars; p < splitEnd; p += splitLen) {		splitLen = TclUtfToUniChar(p, &splitChar);		if (ch == splitChar) {		    objPtr = Tcl_NewStringObj(element, string - element);		    Tcl_ListObjAppendElement(NULL, listPtr, objPtr);		    element = string + len;		    break;		}	    }	}	objPtr = Tcl_NewStringObj(element, string - element);	Tcl_ListObjAppendElement(NULL, listPtr, objPtr);    }    return TCL_OK;}/* *---------------------------------------------------------------------- * * Tcl_StringObjCmd -- * *	This procedure is invoked to process the "string" Tcl command. *	See the user documentation for details on what it does.  Note *	that this command only functions correctly on properly formed *	Tcl UTF strings. * *	Note that the primary methods here (equal, compare, match, ...) *	have bytecode equivalents.  You will find the code for those in *	tclExecute.c.  The code here will only be used in the non-bc *	case (like in an 'eval'). * * Results: *	A standard Tcl result. * * Side effects: *	See the user documentation. * *---------------------------------------------------------------------- */	/* ARGSUSED */intTcl_StringObjCmd(dummy, interp, objc, objv)    ClientData dummy;		/* Not used. */    Tcl_Interp *interp;		/* Current interpreter. */    int objc;			/* Number of arguments. */    Tcl_Obj *CONST objv[];	/* Argument objects. */{    int index, left, right;    Tcl_Obj *resultPtr;    char *string1, *string2;    int length1, length2;    static CONST char *options[] = {	"bytelength",	"compare",	"equal",	"first",	"index",	"is",		"last",		"length",	"map",		"match",	"range",	"repeat",	"replace",	"tolower",	"toupper",	"totitle",	"trim",		"trimleft",	"trimright",	"wordend",	"wordstart",	(char *) NULL    };    enum options {	STR_BYTELENGTH,	STR_COMPARE,	STR_EQUAL,	STR_FIRST,	STR_INDEX,	STR_IS,		STR_LAST,	STR_LENGTH,	STR_MAP,	STR_MATCH,	STR_RANGE,	STR_REPEAT,	STR_REPLACE,	STR_TOLOWER,	STR_TOUPPER,	STR_TOTITLE,	STR_TRIM,	STR_TRIMLEFT,	STR_TRIMRIGHT,	STR_WORDEND,	STR_WORDSTART    };	      if (objc < 2) {        Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");	return TCL_ERROR;    }        if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,	    &index) != TCL_OK) {	return TCL_ERROR;    }    resultPtr = Tcl_GetObjResult(interp);    switch ((enum options) index) {	case STR_EQUAL:	case STR_COMPARE: {	    /*	     * Remember to keep code here in some sync with the	     * byte-compiled versions in tclExecute.c (INST_STR_EQ,	     * INST_STR_NEQ and INST_STR_CMP as well as the expr string	     * comparison in INST_EQ/INST_NEQ/INST_LT/...).	     */	    int i, match, length, nocase = 0, reqlength = -1;	    int (*strCmpFn)();	    if (objc < 4 || objc > 7) {	    str_cmp_args:	        Tcl_WrongNumArgs(interp, 2, objv,				 "?-nocase? ?-length int? string1 string2");		return TCL_ERROR;	    }	    for (i = 2; i < objc-2; i++) {		string2 = Tcl_GetStringFromObj(objv[i], &length2);		if ((length2 > 1)			&& strncmp(string2, "-nocase", (size_t)length2) == 0) {		    nocase = 1;		} else if ((length2 > 1)			&& strncmp(string2, "-length", (size_t)length2) == 0) {		    if (i+1 >= objc-2) {			goto str_cmp_args;		    }		    if (Tcl_GetIntFromObj(interp, objv[++i],			    &reqlength) != TCL_OK) {			return TCL_ERROR;		    }		} else {		    Tcl_AppendStringsToObj(resultPtr, "bad option \"",			    string2, "\": must be -nocase or -length",			    (char *) NULL);		    return TCL_ERROR;		}	    }	    /*	     * From now on, we only access the two objects at the end	     * of the argument array.	     */	    objv += objc-2;	    if ((reqlength == 0) || (objv[0] == objv[1])) {		/*		 * Alway match at 0 chars of if it is the same obj.		 */		Tcl_SetBooleanObj(resultPtr,			((enum options) index == STR_EQUAL));		break;	    } else if (!nocase && objv[0]->typePtr == &tclByteArrayType &&		    objv[1]->typePtr == &tclByteArrayType) {		/*		 * Use binary versions of comparisons since that won't		 * cause undue type conversions and it is much faster.		 * Only do this if we're case-sensitive (which is all		 * that really makes sense with byte arrays anyway, and		 * we have no memcasecmp() for some reason... :^)		 */		string1 = (char*) Tcl_GetByteArrayFromObj(objv[0], &length1);		string2 = (char*) Tcl_GetByteArrayFromObj(objv[1], &length2);		strCmpFn = memcmp;	    } else if ((objv[0]->typePtr == &tclStringType)		    && (objv[1]->typePtr == &tclStringType)) {		/*		 * Do a unicode-specific comparison if both of the args		 * are of String type.  In benchmark testing this proved		 * the most efficient check between the unicode and		 * string comparison operations.		 */		string1 = (char*) Tcl_GetUnicodeFromObj(objv[0], &length1);		string2 = (char*) Tcl_GetUnicodeFromObj(objv[1], &length2);		strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;	    } else {		/*		 * As a catch-all we will work with UTF-8.  We cannot use		 * memcmp() as that is unsafe with any string containing		 * NULL (\xC0\x80 in Tcl's utf rep).  We can use the more		 * efficient TclpUtfNcmp2 if we are case-sensitive and no		 * specific length was requested.		 */		string1 = (char*) Tcl_GetStringFromObj(objv[0], &length1);		string2 = (char*) Tcl_GetStringFromObj(objv[1], &length2);		if ((reqlength < 0) && !nocase) {		    strCmpFn = TclpUtfNcmp2;		} else {		    length1 = Tcl_NumUtfChars(string1, length1);		    length2 = Tcl_NumUtfChars(string2, length2);		    strCmpFn = nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp;		}	    }	    if (((enum options) index == STR_EQUAL)		    && (reqlength < 0) && (length1 != length2)) {		match = 1; /* this will be reversed below */	    } else {		length = (length1 < length2) ? length1 : length2;		if (reqlength > 0 && reqlength < length) {		    length = reqlength;		} else if (reqlength < 0) {		    /*		     * The requested length is negative, so we ignore it by		     * setting it to length + 1 so we correct the match var.		     */		    reqlength = length + 1;		}		match = strCmpFn(string1, string2, (unsigned) length);		if ((match == 0) && (reqlength > length)) {		    match = length1 - length2;		}	    }	    if ((enum options) index == STR_EQUAL) {		Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);	    } else {		Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :					  (match < 0) ? -1 : 0));	    }	    break;	}	case STR_FIRST: {	    Tcl_UniChar *ustring1, *ustring2;	    int match, start;	    if (objc < 4 || objc > 5) {	        Tcl_WrongNumArgs(interp, 2, objv,				 "subString string ?startIndex?");		return TCL_ERROR;	    }	    /*	     * We are searching string2 for the sequence string1.	     */	    match = -1;	    start = 0;	    length2 = -1;	    ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);	    ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);	    if (objc == 5) {		/*		 * If a startIndex is specified, we will need to fast		 * forward to that point in the string before we think		 * about a match		 */		if (TclGetIntForIndex(interp, objv[4], length2 - 1,			&start) != TCL_OK) {		    return TCL_ERROR;		}		if (start >= length2) {		    goto str_first_done;		} else if (start > 0) {		    ustring2 += start;		    length2  -= start;		} else if (start < 0) {		    /*		     * Invalid start index mapped to string start;		     * Bug #423581		     */		    start = 0;		}	    }	    if (length1 > 0) {		register Tcl_UniChar *p, *end;		end = ustring2 + length2 - length1 + 1;		for (p = ustring2;  p < end;  p++) {		    /*		     * Scan forward to find the first character.		     */		    if ((*p == *ustring1) &&			    (TclUniCharNcmp(ustring1, p,				    (unsigned long) length1) == 0)) {			match = p - ustring2;			break;		    }		}	    }	    /*	     * Compute the character index of the matching string by	     * counting the number of characters before the match.	     */	    if ((match != -1) && (objc == 5)) {		match += start;	    }	    str_first_done:	    Tcl_SetIntObj(resultPtr, match);	    break;	}	case STR_INDEX: {	    if (objc != 4) {	        Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");		return TCL_ERROR;	    }	    /*	     * If we have a ByteArray object, avoid indexing in the	     * Utf string since the byte array contains one byte per	     * character.  Otherwise, use the Unicode string rep to	     * get the index'th char.	     */	    if (objv[2]->typePtr == &tclByteArrayType) {		string1 = (char *) Tcl_GetByteArrayFromObj(objv[2], &length1);		if (TclGetIntForIndex(interp, objv[3], length1 - 1,			&index) != TCL_OK) {		    return TCL_ERROR;		}		if ((index >= 0) && (index < length1)) {		    Tcl_SetByteArrayObj(resultPtr,			    (unsigned char *)(&string1[index]), 1);		}	    } else {		/*		 * Get Unicode char length to calulate what 'end' means.		 */		length1 = Tcl_GetCharLength(objv[2]);		if (TclGetIntForIndex(interp, objv[3], length1 - 1,			&index) != TCL_OK) {		    return TCL_ERROR;

⌨️ 快捷键说明

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