4 * This file contains the top-level command routines for most of
5 * the Tcl built-in commands whose names begin with the letters
8 * Copyright (c) 1987-1993 The Regents of the University of California.
9 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 typedef int (StatProc)_ANSI_ARGS_((CONST char *path, struct stat *buf));
24 * Prototypes for local procedures defined in this file:
27 static int CheckAccess _ANSI_ARGS_((Tcl_Interp *interp,
28 Tcl_Obj *objPtr, int mode));
29 static int GetStatBuf _ANSI_ARGS_((Tcl_Interp *interp,
30 Tcl_Obj *objPtr, StatProc *statProc,
31 struct stat *statPtr));
32 static char * GetTypeFromMode _ANSI_ARGS_((int mode));
33 static int SplitPath _ANSI_ARGS_((Tcl_Interp *interp,
34 Tcl_Obj *objPtr, int *argcPtr, char ***argvPtr));
35 static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
36 char *varName, struct stat *statPtr));
37 static char ** StringifyObjects _ANSI_ARGS_((int objc,
38 Tcl_Obj *CONST objv[]));
41 *----------------------------------------------------------------------
45 * This procedure is invoked to process the "break" Tcl command.
46 * See the user documentation for details on what it does.
48 * With the bytecode compiler, this procedure is only called when
49 * a command name is computed at runtime, and is "break" or the name
50 * to which "break" was renamed: e.g., "set z break; $z"
53 * A standard Tcl result.
56 * See the user documentation.
58 *----------------------------------------------------------------------
63 Tcl_BreakObjCmd(dummy, interp, objc, objv)
64 ClientData dummy; /* Not used. */
65 Tcl_Interp *interp; /* Current interpreter. */
66 int objc; /* Number of arguments. */
67 Tcl_Obj *CONST objv[]; /* Argument objects. */
70 Tcl_WrongNumArgs(interp, 1, objv, NULL);
77 *----------------------------------------------------------------------
81 * This procedure is invoked to process the "case" Tcl command.
82 * See the user documentation for details on what it does.
85 * A standard Tcl object result.
88 * See the user documentation.
90 *----------------------------------------------------------------------
95 Tcl_CaseObjCmd(dummy, interp, objc, objv)
96 ClientData dummy; /* Not used. */
97 Tcl_Interp *interp; /* Current interpreter. */
98 int objc; /* Number of arguments. */
99 Tcl_Obj *CONST objv[]; /* Argument objects. */
105 Tcl_Obj *CONST *caseObjv;
109 Tcl_WrongNumArgs(interp, 1, objv,
110 "string ?in? patList body ... ?default body?");
114 string = Tcl_GetString(objv[1]);
117 arg = Tcl_GetString(objv[2]);
118 if (strcmp(arg, "in") == 0) {
127 * If all of the pattern/command pairs are lumped into a single
128 * argument, split them out again.
134 Tcl_ListObjGetElements(interp, caseObjv[0], &caseObjc, &newObjv);
138 for (i = 0; i < caseObjc; i += 2) {
144 if (i == (caseObjc - 1)) {
145 Tcl_ResetResult(interp);
146 Tcl_AppendToObj(Tcl_GetObjResult(interp),
147 "extra case pattern with no body", -1);
152 * Check for special case of single pattern (no list) with
153 * no backslash sequences.
156 pat = Tcl_GetString(caseObjv[i]);
157 for (p = (unsigned char *) pat; *p != '\0'; p++) {
158 if (isspace(*p) || (*p == '\\')) { /* INTL: ISO space, UCHAR */
163 if ((*pat == 'd') && (strcmp(pat, "default") == 0)) {
166 if (Tcl_StringMatch(string, pat)) {
175 * Break up pattern lists, then check each of the patterns
179 result = Tcl_SplitList(interp, pat, &patObjc, &patObjv);
180 if (result != TCL_OK) {
183 for (j = 0; j < patObjc; j++) {
184 if (Tcl_StringMatch(string, patObjv[j])) {
189 ckfree((char *) patObjv);
197 armPtr = caseObjv[body - 1];
198 result = Tcl_EvalObjEx(interp, caseObjv[body], 0);
199 if (result == TCL_ERROR) {
200 char msg[100 + TCL_INTEGER_SPACE];
202 arg = Tcl_GetString(armPtr);
204 "\n (\"%.50s\" arm line %d)", arg,
206 Tcl_AddObjErrorInfo(interp, msg, -1);
212 * Nothing matched: return nothing.
219 *----------------------------------------------------------------------
223 * This object-based procedure is invoked to process the "catch" Tcl
224 * command. See the user documentation for details on what it does.
227 * A standard Tcl object result.
230 * See the user documentation.
232 *----------------------------------------------------------------------
237 Tcl_CatchObjCmd(dummy, interp, objc, objv)
238 ClientData dummy; /* Not used. */
239 Tcl_Interp *interp; /* Current interpreter. */
240 int objc; /* Number of arguments. */
241 Tcl_Obj *CONST objv[]; /* Argument objects. */
243 Tcl_Obj *varNamePtr = NULL;
246 if ((objc != 2) && (objc != 3)) {
247 Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?");
252 * Save a pointer to the variable name object, if any, in case the
253 * Tcl_EvalObj reallocates the bytecode interpreter's evaluation
254 * stack rendering objv invalid.
258 varNamePtr = objv[2];
261 result = Tcl_EvalObjEx(interp, objv[1], 0);
264 if (Tcl_ObjSetVar2(interp, varNamePtr, NULL,
265 Tcl_GetObjResult(interp), 0) == NULL) {
266 Tcl_ResetResult(interp);
267 Tcl_AppendToObj(Tcl_GetObjResult(interp),
268 "couldn't save command result in variable", -1);
274 * Set the interpreter's object result to an integer object holding the
275 * integer Tcl_EvalObj result. Note that we don't bother generating a
276 * string representation. We reset the interpreter's object result
277 * to an unshared empty object and then set it to be an integer object.
280 Tcl_ResetResult(interp);
281 Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
286 *----------------------------------------------------------------------
290 * This procedure is invoked to process the "cd" Tcl command.
291 * See the user documentation for details on what it does.
294 * A standard Tcl result.
297 * See the user documentation.
299 *----------------------------------------------------------------------
304 Tcl_CdObjCmd(dummy, interp, objc, objv)
305 ClientData dummy; /* Not used. */
306 Tcl_Interp *interp; /* Current interpreter. */
307 int objc; /* Number of arguments. */
308 Tcl_Obj *CONST objv[]; /* Argument objects. */
315 Tcl_WrongNumArgs(interp, 1, objv, "?dirName?");
320 dirName = Tcl_GetString(objv[1]);
324 if (Tcl_TranslateFileName(interp, dirName, &ds) == NULL) {
328 result = Tcl_Chdir(Tcl_DStringValue(&ds));
329 Tcl_DStringFree(&ds);
332 Tcl_AppendResult(interp, "couldn't change working directory to \"",
333 dirName, "\": ", Tcl_PosixError(interp), (char *) NULL);
340 *----------------------------------------------------------------------
342 * Tcl_ConcatObjCmd --
344 * This object-based procedure is invoked to process the "concat" Tcl
345 * command. See the user documentation for details on what it does.
348 * A standard Tcl object result.
351 * See the user documentation.
353 *----------------------------------------------------------------------
358 Tcl_ConcatObjCmd(dummy, interp, objc, objv)
359 ClientData dummy; /* Not used. */
360 Tcl_Interp *interp; /* Current interpreter. */
361 int objc; /* Number of arguments. */
362 Tcl_Obj *CONST objv[]; /* Argument objects. */
365 Tcl_SetObjResult(interp, Tcl_ConcatObj(objc-1, objv+1));
371 *----------------------------------------------------------------------
373 * Tcl_ContinueObjCmd -
375 * This procedure is invoked to process the "continue" Tcl command.
376 * See the user documentation for details on what it does.
378 * With the bytecode compiler, this procedure is only called when
379 * a command name is computed at runtime, and is "continue" or the name
380 * to which "continue" was renamed: e.g., "set z continue; $z"
383 * A standard Tcl result.
386 * See the user documentation.
388 *----------------------------------------------------------------------
393 Tcl_ContinueObjCmd(dummy, interp, objc, objv)
394 ClientData dummy; /* Not used. */
395 Tcl_Interp *interp; /* Current interpreter. */
396 int objc; /* Number of arguments. */
397 Tcl_Obj *CONST objv[]; /* Argument objects. */
400 Tcl_WrongNumArgs(interp, 1, objv, NULL);
407 *----------------------------------------------------------------------
409 * Tcl_EncodingObjCmd --
411 * This command manipulates encodings.
414 * A standard Tcl result.
417 * See the user documentation.
419 *----------------------------------------------------------------------
423 Tcl_EncodingObjCmd(dummy, interp, objc, objv)
424 ClientData dummy; /* Not used. */
425 Tcl_Interp *interp; /* Current interpreter. */
426 int objc; /* Number of arguments. */
427 Tcl_Obj *CONST objv[]; /* Argument objects. */
430 Tcl_Encoding encoding;
435 static char *optionStrings[] = {
436 "convertfrom", "convertto", "names", "system",
440 ENC_CONVERTFROM, ENC_CONVERTTO, ENC_NAMES, ENC_SYSTEM
444 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
447 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
452 switch ((enum options) index) {
454 case ENC_CONVERTFROM: {
460 } else if (objc == 4) {
461 name = Tcl_GetString(objv[2]);
464 Tcl_WrongNumArgs(interp, 2, objv, "?encoding? data");
468 encoding = Tcl_GetEncoding(interp, name);
473 if ((enum options) index == ENC_CONVERTFROM) {
475 * Treat the string as binary data.
478 string = (char *) Tcl_GetByteArrayFromObj(data, &length);
479 Tcl_ExternalToUtfDString(encoding, string, length, &ds);
482 * Note that we cannot use Tcl_DStringResult here because
483 * it will truncate the string at the first null byte.
486 Tcl_SetStringObj(Tcl_GetObjResult(interp),
487 Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
488 Tcl_DStringFree(&ds);
491 * Store the result as binary data.
494 string = Tcl_GetStringFromObj(data, &length);
495 Tcl_UtfToExternalDString(encoding, string, length, &ds);
496 resultPtr = Tcl_GetObjResult(interp);
497 Tcl_SetByteArrayObj(resultPtr,
498 (unsigned char *) Tcl_DStringValue(&ds),
499 Tcl_DStringLength(&ds));
500 Tcl_DStringFree(&ds);
503 Tcl_FreeEncoding(encoding);
508 Tcl_WrongNumArgs(interp, 2, objv, NULL);
511 Tcl_GetEncodingNames(interp);
516 Tcl_WrongNumArgs(interp, 2, objv, "?encoding?");
520 Tcl_SetResult(interp, Tcl_GetEncodingName(NULL), TCL_STATIC);
522 return Tcl_SetSystemEncoding(interp,
523 Tcl_GetStringFromObj(objv[2], NULL));
532 *----------------------------------------------------------------------
536 * This procedure is invoked to process the "error" Tcl command.
537 * See the user documentation for details on what it does.
540 * A standard Tcl object result.
543 * See the user documentation.
545 *----------------------------------------------------------------------
550 Tcl_ErrorObjCmd(dummy, interp, objc, objv)
551 ClientData dummy; /* Not used. */
552 Tcl_Interp *interp; /* Current interpreter. */
553 int objc; /* Number of arguments. */
554 Tcl_Obj *CONST objv[]; /* Argument objects. */
556 Interp *iPtr = (Interp *) interp;
560 if ((objc < 2) || (objc > 4)) {
561 Tcl_WrongNumArgs(interp, 1, objv, "message ?errorInfo? ?errorCode?");
565 if (objc >= 3) { /* process the optional info argument */
566 info = Tcl_GetStringFromObj(objv[2], &infoLen);
568 Tcl_AddObjErrorInfo(interp, info, infoLen);
569 iPtr->flags |= ERR_ALREADY_LOGGED;
574 Tcl_SetVar2Ex(interp, "errorCode", NULL, objv[3], TCL_GLOBAL_ONLY);
575 iPtr->flags |= ERROR_CODE_SET;
578 Tcl_SetObjResult(interp, objv[1]);
583 *----------------------------------------------------------------------
587 * This object-based procedure is invoked to process the "eval" Tcl
588 * command. See the user documentation for details on what it does.
591 * A standard Tcl object result.
594 * See the user documentation.
596 *----------------------------------------------------------------------
601 Tcl_EvalObjCmd(dummy, interp, objc, objv)
602 ClientData dummy; /* Not used. */
603 Tcl_Interp *interp; /* Current interpreter. */
604 int objc; /* Number of arguments. */
605 Tcl_Obj *CONST objv[]; /* Argument objects. */
608 register Tcl_Obj *objPtr;
611 Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
616 result = Tcl_EvalObjEx(interp, objv[1], TCL_EVAL_DIRECT);
619 * More than one argument: concatenate them together with spaces
620 * between, then evaluate the result. Tcl_EvalObjEx will delete
621 * the object when it decrements its refcount after eval'ing it.
623 objPtr = Tcl_ConcatObj(objc-1, objv+1);
624 result = Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_DIRECT);
626 if (result == TCL_ERROR) {
627 char msg[32 + TCL_INTEGER_SPACE];
629 sprintf(msg, "\n (\"eval\" body line %d)", interp->errorLine);
630 Tcl_AddObjErrorInfo(interp, msg, -1);
636 *----------------------------------------------------------------------
640 * This procedure is invoked to process the "exit" Tcl command.
641 * See the user documentation for details on what it does.
644 * A standard Tcl object result.
647 * See the user documentation.
649 *----------------------------------------------------------------------
654 Tcl_ExitObjCmd(dummy, interp, objc, objv)
655 ClientData dummy; /* Not used. */
656 Tcl_Interp *interp; /* Current interpreter. */
657 int objc; /* Number of arguments. */
658 Tcl_Obj *CONST objv[]; /* Argument objects. */
662 if ((objc != 1) && (objc != 2)) {
663 Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?");
669 } else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) {
674 return TCL_OK; /* Better not ever reach this! */
678 *----------------------------------------------------------------------
682 * This object-based procedure is invoked to process the "expr" Tcl
683 * command. See the user documentation for details on what it does.
685 * With the bytecode compiler, this procedure is called in two
686 * circumstances: 1) to execute expr commands that are too complicated
687 * or too unsafe to try compiling directly into an inline sequence of
688 * instructions, and 2) to execute commands where the command name is
689 * computed at runtime and is "expr" or the name to which "expr" was
690 * renamed (e.g., "set z expr; $z 2+3")
693 * A standard Tcl object result.
696 * See the user documentation.
698 *----------------------------------------------------------------------
703 Tcl_ExprObjCmd(dummy, interp, objc, objv)
704 ClientData dummy; /* Not used. */
705 Tcl_Interp *interp; /* Current interpreter. */
706 int objc; /* Number of arguments. */
707 Tcl_Obj *CONST objv[]; /* Argument objects. */
709 register Tcl_Obj *objPtr;
711 register char *bytes;
712 int length, i, result;
715 Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
720 result = Tcl_ExprObj(interp, objv[1], &resultPtr);
721 if (result == TCL_OK) {
722 Tcl_SetObjResult(interp, resultPtr);
723 Tcl_DecrRefCount(resultPtr); /* done with the result object */
729 * Create a new object holding the concatenated argument strings.
732 bytes = Tcl_GetStringFromObj(objv[1], &length);
733 objPtr = Tcl_NewStringObj(bytes, length);
734 Tcl_IncrRefCount(objPtr);
735 for (i = 2; i < objc; i++) {
736 Tcl_AppendToObj(objPtr, " ", 1);
737 bytes = Tcl_GetStringFromObj(objv[i], &length);
738 Tcl_AppendToObj(objPtr, bytes, length);
742 * Evaluate the concatenated string object.
745 result = Tcl_ExprObj(interp, objPtr, &resultPtr);
746 if (result == TCL_OK) {
747 Tcl_SetObjResult(interp, resultPtr);
748 Tcl_DecrRefCount(resultPtr); /* done with the result object */
752 * Free allocated resources.
755 Tcl_DecrRefCount(objPtr);
760 *----------------------------------------------------------------------
764 * This procedure is invoked to process the "file" Tcl command.
765 * See the user documentation for details on what it does.
766 * PLEASE NOTE THAT THIS FAILS WITH FILENAMES AND PATHS WITH
767 * EMBEDDED NULLS, WHICH COULD THEORETICALLY HAPPEN ON A MAC.
770 * A standard Tcl result.
773 * See the user documentation.
775 *----------------------------------------------------------------------
780 Tcl_FileObjCmd(dummy, interp, objc, objv)
781 ClientData dummy; /* Not used. */
782 Tcl_Interp *interp; /* Current interpreter. */
783 int objc; /* Number of arguments. */
784 Tcl_Obj *CONST objv[]; /* Argument objects. */
790 * This list of constants should match the fileOption string array below.
793 static char *fileOptions[] = {
794 "atime", "attributes", "channels", "copy",
796 "dirname", "executable", "exists", "extension",
797 "isdirectory", "isfile", "join", "lstat",
798 "mtime", "mkdir", "nativename", "owned",
799 "pathtype", "readable", "readlink", "rename",
800 "rootname", "size", "split", "stat",
801 "tail", "type", "volumes", "writable",
805 FILE_ATIME, FILE_ATTRIBUTES, FILE_CHANNELS, FILE_COPY,
807 FILE_DIRNAME, FILE_EXECUTABLE, FILE_EXISTS, FILE_EXTENSION,
808 FILE_ISDIRECTORY, FILE_ISFILE, FILE_JOIN, FILE_LSTAT,
809 FILE_MTIME, FILE_MKDIR, FILE_NATIVENAME, FILE_OWNED,
810 FILE_PATHTYPE, FILE_READABLE, FILE_READLINK, FILE_RENAME,
811 FILE_ROOTNAME, FILE_SIZE, FILE_SPLIT, FILE_STAT,
812 FILE_TAIL, FILE_TYPE, FILE_VOLUMES, FILE_WRITABLE
816 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
819 if (Tcl_GetIndexFromObj(interp, objv[1], fileOptions, "option", 0,
824 resultPtr = Tcl_GetObjResult(interp);
825 switch ((enum options) index) {
831 if ((objc < 3) || (objc > 4)) {
832 Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
835 if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
839 if (Tcl_GetLongFromObj(interp, objv[3],
840 (long*)(&buf.st_atime)) != TCL_OK) {
843 tval.actime = buf.st_atime;
844 tval.modtime = buf.st_mtime;
845 fileName = Tcl_GetString(objv[2]);
846 if (utime(fileName, &tval) != 0) {
847 Tcl_AppendStringsToObj(resultPtr,
848 "could not set access time for file \"",
850 Tcl_PosixError(interp), (char *) NULL);
854 * Do another stat to ensure that the we return the
855 * new recognized atime - hopefully the same as the
856 * one we sent in. However, fs's like FAT don't
857 * even know what atime is.
859 if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
863 Tcl_SetLongObj(resultPtr, (long) buf.st_atime);
866 case FILE_ATTRIBUTES: {
867 return TclFileAttrsCmd(interp, objc, objv);
869 case FILE_CHANNELS: {
870 if ((objc < 2) || (objc > 3)) {
871 Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
874 return Tcl_GetChannelNamesEx(interp,
875 ((objc == 2) ? NULL : Tcl_GetString(objv[2])));
881 argv = StringifyObjects(objc, objv);
882 result = TclFileCopyCmd(interp, objc, argv);
883 ckfree((char *) argv);
890 argv = StringifyObjects(objc, objv);
891 result = TclFileDeleteCmd(interp, objc, argv);
892 ckfree((char *) argv);
902 if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) {
907 * Return all but the last component. If there is only one
908 * component, return it if the path was non-relative, otherwise
909 * return the current directory.
915 Tcl_DStringInit(&ds);
916 Tcl_JoinPath(argc - 1, argv, &ds);
917 Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds),
918 Tcl_DStringLength(&ds));
919 Tcl_DStringFree(&ds);
920 } else if ((argc == 0)
921 || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) {
922 Tcl_SetStringObj(resultPtr,
923 ((tclPlatform == TCL_PLATFORM_MAC) ? ":" : "."), 1);
925 Tcl_SetStringObj(resultPtr, argv[0], -1);
927 ckfree((char *) argv);
930 case FILE_EXECUTABLE: {
934 return CheckAccess(interp, objv[2], X_OK);
940 return CheckAccess(interp, objv[2], F_OK);
942 case FILE_EXTENSION: {
943 char *fileName, *extension;
947 fileName = Tcl_GetString(objv[2]);
948 extension = TclGetExtension(fileName);
949 if (extension != NULL) {
950 Tcl_SetStringObj(resultPtr, extension, -1);
954 case FILE_ISDIRECTORY: {
962 if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) {
963 value = S_ISDIR(buf.st_mode);
965 Tcl_SetBooleanObj(resultPtr, value);
976 if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) {
977 value = S_ISREG(buf.st_mode);
979 Tcl_SetBooleanObj(resultPtr, value);
987 Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
990 argv = StringifyObjects(objc - 2, objv + 2);
991 Tcl_DStringInit(&ds);
992 Tcl_JoinPath(objc - 2, argv, &ds);
993 Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds),
994 Tcl_DStringLength(&ds));
995 Tcl_DStringFree(&ds);
996 ckfree((char *) argv);
1004 Tcl_WrongNumArgs(interp, 2, objv, "name varName");
1007 if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) {
1010 varName = Tcl_GetString(objv[3]);
1011 return StoreStatData(interp, varName, &buf);
1016 struct utimbuf tval;
1018 if ((objc < 3) || (objc > 4)) {
1019 Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
1022 if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
1026 if (Tcl_GetLongFromObj(interp, objv[3],
1027 (long*)(&buf.st_mtime)) != TCL_OK) {
1030 tval.actime = buf.st_atime;
1031 tval.modtime = buf.st_mtime;
1032 fileName = Tcl_GetString(objv[2]);
1033 if (utime(fileName, &tval) != 0) {
1034 Tcl_AppendStringsToObj(resultPtr,
1035 "could not set modification time for file \"",
1037 Tcl_PosixError(interp), (char *) NULL);
1041 * Do another stat to ensure that the we return the
1042 * new recognized atime - hopefully the same as the
1043 * one we sent in. However, fs's like FAT don't
1044 * even know what atime is.
1046 if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
1050 Tcl_SetLongObj(resultPtr, (long) buf.st_mtime);
1058 Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
1061 argv = StringifyObjects(objc, objv);
1062 result = TclFileMakeDirsCmd(interp, objc, argv);
1063 ckfree((char *) argv);
1066 case FILE_NATIVENAME: {
1073 fileName = Tcl_GetString(objv[2]);
1074 fileName = Tcl_TranslateFileName(interp, fileName, &ds);
1075 if (fileName == NULL) {
1078 Tcl_SetStringObj(resultPtr, fileName, Tcl_DStringLength(&ds));
1079 Tcl_DStringFree(&ds);
1090 if (GetStatBuf(NULL, objv[2], TclStat, &buf) == TCL_OK) {
1092 * For Windows and Macintosh, there are no user ids
1093 * associated with a file, so we always return 1.
1096 #if (defined(__WIN32__) || defined(MAC_TCL))
1099 value = (geteuid() == buf.st_uid);
1102 Tcl_SetBooleanObj(resultPtr, value);
1105 case FILE_PATHTYPE: {
1111 fileName = Tcl_GetString(objv[2]);
1112 switch (Tcl_GetPathType(fileName)) {
1113 case TCL_PATH_ABSOLUTE:
1114 Tcl_SetStringObj(resultPtr, "absolute", -1);
1116 case TCL_PATH_RELATIVE:
1117 Tcl_SetStringObj(resultPtr, "relative", -1);
1119 case TCL_PATH_VOLUME_RELATIVE:
1120 Tcl_SetStringObj(resultPtr, "volumerelative", -1);
1125 case FILE_READABLE: {
1129 return CheckAccess(interp, objv[2], R_OK);
1131 case FILE_READLINK: {
1132 char *fileName, *contents;
1133 Tcl_DString name, link;
1139 fileName = Tcl_GetString(objv[2]);
1140 fileName = Tcl_TranslateFileName(interp, fileName, &name);
1141 if (fileName == NULL) {
1146 * If S_IFLNK isn't defined it means that the machine doesn't
1147 * support symbolic links, so the file can't possibly be a
1148 * symbolic link. Generate an EINVAL error, which is what
1149 * happens on machines that do support symbolic links when
1150 * you invoke readlink on a file that isn't a symbolic link.
1157 contents = TclpReadlink(fileName, &link);
1158 #endif /* S_IFLNK */
1160 Tcl_DStringFree(&name);
1161 if (contents == NULL) {
1162 Tcl_AppendResult(interp, "could not readlink \"",
1163 Tcl_GetString(objv[2]), "\": ",
1164 Tcl_PosixError(interp), (char *) NULL);
1167 Tcl_DStringResult(interp, &link);
1174 argv = StringifyObjects(objc, objv);
1175 result = TclFileRenameCmd(interp, objc, argv);
1176 ckfree((char *) argv);
1179 case FILE_ROOTNAME: {
1181 char *fileName, *extension;
1186 fileName = Tcl_GetStringFromObj(objv[2], &length);
1187 extension = TclGetExtension(fileName);
1188 if (extension == NULL) {
1189 Tcl_SetObjResult(interp, objv[2]);
1191 Tcl_SetStringObj(resultPtr, fileName,
1192 (int) (length - strlen(extension)));
1202 if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
1205 Tcl_SetLongObj(resultPtr, (long) buf.st_size);
1217 fileName = Tcl_GetString(objv[2]);
1218 Tcl_SplitPath(fileName, &argc, &argv);
1219 for (i = 0; i < argc; i++) {
1220 objPtr = Tcl_NewStringObj(argv[i], -1);
1221 Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
1223 ckfree((char *) argv);
1231 Tcl_WrongNumArgs(interp, 1, objv, "stat name varName");
1234 if (GetStatBuf(interp, objv[2], TclStat, &buf) != TCL_OK) {
1237 varName = Tcl_GetString(objv[3]);
1238 return StoreStatData(interp, varName, &buf);
1247 if (SplitPath(interp, objv[2], &argc, &argv) != TCL_OK) {
1252 * Return the last component, unless it is the only component,
1253 * and it is the root of an absolute path.
1258 || (Tcl_GetPathType(argv[0]) == TCL_PATH_RELATIVE)) {
1259 Tcl_SetStringObj(resultPtr, argv[argc - 1], -1);
1262 ckfree((char *) argv);
1271 if (GetStatBuf(interp, objv[2], TclpLstat, &buf) != TCL_OK) {
1274 Tcl_SetStringObj(resultPtr,
1275 GetTypeFromMode((unsigned short) buf.st_mode), -1);
1278 case FILE_VOLUMES: {
1280 Tcl_WrongNumArgs(interp, 2, objv, NULL);
1283 return TclpListVolumes(interp);
1285 case FILE_WRITABLE: {
1289 return CheckAccess(interp, objv[2], W_OK);
1294 Tcl_WrongNumArgs(interp, 2, objv, "name");
1299 *---------------------------------------------------------------------------
1303 * Utility procedure used by Tcl_FileObjCmd() to split a path.
1304 * Differs from standard Tcl_SplitPath in its handling of home
1305 * directories; Tcl_SplitPath preserves the "~" while this
1306 * procedure computes the actual full path name.
1309 * The return value is TCL_OK if the path could be split, TCL_ERROR
1310 * otherwise. If TCL_ERROR was returned, an error message is left
1311 * in interp. If TCL_OK was returned, *argvPtr is set to a newly
1312 * allocated array of strings that represent the individual
1313 * directories in the specified path, and *argcPtr is filled with
1314 * the length of that array.
1317 * Memory allocated. The caller must eventually free this memory
1318 * by calling ckfree() on *argvPtr.
1320 *---------------------------------------------------------------------------
1324 SplitPath(interp, objPtr, argcPtr, argvPtr)
1325 Tcl_Interp *interp; /* Interp for error return. May be NULL. */
1326 Tcl_Obj *objPtr; /* Path to be split. */
1327 int *argcPtr; /* Filled with length of following array. */
1328 char ***argvPtr; /* Filled with array of strings representing
1329 * the elements of the specified path. */
1333 fileName = Tcl_GetString(objPtr);
1336 * If there is only one element, and it starts with a tilde,
1337 * perform tilde substitution and resplit the path.
1340 Tcl_SplitPath(fileName, argcPtr, argvPtr);
1341 if ((*argcPtr == 1) && (fileName[0] == '~')) {
1344 ckfree((char *) *argvPtr);
1345 fileName = Tcl_TranslateFileName(interp, fileName, &ds);
1346 if (fileName == NULL) {
1349 Tcl_SplitPath(fileName, argcPtr, argvPtr);
1350 Tcl_DStringFree(&ds);
1356 *---------------------------------------------------------------------------
1360 * Utility procedure used by Tcl_FileObjCmd() to query file
1361 * attributes available through the access() system call.
1364 * Always returns TCL_OK. Sets interp's result to boolean true or
1365 * false depending on whether the file has the specified attribute.
1370 *---------------------------------------------------------------------------
1374 CheckAccess(interp, objPtr, mode)
1375 Tcl_Interp *interp; /* Interp for status return. Must not be
1377 Tcl_Obj *objPtr; /* Name of file to check. */
1378 int mode; /* Attribute to check; passed as argument to
1385 fileName = Tcl_GetString(objPtr);
1386 fileName = Tcl_TranslateFileName(interp, fileName, &ds);
1387 if (fileName == NULL) {
1390 value = (TclAccess(fileName, mode) == 0);
1391 Tcl_DStringFree(&ds);
1393 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
1399 *---------------------------------------------------------------------------
1403 * Utility procedure used by Tcl_FileObjCmd() to query file
1404 * attributes available through the stat() or lstat() system call.
1407 * The return value is TCL_OK if the specified file exists and can
1408 * be stat'ed, TCL_ERROR otherwise. If TCL_ERROR is returned, an
1409 * error message is left in interp's result. If TCL_OK is returned,
1410 * *statPtr is filled with information about the specified file.
1415 *---------------------------------------------------------------------------
1419 GetStatBuf(interp, objPtr, statProc, statPtr)
1420 Tcl_Interp *interp; /* Interp for error return. May be NULL. */
1421 Tcl_Obj *objPtr; /* Path name to examine. */
1422 StatProc *statProc; /* Either stat() or lstat() depending on
1423 * desired behavior. */
1424 struct stat *statPtr; /* Filled with info about file obtained by
1425 * calling (*statProc)(). */
1431 fileName = Tcl_GetString(objPtr);
1432 fileName = Tcl_TranslateFileName(interp, fileName, &ds);
1433 if (fileName == NULL) {
1437 status = (*statProc)(Tcl_DStringValue(&ds), statPtr);
1438 Tcl_DStringFree(&ds);
1441 if (interp != NULL) {
1442 Tcl_AppendResult(interp, "could not read \"",
1443 Tcl_GetString(objPtr), "\": ",
1444 Tcl_PosixError(interp), (char *) NULL);
1452 *----------------------------------------------------------------------
1456 * This is a utility procedure that breaks out the fields of a
1457 * "stat" structure and stores them in textual form into the
1458 * elements of an associative array.
1461 * Returns a standard Tcl return value. If an error occurs then
1462 * a message is left in interp's result.
1465 * Elements of the associative array given by "varName" are modified.
1467 *----------------------------------------------------------------------
1471 StoreStatData(interp, varName, statPtr)
1472 Tcl_Interp *interp; /* Interpreter for error reports. */
1473 char *varName; /* Name of associative array variable
1474 * in which to store stat results. */
1475 struct stat *statPtr; /* Pointer to buffer containing
1476 * stat data to store in varName. */
1478 char string[TCL_INTEGER_SPACE];
1480 TclFormatInt(string, (long) statPtr->st_dev);
1481 if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG)
1485 TclFormatInt(string, (long) statPtr->st_ino);
1486 if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG)
1490 TclFormatInt(string, (unsigned short) statPtr->st_mode);
1491 if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG)
1495 TclFormatInt(string, (long) statPtr->st_nlink);
1496 if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG)
1500 TclFormatInt(string, (long) statPtr->st_uid);
1501 if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG)
1505 TclFormatInt(string, (long) statPtr->st_gid);
1506 if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG)
1510 sprintf(string, "%lu", (unsigned long) statPtr->st_size);
1511 if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG)
1515 TclFormatInt(string, (long) statPtr->st_atime);
1516 if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG)
1520 TclFormatInt(string, (long) statPtr->st_mtime);
1521 if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG)
1525 TclFormatInt(string, (long) statPtr->st_ctime);
1526 if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG)
1530 if (Tcl_SetVar2(interp, varName, "type",
1531 GetTypeFromMode((unsigned short) statPtr->st_mode),
1532 TCL_LEAVE_ERR_MSG) == NULL) {
1539 *----------------------------------------------------------------------
1541 * GetTypeFromMode --
1543 * Given a mode word, returns a string identifying the type of a
1547 * A static text string giving the file type from mode.
1552 *----------------------------------------------------------------------
1556 GetTypeFromMode(mode)
1559 if (S_ISREG(mode)) {
1561 } else if (S_ISDIR(mode)) {
1563 } else if (S_ISCHR(mode)) {
1564 return "characterSpecial";
1565 } else if (S_ISBLK(mode)) {
1566 return "blockSpecial";
1567 } else if (S_ISFIFO(mode)) {
1570 } else if (S_ISLNK(mode)) {
1574 } else if (S_ISSOCK(mode)) {
1582 *----------------------------------------------------------------------
1586 * This procedure is invoked to process the "for" Tcl command.
1587 * See the user documentation for details on what it does.
1589 * With the bytecode compiler, this procedure is only called when
1590 * a command name is computed at runtime, and is "for" or the name
1591 * to which "for" was renamed: e.g.,
1592 * "set z for; $z {set i 0} {$i<100} {incr i} {puts $i}"
1595 * A standard Tcl result.
1598 * See the user documentation.
1600 *----------------------------------------------------------------------
1605 Tcl_ForObjCmd(dummy, interp, objc, objv)
1606 ClientData dummy; /* Not used. */
1607 Tcl_Interp *interp; /* Current interpreter. */
1608 int objc; /* Number of arguments. */
1609 Tcl_Obj *CONST objv[]; /* Argument objects. */
1614 Tcl_WrongNumArgs(interp, 1, objv, "start test next command");
1618 result = Tcl_EvalObjEx(interp, objv[1], 0);
1619 if (result != TCL_OK) {
1620 if (result == TCL_ERROR) {
1621 Tcl_AddErrorInfo(interp, "\n (\"for\" initial command)");
1627 * We need to reset the result before passing it off to
1628 * Tcl_ExprBooleanObj. Otherwise, any error message will be appended
1629 * to the result of the last evaluation.
1632 Tcl_ResetResult(interp);
1633 result = Tcl_ExprBooleanObj(interp, objv[2], &value);
1634 if (result != TCL_OK) {
1640 result = Tcl_EvalObjEx(interp, objv[4], 0);
1641 if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
1642 if (result == TCL_ERROR) {
1643 char msg[32 + TCL_INTEGER_SPACE];
1645 sprintf(msg, "\n (\"for\" body line %d)",interp->errorLine);
1646 Tcl_AddErrorInfo(interp, msg);
1650 result = Tcl_EvalObjEx(interp, objv[3], 0);
1651 if (result == TCL_BREAK) {
1653 } else if (result != TCL_OK) {
1654 if (result == TCL_ERROR) {
1655 Tcl_AddErrorInfo(interp, "\n (\"for\" loop-end command)");
1660 if (result == TCL_BREAK) {
1663 if (result == TCL_OK) {
1664 Tcl_ResetResult(interp);
1670 *----------------------------------------------------------------------
1672 * Tcl_ForeachObjCmd --
1674 * This object-based procedure is invoked to process the "foreach" Tcl
1675 * command. See the user documentation for details on what it does.
1678 * A standard Tcl object result.
1681 * See the user documentation.
1683 *----------------------------------------------------------------------
1688 Tcl_ForeachObjCmd(dummy, interp, objc, objv)
1689 ClientData dummy; /* Not used. */
1690 Tcl_Interp *interp; /* Current interpreter. */
1691 int objc; /* Number of arguments. */
1692 Tcl_Obj *CONST objv[]; /* Argument objects. */
1694 int result = TCL_OK;
1695 int i; /* i selects a value list */
1696 int j, maxj; /* Number of loop iterations */
1697 int v; /* v selects a loop variable */
1698 int numLists; /* Count of value lists */
1702 * We copy the argument object pointers into a local array to avoid
1703 * the problem that "objv" might become invalid. It is a pointer into
1704 * the evaluation stack and that stack might be grown and reallocated
1705 * if the loop body requires a large amount of stack space.
1709 Tcl_Obj *(argObjStorage[NUM_ARGS]);
1710 Tcl_Obj **argObjv = argObjStorage;
1712 #define STATIC_LIST_SIZE 4
1713 int indexArray[STATIC_LIST_SIZE]; /* Array of value list indices */
1714 int varcListArray[STATIC_LIST_SIZE]; /* # loop variables per list */
1715 Tcl_Obj **varvListArray[STATIC_LIST_SIZE]; /* Array of var name lists */
1716 int argcListArray[STATIC_LIST_SIZE]; /* Array of value list sizes */
1717 Tcl_Obj **argvListArray[STATIC_LIST_SIZE]; /* Array of value lists */
1719 int *index = indexArray;
1720 int *varcList = varcListArray;
1721 Tcl_Obj ***varvList = varvListArray;
1722 int *argcList = argcListArray;
1723 Tcl_Obj ***argvList = argvListArray;
1725 if (objc < 4 || (objc%2 != 0)) {
1726 Tcl_WrongNumArgs(interp, 1, objv,
1727 "varList list ?varList list ...? command");
1732 * Create the object argument array "argObjv". Make sure argObjv is
1733 * large enough to hold the objc arguments.
1736 if (objc > NUM_ARGS) {
1737 argObjv = (Tcl_Obj **) ckalloc(objc * sizeof(Tcl_Obj *));
1739 for (i = 0; i < objc; i++) {
1740 argObjv[i] = objv[i];
1744 * Manage numList parallel value lists.
1745 * argvList[i] is a value list counted by argcList[i]
1746 * varvList[i] is the list of variables associated with the value list
1747 * varcList[i] is the number of variables associated with the value list
1748 * index[i] is the current pointer into the value list argvList[i]
1751 numLists = (objc-2)/2;
1752 if (numLists > STATIC_LIST_SIZE) {
1753 index = (int *) ckalloc(numLists * sizeof(int));
1754 varcList = (int *) ckalloc(numLists * sizeof(int));
1755 varvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
1756 argcList = (int *) ckalloc(numLists * sizeof(int));
1757 argvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
1759 for (i = 0; i < numLists; i++) {
1762 varvList[i] = (Tcl_Obj **) NULL;
1764 argvList[i] = (Tcl_Obj **) NULL;
1768 * Break up the value lists and variable lists into elements
1772 for (i = 0; i < numLists; i++) {
1773 result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
1774 &varcList[i], &varvList[i]);
1775 if (result != TCL_OK) {
1778 if (varcList[i] < 1) {
1779 Tcl_AppendToObj(Tcl_GetObjResult(interp),
1780 "foreach varlist is empty", -1);
1785 result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
1786 &argcList[i], &argvList[i]);
1787 if (result != TCL_OK) {
1791 j = argcList[i] / varcList[i];
1792 if ((argcList[i] % varcList[i]) != 0) {
1801 * Iterate maxj times through the lists in parallel
1802 * If some value lists run out of values, set loop vars to ""
1805 bodyPtr = argObjv[objc-1];
1806 for (j = 0; j < maxj; j++) {
1807 for (i = 0; i < numLists; i++) {
1809 * If a variable or value list object has been converted to
1810 * another kind of Tcl object, convert it back to a list object
1811 * and refetch the pointer to its element array.
1814 if (argObjv[1+i*2]->typePtr != &tclListType) {
1815 result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
1816 &varcList[i], &varvList[i]);
1817 if (result != TCL_OK) {
1818 panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i);
1821 if (argObjv[2+i*2]->typePtr != &tclListType) {
1822 result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
1823 &argcList[i], &argvList[i]);
1824 if (result != TCL_OK) {
1825 panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i);
1829 for (v = 0; v < varcList[i]; v++) {
1831 Tcl_Obj *valuePtr, *varValuePtr;
1834 if (k < argcList[i]) {
1835 valuePtr = argvList[i][k];
1837 valuePtr = Tcl_NewObj(); /* empty string */
1840 varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v],
1842 if (varValuePtr == NULL) {
1844 Tcl_DecrRefCount(valuePtr);
1846 Tcl_ResetResult(interp);
1847 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1848 "couldn't set loop variable: \"",
1849 Tcl_GetString(varvList[i][v]), "\"", (char *) NULL);
1857 result = Tcl_EvalObjEx(interp, bodyPtr, 0);
1858 if (result != TCL_OK) {
1859 if (result == TCL_CONTINUE) {
1861 } else if (result == TCL_BREAK) {
1864 } else if (result == TCL_ERROR) {
1865 char msg[32 + TCL_INTEGER_SPACE];
1867 sprintf(msg, "\n (\"foreach\" body line %d)",
1869 Tcl_AddObjErrorInfo(interp, msg, -1);
1876 if (result == TCL_OK) {
1877 Tcl_ResetResult(interp);
1881 if (numLists > STATIC_LIST_SIZE) {
1882 ckfree((char *) index);
1883 ckfree((char *) varcList);
1884 ckfree((char *) argcList);
1885 ckfree((char *) varvList);
1886 ckfree((char *) argvList);
1888 if (argObjv != argObjStorage) {
1889 ckfree((char *) argObjv);
1892 #undef STATIC_LIST_SIZE
1897 *----------------------------------------------------------------------
1899 * Tcl_FormatObjCmd --
1901 * This procedure is invoked to process the "format" Tcl command.
1902 * See the user documentation for details on what it does.
1905 * A standard Tcl result.
1908 * See the user documentation.
1910 *----------------------------------------------------------------------
1915 Tcl_FormatObjCmd(dummy, interp, objc, objv)
1916 ClientData dummy; /* Not used. */
1917 Tcl_Interp *interp; /* Current interpreter. */
1918 int objc; /* Number of arguments. */
1919 Tcl_Obj *CONST objv[]; /* Argument objects. */
1921 char *format; /* Used to read characters from the format
1923 int formatLen; /* The length of the format string */
1924 char *endPtr; /* Points to the last char in format array */
1925 char newFormat[40]; /* A new format specifier is generated here. */
1926 int width; /* Field width from field specifier, or 0 if
1927 * no width given. */
1928 int precision; /* Field precision from field specifier, or 0
1929 * if no precision given. */
1930 int size; /* Number of bytes needed for result of
1931 * conversion, based on type of conversion
1932 * ("e", "s", etc.), width, and precision. */
1933 int intValue; /* Used to hold value to pass to sprintf, if
1934 * it's a one-word integer or char value */
1935 char *ptrValue = NULL; /* Used to hold value to pass to sprintf, if
1936 * it's a one-word value. */
1937 double doubleValue; /* Used to hold value to pass to sprintf if
1938 * it's a double value. */
1939 int whichValue; /* Indicates which of intValue, ptrValue,
1940 * or doubleValue has the value to pass to
1941 * sprintf, according to the following
1943 # define INT_VALUE 0
1944 # define CHAR_VALUE 1
1945 # define PTR_VALUE 2
1946 # define DOUBLE_VALUE 3
1947 # define STRING_VALUE 4
1948 # define MAX_FLOAT_SIZE 320
1950 Tcl_Obj *resultPtr; /* Where result is stored finally. */
1951 char staticBuf[MAX_FLOAT_SIZE + 1];
1952 /* A static buffer to copy the format results
1954 char *dst = staticBuf; /* The buffer that sprintf writes into each
1955 * time the format processes a specifier */
1956 int dstSize = MAX_FLOAT_SIZE;
1957 /* The size of the dst buffer */
1958 int noPercent; /* Special case for speed: indicates there's
1959 * no field specifier, just a string to copy.*/
1960 int objIndex; /* Index of argument to substitute next. */
1961 int gotXpg = 0; /* Non-zero means that an XPG3 %n$-style
1962 * specifier has been seen. */
1963 int gotSequential = 0; /* Non-zero means that a regular sequential
1964 * (non-XPG3) conversion specifier has been
1966 int useShort; /* Value to be printed is short (half word). */
1967 char *end; /* Used to locate end of numerical fields. */
1968 int stringLen = 0; /* Length of string in characters rather
1969 * than bytes. Used for %s substitution. */
1970 int gotMinus; /* Non-zero indicates that a minus flag has
1971 * been seen in the current field. */
1972 int gotPrecision; /* Non-zero indicates that a precision has
1973 * been set for the current field. */
1974 int gotZero; /* Non-zero indicates that a zero flag has
1975 * been seen in the current field. */
1978 * This procedure is a bit nasty. The goal is to use sprintf to
1979 * do most of the dirty work. There are several problems:
1980 * 1. this procedure can't trust its arguments.
1981 * 2. we must be able to provide a large enough result area to hold
1982 * whatever's generated. This is hard to estimate.
1983 * 3. there's no way to move the arguments from objv to the call
1984 * to sprintf in a reasonable way. This is particularly nasty
1985 * because some of the arguments may be two-word values (doubles).
1986 * So, what happens here is to scan the format string one % group
1987 * at a time, making many individual calls to sprintf.
1991 Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg arg ...?");
1995 format = (char *) Tcl_GetStringFromObj(objv[1], &formatLen);
1996 endPtr = format + formatLen;
1997 resultPtr = Tcl_NewObj();
2000 while (format < endPtr) {
2001 register char *newPtr = newFormat;
2003 width = precision = noPercent = useShort = 0;
2004 gotZero = gotMinus = gotPrecision = 0;
2005 whichValue = PTR_VALUE;
2008 * Get rid of any characters before the next field specifier.
2010 if (*format != '%') {
2012 while ((*format != '%') && (format < endPtr)) {
2015 size = format - ptrValue;
2020 if (format[1] == '%') {
2029 * Parse off a field specifier, compute how many characters
2030 * will be needed to store the result, and substitute for
2031 * "*" size specifiers.
2036 if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
2040 * Check for an XPG3-style %n$ specification. Note: there
2041 * must not be a mixture of XPG3 specs and non-XPG3 specs
2042 * in the same format string.
2045 tmp = strtoul(format, &end, 10); /* INTL: "C" locale. */
2051 if (gotSequential) {
2055 if ((objIndex < 2) || (objIndex >= objc)) {
2068 while ((*format == '-') || (*format == '#') || (*format == '0')
2069 || (*format == ' ') || (*format == '+')) {
2070 if (*format == '-') {
2073 if (*format == '0') {
2075 * This will be handled by sprintf for numbers, but we
2076 * need to do the char/string ones ourselves
2084 if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
2085 width = strtoul(format, &end, 10); /* INTL: Tcl source. */
2087 } else if (*format == '*') {
2088 if (objIndex >= objc) {
2091 if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
2092 objv[objIndex], &width) != TCL_OK) {
2104 if (width > 100000) {
2106 * Don't allow arbitrarily large widths: could cause core
2107 * dump when we try to allocate a zillion bytes of memory
2112 } else if (width < 0) {
2116 TclFormatInt(newPtr, width); /* INTL: printf format. */
2117 while (*newPtr != 0) {
2121 if (*format == '.') {
2127 if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
2128 precision = strtoul(format, &end, 10); /* INTL: "C" locale. */
2130 } else if (*format == '*') {
2131 if (objIndex >= objc) {
2134 if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
2135 objv[objIndex], &precision) != TCL_OK) {
2142 TclFormatInt(newPtr, precision); /* INTL: printf format. */
2143 while (*newPtr != 0) {
2147 if (*format == 'l') {
2149 } else if (*format == 'h') {
2158 if (objIndex >= objc) {
2169 if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
2170 objv[objIndex], &intValue) != TCL_OK) {
2173 whichValue = INT_VALUE;
2174 size = 40 + precision;
2178 * Compute the length of the string in characters and add
2179 * any additional space required by the field width. All of
2180 * the extra characters will be spaces, so one byte per
2181 * character is adequate.
2184 whichValue = STRING_VALUE;
2185 ptrValue = Tcl_GetStringFromObj(objv[objIndex], &size);
2186 stringLen = Tcl_NumUtfChars(ptrValue, size);
2187 if (gotPrecision && (precision < stringLen)) {
2188 stringLen = precision;
2190 size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue;
2191 if (width > stringLen) {
2192 size += (width - stringLen);
2196 if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
2197 objv[objIndex], &intValue) != TCL_OK) {
2200 whichValue = CHAR_VALUE;
2201 size = width + TCL_UTF_MAX;
2208 if (Tcl_GetDoubleFromObj(interp, /* INTL: Tcl source. */
2209 objv[objIndex], &doubleValue) != TCL_OK) {
2212 whichValue = DOUBLE_VALUE;
2213 size = MAX_FLOAT_SIZE;
2214 if (precision > 10) {
2219 Tcl_SetResult(interp,
2220 "format string ended in middle of field specifier",
2225 sprintf(buf, "bad field specifier \"%c\"", *format);
2226 Tcl_SetResult(interp, buf, TCL_VOLATILE);
2234 * Make sure that there's enough space to hold the formatted
2235 * result, then format it.
2243 Tcl_AppendToObj(resultPtr, ptrValue, size);
2245 if (size > dstSize) {
2246 if (dst != staticBuf) {
2249 dst = (char *) ckalloc((unsigned) (size + 1));
2252 switch (whichValue) {
2253 case DOUBLE_VALUE: {
2254 sprintf(dst, newFormat, doubleValue); /* INTL: user locale. */
2259 sprintf(dst, newFormat, (short) intValue);
2261 sprintf(dst, newFormat, intValue);
2267 char padChar = (gotZero ? '0' : ' ');
2270 for ( ; --width > 0; ptr++) {
2274 ptr += Tcl_UniCharToUtf(intValue, ptr);
2275 for ( ; --width > 0; ptr++) {
2281 case STRING_VALUE: {
2283 char padChar = (gotZero ? '0' : ' ');
2287 if (width > stringLen) {
2288 pad = width - stringLen;
2300 size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue;
2302 memcpy(ptr, ptrValue, (size_t) size);
2313 sprintf(dst, newFormat, ptrValue);
2317 Tcl_AppendToObj(resultPtr, dst, -1);
2321 Tcl_SetObjResult(interp, resultPtr);
2322 if(dst != staticBuf) {
2328 Tcl_SetResult(interp,
2329 "cannot mix \"%\" and \"%n$\" conversion specifiers", TCL_STATIC);
2334 Tcl_SetResult(interp,
2335 "\"%n$\" argument index out of range", TCL_STATIC);
2337 Tcl_SetResult(interp,
2338 "not enough arguments for all format specifiers", TCL_STATIC);
2342 if(dst != staticBuf) {
2345 Tcl_DecrRefCount(resultPtr);
2350 *---------------------------------------------------------------------------
2352 * StringifyObjects --
2354 * Helper function to bridge the gap between an object-based procedure
2355 * and an older string-based procedure.
2357 * Given an array of objects, allocate an array that consists of the
2358 * string representations of those objects.
2361 * The return value is a pointer to the newly allocated array of
2362 * strings. Elements 0 to (objc-1) of the string array point to the
2363 * string representation of the corresponding element in the source
2364 * object array; element objc of the string array is NULL.
2367 * Memory allocated. The caller must eventually free this memory
2368 * by calling ckfree() on the return value.
2370 *---------------------------------------------------------------------------
2374 StringifyObjects(objc, objv)
2375 int objc; /* Number of arguments. */
2376 Tcl_Obj *CONST objv[]; /* Argument objects. */
2381 argv = (char **) ckalloc((objc + 1) * sizeof(char *));
2382 for (i = 0; i < objc; i++) {
2383 argv[i] = Tcl_GetString(objv[i]);