os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCmdAH.c
First public contribution.
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.
10 * Portions Copyright (c) 2007 Nokia Corporation and/or its subsidiaries. All rights reserved.
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 * RCS: @(#) $Id: tclCmdAH.c,v 1.27.2.16 2006/11/28 22:20:00 andreas_kupries Exp $
21 #if defined(__SYMBIAN32__)
22 #include "tclSymbianGlobals.h"
26 * Prototypes for local procedures defined in this file:
29 static int CheckAccess _ANSI_ARGS_((Tcl_Interp *interp,
30 Tcl_Obj *objPtr, int mode));
31 static int GetStatBuf _ANSI_ARGS_((Tcl_Interp *interp,
32 Tcl_Obj *objPtr, Tcl_FSStatProc *statProc,
33 Tcl_StatBuf *statPtr));
34 static char * GetTypeFromMode _ANSI_ARGS_((int mode));
35 static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
36 char *varName, Tcl_StatBuf *statPtr));
39 *----------------------------------------------------------------------
43 * This procedure is invoked to process the "break" Tcl command.
44 * See the user documentation for details on what it does.
46 * With the bytecode compiler, this procedure is only called when
47 * a command name is computed at runtime, and is "break" or the name
48 * to which "break" was renamed: e.g., "set z break; $z"
51 * A standard Tcl result.
54 * See the user documentation.
56 *----------------------------------------------------------------------
61 Tcl_BreakObjCmd(dummy, interp, objc, objv)
62 ClientData dummy; /* Not used. */
63 Tcl_Interp *interp; /* Current interpreter. */
64 int objc; /* Number of arguments. */
65 Tcl_Obj *CONST objv[]; /* Argument objects. */
68 Tcl_WrongNumArgs(interp, 1, objv, NULL);
75 *----------------------------------------------------------------------
79 * This procedure is invoked to process the "case" Tcl command.
80 * See the user documentation for details on what it does.
83 * A standard Tcl object result.
86 * See the user documentation.
88 *----------------------------------------------------------------------
93 Tcl_CaseObjCmd(dummy, interp, objc, objv)
94 ClientData dummy; /* Not used. */
95 Tcl_Interp *interp; /* Current interpreter. */
96 int objc; /* Number of arguments. */
97 Tcl_Obj *CONST objv[]; /* Argument objects. */
100 int body, result, caseObjc;
102 Tcl_Obj *CONST *caseObjv;
106 Tcl_WrongNumArgs(interp, 1, objv,
107 "string ?in? patList body ... ?default body?");
111 string = Tcl_GetString(objv[1]);
114 arg = Tcl_GetString(objv[2]);
115 if (strcmp(arg, "in") == 0) {
124 * If all of the pattern/command pairs are lumped into a single
125 * argument, split them out again.
131 Tcl_ListObjGetElements(interp, caseObjv[0], &caseObjc, &newObjv);
135 for (i = 0; i < caseObjc; i += 2) {
137 CONST char **patObjv;
141 if (i == (caseObjc - 1)) {
142 Tcl_ResetResult(interp);
143 Tcl_AppendToObj(Tcl_GetObjResult(interp),
144 "extra case pattern with no body", -1);
149 * Check for special case of single pattern (no list) with
150 * no backslash sequences.
153 pat = Tcl_GetString(caseObjv[i]);
154 for (p = (unsigned char *) pat; *p != '\0'; p++) {
155 if (isspace(*p) || (*p == '\\')) { /* INTL: ISO space, UCHAR */
160 if ((*pat == 'd') && (strcmp(pat, "default") == 0)) {
163 if (Tcl_StringMatch(string, pat)) {
172 * Break up pattern lists, then check each of the patterns
176 result = Tcl_SplitList(interp, pat, &patObjc, &patObjv);
177 if (result != TCL_OK) {
180 for (j = 0; j < patObjc; j++) {
181 if (Tcl_StringMatch(string, patObjv[j])) {
186 ckfree((char *) patObjv);
194 armPtr = caseObjv[body - 1];
195 result = Tcl_EvalObjEx(interp, caseObjv[body], 0);
196 if (result == TCL_ERROR) {
197 char msg[100 + TCL_INTEGER_SPACE];
199 arg = Tcl_GetString(armPtr);
201 "\n (\"%.50s\" arm line %d)", arg,
203 Tcl_AddObjErrorInfo(interp, msg, -1);
209 * Nothing matched: return nothing.
216 *----------------------------------------------------------------------
220 * This object-based procedure is invoked to process the "catch" Tcl
221 * command. See the user documentation for details on what it does.
224 * A standard Tcl object result.
227 * See the user documentation.
229 *----------------------------------------------------------------------
234 Tcl_CatchObjCmd(dummy, interp, objc, objv)
235 ClientData dummy; /* Not used. */
236 Tcl_Interp *interp; /* Current interpreter. */
237 int objc; /* Number of arguments. */
238 Tcl_Obj *CONST objv[]; /* Argument objects. */
240 Tcl_Obj *varNamePtr = NULL;
243 Interp* iPtr = (Interp*) interp;
246 if ((objc != 2) && (objc != 3)) {
247 Tcl_WrongNumArgs(interp, 1, objv, "command ?varName?");
252 varNamePtr = objv[2];
256 result = Tcl_EvalObjEx(interp, objv[1], 0);
258 /* TIP #280. Make invoking context available to caught script */
259 result = TclEvalObjEx(interp, objv[1], 0, iPtr->cmdFramePtr,1);
263 if (Tcl_ObjSetVar2(interp, varNamePtr, NULL,
264 Tcl_GetObjResult(interp), 0) == NULL) {
265 Tcl_ResetResult(interp);
266 Tcl_AppendToObj(Tcl_GetObjResult(interp),
267 "couldn't save command result in variable", -1);
273 * Set the interpreter's object result to an integer object holding the
274 * integer Tcl_EvalObj result. Note that we don't bother generating a
275 * string representation. We reset the interpreter's object result
276 * to an unshared empty object and then set it to be an integer object.
279 Tcl_ResetResult(interp);
280 Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
285 *----------------------------------------------------------------------
289 * This procedure is invoked to process the "cd" Tcl command.
290 * See the user documentation for details on what it does.
293 * A standard Tcl result.
296 * See the user documentation.
298 *----------------------------------------------------------------------
303 Tcl_CdObjCmd(dummy, interp, objc, objv)
304 ClientData dummy; /* Not used. */
305 Tcl_Interp *interp; /* Current interpreter. */
306 int objc; /* Number of arguments. */
307 Tcl_Obj *CONST objv[]; /* Argument objects. */
313 Tcl_WrongNumArgs(interp, 1, objv, "?dirName?");
320 dir = Tcl_NewStringObj("~",1);
321 Tcl_IncrRefCount(dir);
323 if (Tcl_FSConvertToPathType(interp, dir) != TCL_OK) {
326 result = Tcl_FSChdir(dir);
327 if (result != TCL_OK) {
328 Tcl_AppendResult(interp, "couldn't change working directory to \"",
329 Tcl_GetString(dir), "\": ", Tcl_PosixError(interp), (char *) NULL);
334 Tcl_DecrRefCount(dir);
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 CONST 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: {
457 encoding = Tcl_GetEncoding(interp, NULL);
459 } else if (objc == 4) {
460 if (TclGetEncodingFromObj(interp, objv[2], &encoding)
466 Tcl_WrongNumArgs(interp, 2, objv, "?encoding? data");
470 if ((enum options) index == ENC_CONVERTFROM) {
472 * Treat the string as binary data.
475 string = (char *) Tcl_GetByteArrayFromObj(data, &length);
476 Tcl_ExternalToUtfDString(encoding, string, length, &ds);
479 * Note that we cannot use Tcl_DStringResult here because
480 * it will truncate the string at the first null byte.
483 Tcl_SetStringObj(Tcl_GetObjResult(interp),
484 Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
485 Tcl_DStringFree(&ds);
488 * Store the result as binary data.
491 string = Tcl_GetStringFromObj(data, &length);
492 Tcl_UtfToExternalDString(encoding, string, length, &ds);
493 resultPtr = Tcl_GetObjResult(interp);
494 Tcl_SetByteArrayObj(resultPtr,
495 (unsigned char *) Tcl_DStringValue(&ds),
496 Tcl_DStringLength(&ds));
497 Tcl_DStringFree(&ds);
500 Tcl_FreeEncoding(encoding);
505 Tcl_WrongNumArgs(interp, 2, objv, NULL);
508 Tcl_GetEncodingNames(interp);
513 Tcl_WrongNumArgs(interp, 2, objv, "?encoding?");
517 Tcl_SetStringObj(Tcl_GetObjResult(interp),
518 Tcl_GetEncodingName(NULL), -1);
520 return Tcl_SetSystemEncoding(interp,
521 Tcl_GetStringFromObj(objv[2], NULL));
530 *----------------------------------------------------------------------
534 * This procedure is invoked to process the "error" Tcl command.
535 * See the user documentation for details on what it does.
538 * A standard Tcl object result.
541 * See the user documentation.
543 *----------------------------------------------------------------------
548 Tcl_ErrorObjCmd(dummy, interp, objc, objv)
549 ClientData dummy; /* Not used. */
550 Tcl_Interp *interp; /* Current interpreter. */
551 int objc; /* Number of arguments. */
552 Tcl_Obj *CONST objv[]; /* Argument objects. */
554 Interp *iPtr = (Interp *) interp;
558 if ((objc < 2) || (objc > 4)) {
559 Tcl_WrongNumArgs(interp, 1, objv, "message ?errorInfo? ?errorCode?");
563 if (objc >= 3) { /* process the optional info argument */
564 info = Tcl_GetStringFromObj(objv[2], &infoLen);
566 Tcl_AddObjErrorInfo(interp, info, infoLen);
567 iPtr->flags |= ERR_ALREADY_LOGGED;
572 Tcl_SetVar2Ex(interp, "errorCode", NULL, objv[3], TCL_GLOBAL_ONLY);
573 iPtr->flags |= ERROR_CODE_SET;
576 Tcl_SetObjResult(interp, objv[1]);
581 *----------------------------------------------------------------------
585 * This object-based procedure is invoked to process the "eval" Tcl
586 * command. See the user documentation for details on what it does.
589 * A standard Tcl object result.
592 * See the user documentation.
594 *----------------------------------------------------------------------
599 Tcl_EvalObjCmd(dummy, interp, objc, objv)
600 ClientData dummy; /* Not used. */
601 Tcl_Interp *interp; /* Current interpreter. */
602 int objc; /* Number of arguments. */
603 Tcl_Obj *CONST objv[]; /* Argument objects. */
606 register Tcl_Obj *objPtr;
608 Interp* iPtr = (Interp*) interp;
612 Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
618 result = Tcl_EvalObjEx(interp, objv[1], TCL_EVAL_DIRECT);
620 /* TIP #280. Make invoking context available to eval'd script */
621 result = TclEvalObjEx(interp, objv[1], TCL_EVAL_DIRECT,
622 iPtr->cmdFramePtr,1);
626 * More than one argument: concatenate them together with spaces
627 * between, then evaluate the result. Tcl_EvalObjEx will delete
628 * the object when it decrements its refcount after eval'ing it.
630 objPtr = Tcl_ConcatObj(objc-1, objv+1);
632 result = Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_DIRECT);
634 /* TIP #280. Make invoking context available to eval'd script */
635 result = TclEvalObjEx(interp, objPtr, TCL_EVAL_DIRECT, NULL, 0);
638 if (result == TCL_ERROR) {
639 char msg[32 + TCL_INTEGER_SPACE];
641 sprintf(msg, "\n (\"eval\" body line %d)", interp->errorLine);
642 Tcl_AddObjErrorInfo(interp, msg, -1);
648 *----------------------------------------------------------------------
652 * This procedure is invoked to process the "exit" Tcl command.
653 * See the user documentation for details on what it does.
656 * A standard Tcl object result.
659 * See the user documentation.
661 *----------------------------------------------------------------------
666 Tcl_ExitObjCmd(dummy, interp, objc, objv)
667 ClientData dummy; /* Not used. */
668 Tcl_Interp *interp; /* Current interpreter. */
669 int objc; /* Number of arguments. */
670 Tcl_Obj *CONST objv[]; /* Argument objects. */
674 if ((objc != 1) && (objc != 2)) {
675 Tcl_WrongNumArgs(interp, 1, objv, "?returnCode?");
681 } else if (Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_OK) {
686 return TCL_OK; /* Better not ever reach this! */
690 *----------------------------------------------------------------------
694 * This object-based procedure is invoked to process the "expr" Tcl
695 * command. See the user documentation for details on what it does.
697 * With the bytecode compiler, this procedure is called in two
698 * circumstances: 1) to execute expr commands that are too complicated
699 * or too unsafe to try compiling directly into an inline sequence of
700 * instructions, and 2) to execute commands where the command name is
701 * computed at runtime and is "expr" or the name to which "expr" was
702 * renamed (e.g., "set z expr; $z 2+3")
705 * A standard Tcl object result.
708 * See the user documentation.
710 *----------------------------------------------------------------------
715 Tcl_ExprObjCmd(dummy, interp, objc, objv)
716 ClientData dummy; /* Not used. */
717 Tcl_Interp *interp; /* Current interpreter. */
718 int objc; /* Number of arguments. */
719 Tcl_Obj *CONST objv[]; /* Argument objects. */
721 register Tcl_Obj *objPtr;
723 register char *bytes;
724 int length, i, result;
727 Tcl_WrongNumArgs(interp, 1, objv, "arg ?arg ...?");
732 result = Tcl_ExprObj(interp, objv[1], &resultPtr);
733 if (result == TCL_OK) {
734 Tcl_SetObjResult(interp, resultPtr);
735 Tcl_DecrRefCount(resultPtr); /* done with the result object */
741 * Create a new object holding the concatenated argument strings.
744 /*** QUESTION: Do we need to copy the slow way? ***/
745 bytes = Tcl_GetStringFromObj(objv[1], &length);
746 objPtr = Tcl_NewStringObj(bytes, length);
747 Tcl_IncrRefCount(objPtr);
748 for (i = 2; i < objc; i++) {
749 Tcl_AppendToObj(objPtr, " ", 1);
750 bytes = Tcl_GetStringFromObj(objv[i], &length);
751 Tcl_AppendToObj(objPtr, bytes, length);
755 * Evaluate the concatenated string object.
758 result = Tcl_ExprObj(interp, objPtr, &resultPtr);
759 if (result == TCL_OK) {
760 Tcl_SetObjResult(interp, resultPtr);
761 Tcl_DecrRefCount(resultPtr); /* done with the result object */
765 * Free allocated resources.
768 Tcl_DecrRefCount(objPtr);
773 *----------------------------------------------------------------------
777 * This procedure is invoked to process the "file" Tcl command.
778 * See the user documentation for details on what it does.
779 * PLEASE NOTE THAT THIS FAILS WITH FILENAMES AND PATHS WITH
780 * EMBEDDED NULLS, WHICH COULD THEORETICALLY HAPPEN ON A MAC.
781 * With the object-based Tcl_FS APIs, the above NOTE may no
782 * longer be true. In any case this assertion should be tested.
785 * A standard Tcl result.
788 * See the user documentation.
790 *----------------------------------------------------------------------
795 Tcl_FileObjCmd(dummy, interp, objc, objv)
796 ClientData dummy; /* Not used. */
797 Tcl_Interp *interp; /* Current interpreter. */
798 int objc; /* Number of arguments. */
799 Tcl_Obj *CONST objv[]; /* Argument objects. */
804 * This list of constants should match the fileOption string array below.
807 static CONST char *fileOptions[] = {
808 "atime", "attributes", "channels", "copy",
810 "dirname", "executable", "exists", "extension",
811 "isdirectory", "isfile", "join", "link",
812 "lstat", "mtime", "mkdir", "nativename",
813 "normalize", "owned",
814 "pathtype", "readable", "readlink", "rename",
815 "rootname", "separator", "size", "split",
817 "tail", "type", "volumes", "writable",
821 FCMD_ATIME, FCMD_ATTRIBUTES, FCMD_CHANNELS, FCMD_COPY,
823 FCMD_DIRNAME, FCMD_EXECUTABLE, FCMD_EXISTS, FCMD_EXTENSION,
824 FCMD_ISDIRECTORY, FCMD_ISFILE, FCMD_JOIN, FCMD_LINK,
825 FCMD_LSTAT, FCMD_MTIME, FCMD_MKDIR, FCMD_NATIVENAME,
826 FCMD_NORMALIZE, FCMD_OWNED,
827 FCMD_PATHTYPE, FCMD_READABLE, FCMD_READLINK, FCMD_RENAME,
828 FCMD_ROOTNAME, FCMD_SEPARATOR, FCMD_SIZE, FCMD_SPLIT,
829 FCMD_STAT, FCMD_SYSTEM,
830 FCMD_TAIL, FCMD_TYPE, FCMD_VOLUMES, FCMD_WRITABLE
834 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
837 if (Tcl_GetIndexFromObj(interp, objv[1], fileOptions, "option", 0,
842 switch ((enum options) index) {
847 if ((objc < 3) || (objc > 4)) {
848 Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
851 if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
857 if (Tcl_GetLongFromObj(interp, objv[3], &newTime) != TCL_OK) {
860 tval.actime = newTime;
861 tval.modtime = buf.st_mtime;
862 if (Tcl_FSUtime(objv[2], &tval) != 0) {
863 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
864 "could not set access time for file \"",
865 Tcl_GetString(objv[2]), "\": ",
866 Tcl_PosixError(interp), (char *) NULL);
870 * Do another stat to ensure that the we return the
871 * new recognized atime - hopefully the same as the
872 * one we sent in. However, fs's like FAT don't
873 * even know what atime is.
875 if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
879 Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) buf.st_atime);
882 case FCMD_ATTRIBUTES: {
883 return TclFileAttrsCmd(interp, objc, objv);
885 case FCMD_CHANNELS: {
886 if ((objc < 2) || (objc > 3)) {
887 Tcl_WrongNumArgs(interp, 2, objv, "?pattern?");
890 return Tcl_GetChannelNamesEx(interp,
891 ((objc == 2) ? NULL : Tcl_GetString(objv[2])));
894 return TclFileCopyCmd(interp, objc, objv);
897 return TclFileDeleteCmd(interp, objc, objv);
904 dirPtr = TclFileDirname(interp, objv[2]);
905 if (dirPtr == NULL) {
908 Tcl_SetObjResult(interp, dirPtr);
909 Tcl_DecrRefCount(dirPtr);
913 case FCMD_EXECUTABLE: {
917 return CheckAccess(interp, objv[2], X_OK);
923 return CheckAccess(interp, objv[2], F_OK);
925 case FCMD_EXTENSION: {
926 char *fileName, *extension;
930 fileName = Tcl_GetString(objv[2]);
931 extension = TclGetExtension(fileName);
932 if (extension != NULL) {
933 Tcl_SetStringObj(Tcl_GetObjResult(interp), extension, -1);
937 case FCMD_ISDIRECTORY: {
945 if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
946 value = S_ISDIR(buf.st_mode);
948 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
959 if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
960 value = S_ISREG(buf.st_mode);
962 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
969 Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
972 resObj = Tcl_FSJoinToPath(NULL, objc - 2, objv + 2);
973 Tcl_SetObjResult(interp, resObj);
980 if (objc < 3 || objc > 5) {
981 Tcl_WrongNumArgs(interp, 2, objv,
982 "?-linktype? linkname ?target?");
986 /* Index of the 'source' argument */
996 /* We have a '-linktype' argument */
997 static CONST char *linkTypes[] = {
998 "-symbolic", "-hard", NULL
1000 if (Tcl_GetIndexFromObj(interp, objv[2], linkTypes,
1001 "switch", 0, &linkAction) != TCL_OK) {
1004 if (linkAction == 0) {
1005 linkAction = TCL_CREATE_SYMBOLIC_LINK;
1007 linkAction = TCL_CREATE_HARD_LINK;
1010 linkAction = TCL_CREATE_SYMBOLIC_LINK|TCL_CREATE_HARD_LINK;
1012 if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
1015 /* Create link from source to target */
1016 contents = Tcl_FSLink(objv[index], objv[index+1], linkAction);
1017 if (contents == NULL) {
1019 * We handle two common error cases specially, and
1020 * for all other errors, we use the standard posix
1023 if (errno == EEXIST) {
1024 Tcl_AppendResult(interp, "could not create new link \"",
1025 Tcl_GetString(objv[index]),
1026 "\": that path already exists", (char *) NULL);
1027 } else if (errno == ENOENT) {
1028 Tcl_AppendResult(interp, "could not create new link \"",
1029 Tcl_GetString(objv[index]),
1030 "\" since target \"",
1031 Tcl_GetString(objv[index+1]),
1035 Tcl_AppendResult(interp, "could not create new link \"",
1036 Tcl_GetString(objv[index]), "\" pointing to \"",
1037 Tcl_GetString(objv[index+1]), "\": ",
1038 Tcl_PosixError(interp), (char *) NULL);
1043 if (Tcl_FSConvertToPathType(interp, objv[index]) != TCL_OK) {
1047 contents = Tcl_FSLink(objv[index], NULL, 0);
1048 if (contents == NULL) {
1049 Tcl_AppendResult(interp, "could not read link \"",
1050 Tcl_GetString(objv[index]), "\": ",
1051 Tcl_PosixError(interp), (char *) NULL);
1055 Tcl_SetObjResult(interp, contents);
1058 * If we are reading a link, we need to free this
1059 * result refCount. If we are creating a link, this
1060 * will just be objv[index+1], and so we don't own it.
1062 Tcl_DecrRefCount(contents);
1071 Tcl_WrongNumArgs(interp, 2, objv, "name varName");
1074 if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
1077 varName = Tcl_GetString(objv[3]);
1078 return StoreStatData(interp, varName, &buf);
1082 struct utimbuf tval;
1084 if ((objc < 3) || (objc > 4)) {
1085 Tcl_WrongNumArgs(interp, 2, objv, "name ?time?");
1088 if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
1094 if (Tcl_GetLongFromObj(interp, objv[3], &newTime) != TCL_OK) {
1097 tval.actime = buf.st_atime;
1098 tval.modtime = newTime;
1099 if (Tcl_FSUtime(objv[2], &tval) != 0) {
1100 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1101 "could not set modification time for file \"",
1102 Tcl_GetString(objv[2]), "\": ",
1103 Tcl_PosixError(interp), (char *) NULL);
1107 * Do another stat to ensure that the we return the
1108 * new recognized atime - hopefully the same as the
1109 * one we sent in. However, fs's like FAT don't
1110 * even know what atime is.
1112 if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
1116 Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) buf.st_mtime);
1121 Tcl_WrongNumArgs(interp, 2, objv, "name ?name ...?");
1124 return TclFileMakeDirsCmd(interp, objc, objv);
1126 case FCMD_NATIVENAME: {
1127 CONST char *fileName;
1133 fileName = Tcl_GetString(objv[2]);
1134 fileName = Tcl_TranslateFileName(interp, fileName, &ds);
1135 if (fileName == NULL) {
1138 Tcl_SetStringObj(Tcl_GetObjResult(interp), fileName,
1139 Tcl_DStringLength(&ds));
1140 Tcl_DStringFree(&ds);
1143 case FCMD_NORMALIZE: {
1147 Tcl_WrongNumArgs(interp, 2, objv, "filename");
1151 fileName = Tcl_FSGetNormalizedPath(interp, objv[2]);
1152 if (fileName == NULL) {
1155 Tcl_SetObjResult(interp, fileName);
1166 if (GetStatBuf(NULL, objv[2], Tcl_FSStat, &buf) == TCL_OK) {
1168 * For Windows and Macintosh, there are no user ids
1169 * associated with a file, so we always return 1.
1172 #if (defined(__WIN32__) || defined(MAC_TCL))
1175 value = (geteuid() == buf.st_uid);
1178 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
1181 case FCMD_PATHTYPE: {
1185 switch (Tcl_FSGetPathType(objv[2])) {
1186 case TCL_PATH_ABSOLUTE:
1187 Tcl_SetStringObj(Tcl_GetObjResult(interp), "absolute", -1);
1189 case TCL_PATH_RELATIVE:
1190 Tcl_SetStringObj(Tcl_GetObjResult(interp), "relative", -1);
1192 case TCL_PATH_VOLUME_RELATIVE:
1193 Tcl_SetStringObj(Tcl_GetObjResult(interp),
1194 "volumerelative", -1);
1199 case FCMD_READABLE: {
1203 return CheckAccess(interp, objv[2], R_OK);
1205 case FCMD_READLINK: {
1212 if (Tcl_FSConvertToPathType(interp, objv[2]) != TCL_OK) {
1216 contents = Tcl_FSLink(objv[2], NULL, 0);
1218 if (contents == NULL) {
1219 Tcl_AppendResult(interp, "could not readlink \"",
1220 Tcl_GetString(objv[2]), "\": ",
1221 Tcl_PosixError(interp), (char *) NULL);
1224 Tcl_SetObjResult(interp, contents);
1225 Tcl_DecrRefCount(contents);
1229 return TclFileRenameCmd(interp, objc, objv);
1231 case FCMD_ROOTNAME: {
1233 char *fileName, *extension;
1238 fileName = Tcl_GetStringFromObj(objv[2], &length);
1239 extension = TclGetExtension(fileName);
1240 if (extension == NULL) {
1241 Tcl_SetObjResult(interp, objv[2]);
1243 Tcl_SetStringObj(Tcl_GetObjResult(interp), fileName,
1244 (int) (length - strlen(extension)));
1248 case FCMD_SEPARATOR: {
1249 if ((objc < 2) || (objc > 3)) {
1250 Tcl_WrongNumArgs(interp, 2, objv, "?name?");
1254 char *separator = NULL; /* lint */
1255 switch (tclPlatform) {
1256 case TCL_PLATFORM_UNIX:
1259 case TCL_PLATFORM_WINDOWS:
1262 case TCL_PLATFORM_MAC:
1266 Tcl_SetObjResult(interp, Tcl_NewStringObj(separator,1));
1268 Tcl_Obj *separatorObj = Tcl_FSPathSeparator(objv[2]);
1269 if (separatorObj != NULL) {
1270 Tcl_SetObjResult(interp, separatorObj);
1272 Tcl_SetObjResult(interp,
1273 Tcl_NewStringObj("Unrecognised path",-1));
1285 if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
1288 Tcl_SetWideIntObj(Tcl_GetObjResult(interp),
1289 (Tcl_WideInt) buf.st_size);
1296 Tcl_SetObjResult(interp, Tcl_FSSplitPath(objv[2], NULL));
1304 Tcl_WrongNumArgs(interp, 1, objv, "stat name varName");
1307 if (GetStatBuf(interp, objv[2], Tcl_FSStat, &buf) != TCL_OK) {
1310 varName = Tcl_GetString(objv[3]);
1311 return StoreStatData(interp, varName, &buf);
1318 fsInfo = Tcl_FSFileSystemInfo(objv[2]);
1319 if (fsInfo != NULL) {
1320 Tcl_SetObjResult(interp, fsInfo);
1323 Tcl_SetObjResult(interp,
1324 Tcl_NewStringObj("Unrecognised path",-1));
1336 * The behaviour we want here is slightly different to
1337 * the standard Tcl_FSSplitPath in the handling of home
1338 * directories; Tcl_FSSplitPath preserves the "~" while
1339 * this code computes the actual full path name, if we
1340 * had just a single component.
1342 splitPtr = Tcl_FSSplitPath(objv[2], &splitElements);
1343 if ((splitElements == 1) && (Tcl_GetString(objv[2])[0] == '~')) {
1344 Tcl_DecrRefCount(splitPtr);
1345 splitPtr = Tcl_FSGetNormalizedPath(interp, objv[2]);
1346 if (splitPtr == NULL) {
1349 splitPtr = Tcl_FSSplitPath(splitPtr, &splitElements);
1353 * Return the last component, unless it is the only component,
1354 * and it is the root of an absolute path.
1357 if (splitElements > 0) {
1358 if ((splitElements > 1)
1359 || (Tcl_FSGetPathType(objv[2]) == TCL_PATH_RELATIVE)) {
1361 Tcl_Obj *tail = NULL;
1362 Tcl_ListObjIndex(NULL, splitPtr, splitElements-1, &tail);
1363 Tcl_SetObjResult(interp, tail);
1366 Tcl_DecrRefCount(splitPtr);
1375 if (GetStatBuf(interp, objv[2], Tcl_FSLstat, &buf) != TCL_OK) {
1378 Tcl_SetStringObj(Tcl_GetObjResult(interp),
1379 GetTypeFromMode((unsigned short) buf.st_mode), -1);
1382 case FCMD_VOLUMES: {
1384 Tcl_WrongNumArgs(interp, 2, objv, NULL);
1387 Tcl_SetObjResult(interp, Tcl_FSListVolumes());
1390 case FCMD_WRITABLE: {
1394 return CheckAccess(interp, objv[2], W_OK);
1399 Tcl_WrongNumArgs(interp, 2, objv, "name");
1404 *---------------------------------------------------------------------------
1408 * Utility procedure used by Tcl_FileObjCmd() to query file
1409 * attributes available through the access() system call.
1412 * Always returns TCL_OK. Sets interp's result to boolean true or
1413 * false depending on whether the file has the specified attribute.
1418 *---------------------------------------------------------------------------
1422 CheckAccess(interp, objPtr, mode)
1423 Tcl_Interp *interp; /* Interp for status return. Must not be
1425 Tcl_Obj *objPtr; /* Name of file to check. */
1426 int mode; /* Attribute to check; passed as argument to
1431 if (Tcl_FSConvertToPathType(interp, objPtr) != TCL_OK) {
1434 value = (Tcl_FSAccess(objPtr, mode) == 0);
1436 Tcl_SetBooleanObj(Tcl_GetObjResult(interp), value);
1442 *---------------------------------------------------------------------------
1446 * Utility procedure used by Tcl_FileObjCmd() to query file
1447 * attributes available through the stat() or lstat() system call.
1450 * The return value is TCL_OK if the specified file exists and can
1451 * be stat'ed, TCL_ERROR otherwise. If TCL_ERROR is returned, an
1452 * error message is left in interp's result. If TCL_OK is returned,
1453 * *statPtr is filled with information about the specified file.
1458 *---------------------------------------------------------------------------
1462 GetStatBuf(interp, objPtr, statProc, statPtr)
1463 Tcl_Interp *interp; /* Interp for error return. May be NULL. */
1464 Tcl_Obj *objPtr; /* Path name to examine. */
1465 Tcl_FSStatProc *statProc; /* Either stat() or lstat() depending on
1466 * desired behavior. */
1467 Tcl_StatBuf *statPtr; /* Filled with info about file obtained by
1468 * calling (*statProc)(). */
1472 if (Tcl_FSConvertToPathType(interp, objPtr) != TCL_OK) {
1476 status = (*statProc)(objPtr, statPtr);
1479 if (interp != NULL) {
1480 Tcl_AppendResult(interp, "could not read \"",
1481 Tcl_GetString(objPtr), "\": ",
1482 Tcl_PosixError(interp), (char *) NULL);
1490 *----------------------------------------------------------------------
1494 * This is a utility procedure that breaks out the fields of a
1495 * "stat" structure and stores them in textual form into the
1496 * elements of an associative array.
1499 * Returns a standard Tcl return value. If an error occurs then
1500 * a message is left in interp's result.
1503 * Elements of the associative array given by "varName" are modified.
1505 *----------------------------------------------------------------------
1509 StoreStatData(interp, varName, statPtr)
1510 Tcl_Interp *interp; /* Interpreter for error reports. */
1511 char *varName; /* Name of associative array variable
1512 * in which to store stat results. */
1513 Tcl_StatBuf *statPtr; /* Pointer to buffer containing
1514 * stat data to store in varName. */
1516 Tcl_Obj *var = Tcl_NewStringObj(varName, -1);
1517 Tcl_Obj *field = Tcl_NewObj();
1519 register unsigned short mode;
1522 * Assume Tcl_ObjSetVar2() does not keep a copy of the field name!
1524 #define STORE_ARY(fieldName, object) \
1525 Tcl_SetStringObj(field, (fieldName), -1); \
1527 if (Tcl_ObjSetVar2(interp,var,field,value,TCL_LEAVE_ERR_MSG) == NULL) { \
1528 Tcl_DecrRefCount(var); \
1529 Tcl_DecrRefCount(field); \
1530 Tcl_DecrRefCount(value); \
1534 Tcl_IncrRefCount(var);
1535 Tcl_IncrRefCount(field);
1536 STORE_ARY("dev", Tcl_NewLongObj((long)statPtr->st_dev));
1538 * Watch out porters; the inode is meant to be an *unsigned* value,
1539 * so the cast might fail when there isn't a real arithmentic 'long
1542 STORE_ARY("ino", Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_ino));
1543 STORE_ARY("nlink", Tcl_NewLongObj((long)statPtr->st_nlink));
1544 STORE_ARY("uid", Tcl_NewLongObj((long)statPtr->st_uid));
1545 STORE_ARY("gid", Tcl_NewLongObj((long)statPtr->st_gid));
1546 STORE_ARY("size", Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_size));
1547 #ifdef HAVE_ST_BLOCKS
1548 STORE_ARY("blocks",Tcl_NewWideIntObj((Tcl_WideInt)statPtr->st_blocks));
1550 STORE_ARY("atime", Tcl_NewLongObj((long)statPtr->st_atime));
1551 STORE_ARY("mtime", Tcl_NewLongObj((long)statPtr->st_mtime));
1552 STORE_ARY("ctime", Tcl_NewLongObj((long)statPtr->st_ctime));
1553 mode = (unsigned short) statPtr->st_mode;
1554 STORE_ARY("mode", Tcl_NewIntObj(mode));
1555 STORE_ARY("type", Tcl_NewStringObj(GetTypeFromMode(mode), -1));
1557 Tcl_DecrRefCount(var);
1558 Tcl_DecrRefCount(field);
1563 *----------------------------------------------------------------------
1565 * GetTypeFromMode --
1567 * Given a mode word, returns a string identifying the type of a
1571 * A static text string giving the file type from mode.
1576 *----------------------------------------------------------------------
1580 GetTypeFromMode(mode)
1583 if (S_ISREG(mode)) {
1585 } else if (S_ISDIR(mode)) {
1587 } else if (S_ISCHR(mode)) {
1588 return "characterSpecial";
1589 } else if (S_ISBLK(mode)) {
1590 return "blockSpecial";
1591 } else if (S_ISFIFO(mode)) {
1594 } else if (S_ISLNK(mode)) {
1598 } else if (S_ISSOCK(mode)) {
1606 *----------------------------------------------------------------------
1610 * This procedure is invoked to process the "for" Tcl command.
1611 * See the user documentation for details on what it does.
1613 * With the bytecode compiler, this procedure is only called when
1614 * a command name is computed at runtime, and is "for" or the name
1615 * to which "for" was renamed: e.g.,
1616 * "set z for; $z {set i 0} {$i<100} {incr i} {puts $i}"
1619 * A standard Tcl result.
1622 * See the user documentation.
1624 *----------------------------------------------------------------------
1629 Tcl_ForObjCmd(dummy, interp, objc, objv)
1630 ClientData dummy; /* Not used. */
1631 Tcl_Interp *interp; /* Current interpreter. */
1632 int objc; /* Number of arguments. */
1633 Tcl_Obj *CONST objv[]; /* Argument objects. */
1637 Interp* iPtr = (Interp*) interp;
1641 Tcl_WrongNumArgs(interp, 1, objv, "start test next command");
1646 result = Tcl_EvalObjEx(interp, objv[1], 0);
1648 /* TIP #280. Make invoking context available to initial script */
1649 result = TclEvalObjEx(interp, objv[1], 0, iPtr->cmdFramePtr,1);
1651 if (result != TCL_OK) {
1652 if (result == TCL_ERROR) {
1653 Tcl_AddErrorInfo(interp, "\n (\"for\" initial command)");
1659 * We need to reset the result before passing it off to
1660 * Tcl_ExprBooleanObj. Otherwise, any error message will be appended
1661 * to the result of the last evaluation.
1664 Tcl_ResetResult(interp);
1665 result = Tcl_ExprBooleanObj(interp, objv[2], &value);
1666 if (result != TCL_OK) {
1673 result = Tcl_EvalObjEx(interp, objv[4], 0);
1675 /* TIP #280. Make invoking context available to loop body */
1676 result = TclEvalObjEx(interp, objv[4], 0, iPtr->cmdFramePtr,4);
1678 if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
1679 if (result == TCL_ERROR) {
1680 char msg[32 + TCL_INTEGER_SPACE];
1682 sprintf(msg, "\n (\"for\" body line %d)",interp->errorLine);
1683 Tcl_AddErrorInfo(interp, msg);
1688 result = Tcl_EvalObjEx(interp, objv[3], 0);
1690 /* TIP #280. Make invoking context available to next script */
1691 result = TclEvalObjEx(interp, objv[3], 0, iPtr->cmdFramePtr,3);
1693 if (result == TCL_BREAK) {
1695 } else if (result != TCL_OK) {
1696 if (result == TCL_ERROR) {
1697 Tcl_AddErrorInfo(interp, "\n (\"for\" loop-end command)");
1702 if (result == TCL_BREAK) {
1705 if (result == TCL_OK) {
1706 Tcl_ResetResult(interp);
1712 *----------------------------------------------------------------------
1714 * Tcl_ForeachObjCmd --
1716 * This object-based procedure is invoked to process the "foreach" Tcl
1717 * command. See the user documentation for details on what it does.
1720 * A standard Tcl object result.
1723 * See the user documentation.
1725 *----------------------------------------------------------------------
1730 Tcl_ForeachObjCmd(dummy, interp, objc, objv)
1731 ClientData dummy; /* Not used. */
1732 Tcl_Interp *interp; /* Current interpreter. */
1733 int objc; /* Number of arguments. */
1734 Tcl_Obj *CONST objv[]; /* Argument objects. */
1736 int result = TCL_OK;
1737 int i; /* i selects a value list */
1738 int j, maxj; /* Number of loop iterations */
1739 int v; /* v selects a loop variable */
1740 int numLists; /* Count of value lists */
1744 * We copy the argument object pointers into a local array to avoid
1745 * the problem that "objv" might become invalid. It is a pointer into
1746 * the evaluation stack and that stack might be grown and reallocated
1747 * if the loop body requires a large amount of stack space.
1751 Tcl_Obj *(argObjStorage[NUM_ARGS]);
1752 Tcl_Obj **argObjv = argObjStorage;
1754 #define STATIC_LIST_SIZE 4
1755 int indexArray[STATIC_LIST_SIZE];
1756 int varcListArray[STATIC_LIST_SIZE];
1757 Tcl_Obj **varvListArray[STATIC_LIST_SIZE];
1758 int argcListArray[STATIC_LIST_SIZE];
1759 Tcl_Obj **argvListArray[STATIC_LIST_SIZE];
1761 int *index = indexArray; /* Array of value list indices */
1762 int *varcList = varcListArray; /* # loop variables per list */
1763 Tcl_Obj ***varvList = varvListArray; /* Array of var name lists */
1764 int *argcList = argcListArray; /* Array of value list sizes */
1765 Tcl_Obj ***argvList = argvListArray; /* Array of value lists */
1767 Interp* iPtr = (Interp*) interp;
1770 if (objc < 4 || (objc%2 != 0)) {
1771 Tcl_WrongNumArgs(interp, 1, objv,
1772 "varList list ?varList list ...? command");
1777 * Create the object argument array "argObjv". Make sure argObjv is
1778 * large enough to hold the objc arguments.
1781 if (objc > NUM_ARGS) {
1782 argObjv = (Tcl_Obj **) ckalloc(objc * sizeof(Tcl_Obj *));
1784 for (i = 0; i < objc; i++) {
1785 argObjv[i] = objv[i];
1789 * Manage numList parallel value lists.
1790 * argvList[i] is a value list counted by argcList[i]
1791 * varvList[i] is the list of variables associated with the value list
1792 * varcList[i] is the number of variables associated with the value list
1793 * index[i] is the current pointer into the value list argvList[i]
1796 numLists = (objc-2)/2;
1797 if (numLists > STATIC_LIST_SIZE) {
1798 index = (int *) ckalloc(numLists * sizeof(int));
1799 varcList = (int *) ckalloc(numLists * sizeof(int));
1800 varvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
1801 argcList = (int *) ckalloc(numLists * sizeof(int));
1802 argvList = (Tcl_Obj ***) ckalloc(numLists * sizeof(Tcl_Obj **));
1804 for (i = 0; i < numLists; i++) {
1807 varvList[i] = (Tcl_Obj **) NULL;
1809 argvList[i] = (Tcl_Obj **) NULL;
1813 * Break up the value lists and variable lists into elements
1817 for (i = 0; i < numLists; i++) {
1818 result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
1819 &varcList[i], &varvList[i]);
1820 if (result != TCL_OK) {
1823 if (varcList[i] < 1) {
1824 Tcl_AppendToObj(Tcl_GetObjResult(interp),
1825 "foreach varlist is empty", -1);
1830 result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
1831 &argcList[i], &argvList[i]);
1832 if (result != TCL_OK) {
1836 j = argcList[i] / varcList[i];
1837 if ((argcList[i] % varcList[i]) != 0) {
1846 * Iterate maxj times through the lists in parallel
1847 * If some value lists run out of values, set loop vars to ""
1850 bodyPtr = argObjv[objc-1];
1851 for (j = 0; j < maxj; j++) {
1852 for (i = 0; i < numLists; i++) {
1854 * Refetch the list members; we assume that the sizes are
1855 * the same, but the array of elements might be different
1856 * if the internal rep of the objects has been lost and
1857 * recreated (it is too difficult to accurately tell when
1858 * this happens, which can lead to some wierd crashes,
1859 * like Bug #494348...)
1862 result = Tcl_ListObjGetElements(interp, argObjv[1+i*2],
1863 &varcList[i], &varvList[i]);
1864 if (result != TCL_OK) {
1865 panic("Tcl_ForeachObjCmd: could not reconvert variable list %d to a list object\n", i);
1867 result = Tcl_ListObjGetElements(interp, argObjv[2+i*2],
1868 &argcList[i], &argvList[i]);
1869 if (result != TCL_OK) {
1870 panic("Tcl_ForeachObjCmd: could not reconvert value list %d to a list object\n", i);
1873 for (v = 0; v < varcList[i]; v++) {
1875 Tcl_Obj *valuePtr, *varValuePtr;
1877 if (k < argcList[i]) {
1878 valuePtr = argvList[i][k];
1880 valuePtr = Tcl_NewObj(); /* empty string */
1882 Tcl_IncrRefCount(valuePtr);
1883 varValuePtr = Tcl_ObjSetVar2(interp, varvList[i][v],
1885 Tcl_DecrRefCount(valuePtr);
1886 if (varValuePtr == NULL) {
1887 Tcl_ResetResult(interp);
1888 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1889 "couldn't set loop variable: \"",
1890 Tcl_GetString(varvList[i][v]), "\"", (char *) NULL);
1899 result = Tcl_EvalObjEx(interp, bodyPtr, 0);
1901 /* TIP #280. Make invoking context available to loop body */
1902 result = TclEvalObjEx(interp, bodyPtr, 0, iPtr->cmdFramePtr,objc-1);
1904 if (result != TCL_OK) {
1905 if (result == TCL_CONTINUE) {
1907 } else if (result == TCL_BREAK) {
1910 } else if (result == TCL_ERROR) {
1911 char msg[32 + TCL_INTEGER_SPACE];
1913 sprintf(msg, "\n (\"foreach\" body line %d)",
1915 Tcl_AddObjErrorInfo(interp, msg, -1);
1922 if (result == TCL_OK) {
1923 Tcl_ResetResult(interp);
1927 if (numLists > STATIC_LIST_SIZE) {
1928 ckfree((char *) index);
1929 ckfree((char *) varcList);
1930 ckfree((char *) argcList);
1931 ckfree((char *) varvList);
1932 ckfree((char *) argvList);
1934 if (argObjv != argObjStorage) {
1935 ckfree((char *) argObjv);
1938 #undef STATIC_LIST_SIZE
1943 *----------------------------------------------------------------------
1945 * Tcl_FormatObjCmd --
1947 * This procedure is invoked to process the "format" Tcl command.
1948 * See the user documentation for details on what it does.
1951 * A standard Tcl result.
1954 * See the user documentation.
1956 *----------------------------------------------------------------------
1961 Tcl_FormatObjCmd(dummy, interp, objc, objv)
1962 ClientData dummy; /* Not used. */
1963 Tcl_Interp *interp; /* Current interpreter. */
1964 int objc; /* Number of arguments. */
1965 Tcl_Obj *CONST objv[]; /* Argument objects. */
1967 char *format; /* Used to read characters from the format
1969 int formatLen; /* The length of the format string */
1970 char *endPtr; /* Points to the last char in format array */
1971 char newFormat[43]; /* A new format specifier is generated here. */
1972 int width; /* Field width from field specifier, or 0 if
1973 * no width given. */
1974 int precision; /* Field precision from field specifier, or 0
1975 * if no precision given. */
1976 int size; /* Number of bytes needed for result of
1977 * conversion, based on type of conversion
1978 * ("e", "s", etc.), width, and precision. */
1979 long intValue; /* Used to hold value to pass to sprintf, if
1980 * it's a one-word integer or char value */
1981 char *ptrValue = NULL; /* Used to hold value to pass to sprintf, if
1982 * it's a one-word value. */
1983 double doubleValue; /* Used to hold value to pass to sprintf if
1984 * it's a double value. */
1985 Tcl_WideInt wideValue; /* Used to hold value to pass to sprintf if
1986 * it's a 'long long' value. */
1987 int whichValue; /* Indicates which of intValue, ptrValue,
1988 * or doubleValue has the value to pass to
1989 * sprintf, according to the following
1991 # define INT_VALUE 0
1992 # define CHAR_VALUE 1
1993 # define PTR_VALUE 2
1994 # define DOUBLE_VALUE 3
1995 # define STRING_VALUE 4
1996 # define WIDE_VALUE 5
1997 # define MAX_FLOAT_SIZE 320
1999 Tcl_Obj *resultPtr; /* Where result is stored finally. */
2000 char staticBuf[MAX_FLOAT_SIZE + 1];
2001 /* A static buffer to copy the format results
2003 char *dst = staticBuf; /* The buffer that sprintf writes into each
2004 * time the format processes a specifier */
2005 int dstSize = MAX_FLOAT_SIZE;
2006 /* The size of the dst buffer */
2007 int noPercent; /* Special case for speed: indicates there's
2008 * no field specifier, just a string to copy.*/
2009 int objIndex; /* Index of argument to substitute next. */
2010 int gotXpg = 0; /* Non-zero means that an XPG3 %n$-style
2011 * specifier has been seen. */
2012 int gotSequential = 0; /* Non-zero means that a regular sequential
2013 * (non-XPG3) conversion specifier has been
2015 int useShort; /* Value to be printed is short (half word). */
2016 char *end; /* Used to locate end of numerical fields. */
2017 int stringLen = 0; /* Length of string in characters rather
2018 * than bytes. Used for %s substitution. */
2019 int gotMinus; /* Non-zero indicates that a minus flag has
2020 * been seen in the current field. */
2021 int gotPrecision; /* Non-zero indicates that a precision has
2022 * been set for the current field. */
2023 int gotZero; /* Non-zero indicates that a zero flag has
2024 * been seen in the current field. */
2025 int useWide; /* Value to be printed is Tcl_WideInt. */
2028 * This procedure is a bit nasty. The goal is to use sprintf to
2029 * do most of the dirty work. There are several problems:
2030 * 1. this procedure can't trust its arguments.
2031 * 2. we must be able to provide a large enough result area to hold
2032 * whatever's generated. This is hard to estimate.
2033 * 3. there's no way to move the arguments from objv to the call
2034 * to sprintf in a reasonable way. This is particularly nasty
2035 * because some of the arguments may be two-word values (doubles
2037 * So, what happens here is to scan the format string one % group
2038 * at a time, making many individual calls to sprintf.
2042 Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg arg ...?");
2046 format = Tcl_GetStringFromObj(objv[1], &formatLen);
2047 endPtr = format + formatLen;
2048 resultPtr = Tcl_NewObj();
2051 while (format < endPtr) {
2052 register char *newPtr = newFormat;
2054 width = precision = noPercent = useShort = 0;
2055 gotZero = gotMinus = gotPrecision = 0;
2057 whichValue = PTR_VALUE;
2060 * Get rid of any characters before the next field specifier.
2062 if (*format != '%') {
2064 while ((*format != '%') && (format < endPtr)) {
2067 size = format - ptrValue;
2072 if (format[1] == '%') {
2081 * Parse off a field specifier, compute how many characters
2082 * will be needed to store the result, and substitute for
2083 * "*" size specifiers.
2088 if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
2092 * Check for an XPG3-style %n$ specification. Note: there
2093 * must not be a mixture of XPG3 specs and non-XPG3 specs
2094 * in the same format string.
2097 tmp = strtoul(format, &end, 10); /* INTL: "C" locale. */
2103 if (gotSequential) {
2107 if ((objIndex < 2) || (objIndex >= objc)) {
2120 while ((*format == '-') || (*format == '#') || (*format == '0')
2121 || (*format == ' ') || (*format == '+')) {
2122 if (*format == '-') {
2125 if (*format == '0') {
2127 * This will be handled by sprintf for numbers, but we
2128 * need to do the char/string ones ourselves
2136 if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
2137 width = strtoul(format, &end, 10); /* INTL: Tcl source. */
2139 } else if (*format == '*') {
2140 if (objIndex >= objc) {
2143 if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
2144 objv[objIndex], &width) != TCL_OK) {
2156 if (width > 100000) {
2158 * Don't allow arbitrarily large widths: could cause core
2159 * dump when we try to allocate a zillion bytes of memory
2164 } else if (width < 0) {
2168 TclFormatInt(newPtr, width); /* INTL: printf format. */
2169 while (*newPtr != 0) {
2173 if (*format == '.') {
2179 if (isdigit(UCHAR(*format))) { /* INTL: Tcl source. */
2180 precision = strtoul(format, &end, 10); /* INTL: "C" locale. */
2182 } else if (*format == '*') {
2183 if (objIndex >= objc) {
2186 if (Tcl_GetIntFromObj(interp, /* INTL: Tcl source. */
2187 objv[objIndex], &precision) != TCL_OK) {
2194 TclFormatInt(newPtr, precision); /* INTL: printf format. */
2195 while (*newPtr != 0) {
2199 if (*format == 'l') {
2202 * Only add a 'll' modifier for integer values as it makes
2203 * some libc's go into spasm otherwise. [Bug #702622]
2205 switch (format[1]) {
2212 strcpy(newPtr, TCL_LL_MODIFIER);
2213 newPtr += TCL_LL_MODIFIER_SIZE;
2216 } else if (*format == 'h') {
2225 if (objIndex >= objc) {
2237 if (Tcl_GetWideIntFromObj(interp, /* INTL: Tcl source. */
2238 objv[objIndex], &wideValue) != TCL_OK) {
2241 whichValue = WIDE_VALUE;
2242 size = 40 + precision;
2245 if (Tcl_GetLongFromObj(interp, /* INTL: Tcl source. */
2246 objv[objIndex], &intValue) != TCL_OK) {
2247 if (Tcl_GetWideIntFromObj(interp, /* INTL: Tcl source. */
2248 objv[objIndex], &wideValue) != TCL_OK) {
2251 intValue = Tcl_WideAsLong(wideValue);
2254 #if (LONG_MAX > INT_MAX)
2257 * Add the 'l' for long format type because we are on an
2258 * LP64 archtecture and we are really going to pass a long
2259 * argument to sprintf.
2261 * Do not add this if we're going to pass in a short (i.e.
2262 * if we've got an 'h' modifier already in the string); some
2263 * libc implementations of sprintf() do not like it at all.
2268 newPtr[-1] = newPtr[-2];
2271 #endif /* LONG_MAX > INT_MAX */
2272 whichValue = INT_VALUE;
2273 size = 40 + precision;
2277 * Compute the length of the string in characters and add
2278 * any additional space required by the field width. All
2279 * of the extra characters will be spaces, so one byte per
2280 * character is adequate.
2283 whichValue = STRING_VALUE;
2284 ptrValue = Tcl_GetStringFromObj(objv[objIndex], &size);
2285 stringLen = Tcl_NumUtfChars(ptrValue, size);
2286 if (gotPrecision && (precision < stringLen)) {
2287 stringLen = precision;
2289 size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue;
2290 if (width > stringLen) {
2291 size += (width - stringLen);
2295 if (Tcl_GetLongFromObj(interp, /* INTL: Tcl source. */
2296 objv[objIndex], &intValue) != TCL_OK) {
2299 whichValue = CHAR_VALUE;
2300 size = width + TCL_UTF_MAX;
2307 if (Tcl_GetDoubleFromObj(interp, /* INTL: Tcl source. */
2308 objv[objIndex], &doubleValue) != TCL_OK) {
2311 whichValue = DOUBLE_VALUE;
2312 size = MAX_FLOAT_SIZE;
2313 if (precision > 10) {
2318 Tcl_SetResult(interp,
2319 "format string ended in middle of field specifier",
2326 sprintf(buf, "bad field specifier \"%c\"", *format);
2327 Tcl_SetResult(interp, buf, TCL_VOLATILE);
2335 * Make sure that there's enough space to hold the formatted
2336 * result, then format it.
2344 Tcl_AppendToObj(resultPtr, ptrValue, size);
2346 if (size > dstSize) {
2347 if (dst != staticBuf) {
2350 dst = (char *) ckalloc((unsigned) (size + 1));
2353 switch (whichValue) {
2355 sprintf(dst, newFormat, doubleValue); /* INTL: user locale. */
2358 sprintf(dst, newFormat, wideValue);
2362 sprintf(dst, newFormat, (short) intValue);
2364 sprintf(dst, newFormat, intValue);
2369 char padChar = (gotZero ? '0' : ' ');
2372 for ( ; --width > 0; ptr++) {
2376 ptr += Tcl_UniCharToUtf(intValue, ptr);
2377 for ( ; --width > 0; ptr++) {
2383 case STRING_VALUE: {
2385 char padChar = (gotZero ? '0' : ' ');
2389 if (width > stringLen) {
2390 pad = width - stringLen;
2402 size = Tcl_UtfAtIndex(ptrValue, stringLen) - ptrValue;
2404 memcpy(ptr, ptrValue, (size_t) size);
2415 sprintf(dst, newFormat, ptrValue);
2418 Tcl_AppendToObj(resultPtr, dst, -1);
2422 Tcl_SetObjResult(interp, resultPtr);
2423 if (dst != staticBuf) {
2429 Tcl_SetResult(interp,
2430 "cannot mix \"%\" and \"%n$\" conversion specifiers", TCL_STATIC);
2435 Tcl_SetResult(interp,
2436 "\"%n$\" argument index out of range", TCL_STATIC);
2438 Tcl_SetResult(interp,
2439 "not enough arguments for all format specifiers", TCL_STATIC);
2443 if (dst != staticBuf) {
2446 Tcl_DecrRefCount(resultPtr);