4 * This file contains a collection of utility procedures that
5 * are shared by the platform specific IO drivers.
7 * Parts of this file are based on code contributed by Karl
8 * Lehenbauer, Mark Diekhans and Peter da Silva.
10 * Copyright (c) 1991-1994 The Regents of the University of California.
11 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
13 * See the file "license.terms" for information on usage and redistribution
14 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
23 * The following typedef declarations allow for hooking into the chain
24 * of functions maintained for 'Tcl_Stat(...)', 'Tcl_Access(...)' &
25 * 'Tcl_OpenFileChannel(...)'. Basically for each hookable function
26 * a linked list is defined.
29 typedef struct StatProc {
30 TclStatProc_ *proc; /* Function to process a 'stat()' call */
31 struct StatProc *nextPtr; /* The next 'stat()' function to call */
34 typedef struct AccessProc {
35 TclAccessProc_ *proc; /* Function to process a 'access()' call */
36 struct AccessProc *nextPtr; /* The next 'access()' function to call */
39 typedef struct OpenFileChannelProc {
40 TclOpenFileChannelProc_ *proc; /* Function to process a
41 * 'Tcl_OpenFileChannel()' call */
42 struct OpenFileChannelProc *nextPtr;
43 /* The next 'Tcl_OpenFileChannel()'
45 } OpenFileChannelProc;
48 * For each type of hookable function, a static node is declared to
49 * hold the function pointer for the "built-in" routine (e.g.
50 * 'TclpStat(...)') and the respective list is initialized as a pointer
53 * The "delete" functions (e.g. 'TclStatDeleteProc(...)') ensure that
54 * these statically declared list entry cannot be inadvertently removed.
56 * This method avoids the need to call any sort of "initialization"
59 * All three lists are protected by a global hookMutex.
62 static StatProc defaultStatProc = {
65 static StatProc *statProcList = &defaultStatProc;
67 static AccessProc defaultAccessProc = {
70 static AccessProc *accessProcList = &defaultAccessProc;
72 static OpenFileChannelProc defaultOpenFileChannelProc = {
73 &TclpOpenFileChannel, NULL
75 static OpenFileChannelProc *openFileChannelProcList =
76 &defaultOpenFileChannelProc;
78 TCL_DECLARE_MUTEX(hookMutex)
81 *---------------------------------------------------------------------------
86 * Computes a POSIX mode mask for opening a file, from a given string,
87 * and also sets a flag to indicate whether the caller should seek to
88 * EOF after opening the file.
91 * On success, returns mode to pass to "open". If an error occurs, the
92 * return value is -1 and if interp is not NULL, sets interp's result
93 * object to an error message.
96 * Sets the integer referenced by seekFlagPtr to 1 to tell the caller
97 * to seek to EOF after opening the file.
100 * This code is based on a prototype implementation contributed
103 *---------------------------------------------------------------------------
107 TclGetOpenMode(interp, string, seekFlagPtr)
108 Tcl_Interp *interp; /* Interpreter to use for error
109 * reporting - may be NULL. */
110 char *string; /* Mode string, e.g. "r+" or
112 int *seekFlagPtr; /* Set this to 1 if the caller
113 * should seek to EOF during the
114 * opening of the file. */
116 int mode, modeArgc, c, i, gotRW;
117 char **modeArgv, *flag;
118 #define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR)
121 * Check for the simpler fopen-like access modes (e.g. "r"). They
122 * are distinguished from the POSIX access modes by the presence
123 * of a lower-case first letter.
130 * Guard against international characters before using byte oriented
134 if (!(string[0] & 0x80)
135 && islower(UCHAR(string[0]))) { /* INTL: ISO only. */
141 mode = O_WRONLY|O_CREAT|O_TRUNC;
144 mode = O_WRONLY|O_CREAT;
149 if (interp != (Tcl_Interp *) NULL) {
150 Tcl_AppendResult(interp,
151 "illegal access mode \"", string, "\"",
156 if (string[1] == '+') {
157 mode &= ~(O_RDONLY|O_WRONLY);
159 if (string[2] != 0) {
162 } else if (string[1] != 0) {
169 * The access modes are specified using a list of POSIX modes
172 * IMPORTANT NOTE: We rely on Tcl_SplitList working correctly when
173 * a NULL interpreter is passed in.
176 if (Tcl_SplitList(interp, string, &modeArgc, &modeArgv) != TCL_OK) {
177 if (interp != (Tcl_Interp *) NULL) {
178 Tcl_AddErrorInfo(interp,
179 "\n while processing open access modes \"");
180 Tcl_AddErrorInfo(interp, string);
181 Tcl_AddErrorInfo(interp, "\"");
187 for (i = 0; i < modeArgc; i++) {
190 if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) {
191 mode = (mode & ~RW_MODES) | O_RDONLY;
193 } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) {
194 mode = (mode & ~RW_MODES) | O_WRONLY;
196 } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) {
197 mode = (mode & ~RW_MODES) | O_RDWR;
199 } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) {
202 } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) {
204 } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) {
206 } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) {
210 if (interp != (Tcl_Interp *) NULL) {
211 Tcl_AppendResult(interp, "access mode \"", flag,
212 "\" not supported by this system", (char *) NULL);
214 ckfree((char *) modeArgv);
217 } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) {
218 #if defined(O_NDELAY) || defined(O_NONBLOCK)
225 if (interp != (Tcl_Interp *) NULL) {
226 Tcl_AppendResult(interp, "access mode \"", flag,
227 "\" not supported by this system", (char *) NULL);
229 ckfree((char *) modeArgv);
232 } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) {
235 if (interp != (Tcl_Interp *) NULL) {
236 Tcl_AppendResult(interp, "invalid access mode \"", flag,
237 "\": must be RDONLY, WRONLY, RDWR, APPEND, CREAT",
238 " EXCL, NOCTTY, NONBLOCK, or TRUNC", (char *) NULL);
240 ckfree((char *) modeArgv);
244 ckfree((char *) modeArgv);
246 if (interp != (Tcl_Interp *) NULL) {
247 Tcl_AppendResult(interp, "access mode must include either",
248 " RDONLY, WRONLY, or RDWR", (char *) NULL);
256 *----------------------------------------------------------------------
260 * Read in a file and process the entire file as one gigantic
264 * A standard Tcl result, which is either the result of executing
265 * the file or an error indicating why the file couldn't be read.
268 * Depends on the commands in the file.
270 *----------------------------------------------------------------------
274 Tcl_EvalFile(interp, fileName)
275 Tcl_Interp *interp; /* Interpreter in which to process file. */
276 char *fileName; /* Name of file to process. Tilde-substitution
277 * will be performed on this name. */
283 Tcl_DString nameString;
288 name = Tcl_TranslateFileName(interp, fileName, &nameString);
294 objPtr = Tcl_NewObj();
296 if (TclStat(name, &statBuf) == -1) {
298 Tcl_AppendResult(interp, "couldn't read file \"", fileName,
299 "\": ", Tcl_PosixError(interp), (char *) NULL);
302 chan = Tcl_OpenFileChannel(interp, name, "r", 0644);
303 if (chan == (Tcl_Channel) NULL) {
304 Tcl_ResetResult(interp);
305 Tcl_AppendResult(interp, "couldn't read file \"", fileName,
306 "\": ", Tcl_PosixError(interp), (char *) NULL);
309 if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) {
310 Tcl_Close(interp, chan);
311 Tcl_AppendResult(interp, "couldn't read file \"", fileName,
312 "\": ", Tcl_PosixError(interp), (char *) NULL);
315 if (Tcl_Close(interp, chan) != TCL_OK) {
319 iPtr = (Interp *) interp;
320 oldScriptFile = iPtr->scriptFile;
321 iPtr->scriptFile = fileName;
322 string = Tcl_GetStringFromObj(objPtr, &length);
323 result = Tcl_EvalEx(interp, string, length, 0);
324 iPtr->scriptFile = oldScriptFile;
326 if (result == TCL_RETURN) {
327 result = TclUpdateReturnInfo(iPtr);
328 } else if (result == TCL_ERROR) {
329 char msg[200 + TCL_INTEGER_SPACE];
332 * Record information telling where the error occurred.
335 sprintf(msg, "\n (file \"%.150s\" line %d)", fileName,
337 Tcl_AddErrorInfo(interp, msg);
341 Tcl_DecrRefCount(objPtr);
342 Tcl_DStringFree(&nameString);
347 *----------------------------------------------------------------------
351 * Gets the current value of the Tcl error code variable. This is
352 * currently the global variable "errno" but could in the future
353 * change to something else.
356 * The value of the Tcl error code variable.
359 * None. Note that the value of the Tcl error code variable is
360 * UNDEFINED if a call to Tcl_SetErrno did not precede this call.
362 *----------------------------------------------------------------------
372 *----------------------------------------------------------------------
376 * Sets the Tcl error code variable to the supplied value.
382 * Modifies the value of the Tcl error code variable.
384 *----------------------------------------------------------------------
389 int err; /* The new value. */
395 *----------------------------------------------------------------------
399 * This procedure is typically called after UNIX kernel calls
400 * return errors. It stores machine-readable information about
401 * the error in $errorCode returns an information string for
405 * The return value is a human-readable string describing the
409 * The global variable $errorCode is reset.
411 *----------------------------------------------------------------------
415 Tcl_PosixError(interp)
416 Tcl_Interp *interp; /* Interpreter whose $errorCode variable
417 * is to be changed. */
421 msg = Tcl_ErrnoMsg(errno);
423 Tcl_SetErrorCode(interp, "POSIX", id, msg, (char *) NULL);
428 *----------------------------------------------------------------------
432 * This procedure replaces the library version of stat and lsat.
433 * The chain of functions that have been "inserted" into the
434 * 'statProcList' will be called in succession until either
435 * a value of zero is returned, or the entire list is visited.
438 * See stat documentation.
441 * See stat documentation.
443 *----------------------------------------------------------------------
448 CONST char *path; /* Path of file to stat (in current CP). */
449 struct stat *buf; /* Filled with results of stat call. */
451 StatProc *statProcPtr;
455 * Call each of the "stat" function in succession. A non-return
456 * value of -1 indicates the particular function has succeeded.
459 Tcl_MutexLock(&hookMutex);
460 statProcPtr = statProcList;
461 while ((retVal == -1) && (statProcPtr != NULL)) {
462 retVal = (*statProcPtr->proc)(path, buf);
463 statProcPtr = statProcPtr->nextPtr;
465 Tcl_MutexUnlock(&hookMutex);
471 *----------------------------------------------------------------------
475 * This procedure replaces the library version of access.
476 * The chain of functions that have been "inserted" into the
477 * 'accessProcList' will be called in succession until either
478 * a value of zero is returned, or the entire list is visited.
481 * See access documentation.
484 * See access documentation.
486 *----------------------------------------------------------------------
490 TclAccess(path, mode)
491 CONST char *path; /* Path of file to access (in current CP). */
492 int mode; /* Permission setting. */
494 AccessProc *accessProcPtr;
498 * Call each of the "access" function in succession. A non-return
499 * value of -1 indicates the particular function has succeeded.
502 Tcl_MutexLock(&hookMutex);
503 accessProcPtr = accessProcList;
504 while ((retVal == -1) && (accessProcPtr != NULL)) {
505 retVal = (*accessProcPtr->proc)(path, mode);
506 accessProcPtr = accessProcPtr->nextPtr;
508 Tcl_MutexUnlock(&hookMutex);
514 *----------------------------------------------------------------------
516 * Tcl_OpenFileChannel --
518 * The chain of functions that have been "inserted" into the
519 * 'openFileChannelProcList' will be called in succession until
520 * either a valid file channel is returned, or the entire list is
524 * The new channel or NULL, if the named file could not be opened.
527 * May open the channel and may cause creation of a file on the
530 *----------------------------------------------------------------------
534 Tcl_OpenFileChannel(interp, fileName, modeString, permissions)
535 Tcl_Interp *interp; /* Interpreter for error reporting;
537 char *fileName; /* Name of file to open. */
538 char *modeString; /* A list of POSIX open modes or
539 * a string such as "rw". */
540 int permissions; /* If the open involves creating a
541 * file, with what modes to create
544 OpenFileChannelProc *openFileChannelProcPtr;
545 Tcl_Channel retVal = NULL;
548 * Call each of the "Tcl_OpenFileChannel" function in succession.
549 * A non-NULL return value indicates the particular function has
553 Tcl_MutexLock(&hookMutex);
554 openFileChannelProcPtr = openFileChannelProcList;
555 while ((retVal == NULL) && (openFileChannelProcPtr != NULL)) {
556 retVal = (*openFileChannelProcPtr->proc)(interp, fileName,
557 modeString, permissions);
558 openFileChannelProcPtr = openFileChannelProcPtr->nextPtr;
560 Tcl_MutexUnlock(&hookMutex);
566 *----------------------------------------------------------------------
568 * TclStatInsertProc --
570 * Insert the passed procedure pointer at the head of the list of
571 * functions which are used during a call to 'TclStat(...)'. The
572 * passed function should be have exactly like 'TclStat' when called
573 * during that time (see 'TclStat(...)' for more informatin).
574 * The function will be added even if it already in the list.
577 * Normally TCL_OK; TCL_ERROR if memory for a new node in the list
578 * could not be allocated.
581 * Memory allocataed and modifies the link list for 'TclStat'
584 *----------------------------------------------------------------------
588 TclStatInsertProc (proc)
591 int retVal = TCL_ERROR;
594 StatProc *newStatProcPtr;
596 newStatProcPtr = (StatProc *)ckalloc(sizeof(StatProc));
598 if (newStatProcPtr != NULL) {
599 newStatProcPtr->proc = proc;
600 Tcl_MutexLock(&hookMutex);
601 newStatProcPtr->nextPtr = statProcList;
602 statProcList = newStatProcPtr;
603 Tcl_MutexUnlock(&hookMutex);
613 *----------------------------------------------------------------------
615 * TclStatDeleteProc --
617 * Removed the passed function pointer from the list of 'TclStat'
618 * functions. Ensures that the built-in stat function is not
622 * TCL_OK if the procedure pointer was successfully removed,
623 * TCL_ERROR otherwise.
626 * Memory is deallocated and the respective list updated.
628 *----------------------------------------------------------------------
632 TclStatDeleteProc (proc)
635 int retVal = TCL_ERROR;
636 StatProc *tmpStatProcPtr;
637 StatProc *prevStatProcPtr = NULL;
639 Tcl_MutexLock(&hookMutex);
640 tmpStatProcPtr = statProcList;
642 * Traverse the 'statProcList' looking for the particular node
643 * whose 'proc' member matches 'proc' and remove that one from
644 * the list. Ensure that the "default" node cannot be removed.
647 while ((retVal == TCL_ERROR) && (tmpStatProcPtr != &defaultStatProc)) {
648 if (tmpStatProcPtr->proc == proc) {
649 if (prevStatProcPtr == NULL) {
650 statProcList = tmpStatProcPtr->nextPtr;
652 prevStatProcPtr->nextPtr = tmpStatProcPtr->nextPtr;
655 Tcl_Free((char *)tmpStatProcPtr);
659 prevStatProcPtr = tmpStatProcPtr;
660 tmpStatProcPtr = tmpStatProcPtr->nextPtr;
664 Tcl_MutexUnlock(&hookMutex);
669 *----------------------------------------------------------------------
671 * TclAccessInsertProc --
673 * Insert the passed procedure pointer at the head of the list of
674 * functions which are used during a call to 'TclAccess(...)'. The
675 * passed function should be have exactly like 'TclAccess' when
676 * called during that time (see 'TclAccess(...)' for more informatin).
677 * The function will be added even if it already in the list.
680 * Normally TCL_OK; TCL_ERROR if memory for a new node in the list
681 * could not be allocated.
684 * Memory allocataed and modifies the link list for 'TclAccess'
687 *----------------------------------------------------------------------
691 TclAccessInsertProc(proc)
692 TclAccessProc_ *proc;
694 int retVal = TCL_ERROR;
697 AccessProc *newAccessProcPtr;
699 newAccessProcPtr = (AccessProc *)ckalloc(sizeof(AccessProc));
701 if (newAccessProcPtr != NULL) {
702 newAccessProcPtr->proc = proc;
703 Tcl_MutexLock(&hookMutex);
704 newAccessProcPtr->nextPtr = accessProcList;
705 accessProcList = newAccessProcPtr;
706 Tcl_MutexUnlock(&hookMutex);
716 *----------------------------------------------------------------------
718 * TclAccessDeleteProc --
720 * Removed the passed function pointer from the list of 'TclAccess'
721 * functions. Ensures that the built-in access function is not
725 * TCL_OK if the procedure pointer was successfully removed,
726 * TCL_ERROR otherwise.
729 * Memory is deallocated and the respective list updated.
731 *----------------------------------------------------------------------
735 TclAccessDeleteProc(proc)
736 TclAccessProc_ *proc;
738 int retVal = TCL_ERROR;
739 AccessProc *tmpAccessProcPtr;
740 AccessProc *prevAccessProcPtr = NULL;
743 * Traverse the 'accessProcList' looking for the particular node
744 * whose 'proc' member matches 'proc' and remove that one from
745 * the list. Ensure that the "default" node cannot be removed.
748 Tcl_MutexLock(&hookMutex);
749 tmpAccessProcPtr = accessProcList;
750 while ((retVal == TCL_ERROR) && (tmpAccessProcPtr != &defaultAccessProc)) {
751 if (tmpAccessProcPtr->proc == proc) {
752 if (prevAccessProcPtr == NULL) {
753 accessProcList = tmpAccessProcPtr->nextPtr;
755 prevAccessProcPtr->nextPtr = tmpAccessProcPtr->nextPtr;
758 Tcl_Free((char *)tmpAccessProcPtr);
762 prevAccessProcPtr = tmpAccessProcPtr;
763 tmpAccessProcPtr = tmpAccessProcPtr->nextPtr;
766 Tcl_MutexUnlock(&hookMutex);
772 *----------------------------------------------------------------------
774 * TclOpenFileChannelInsertProc --
776 * Insert the passed procedure pointer at the head of the list of
777 * functions which are used during a call to
778 * 'Tcl_OpenFileChannel(...)'. The passed function should be have
779 * exactly like 'Tcl_OpenFileChannel' when called during that time
780 * (see 'Tcl_OpenFileChannel(...)' for more informatin). The
781 * function will be added even if it already in the list.
784 * Normally TCL_OK; TCL_ERROR if memory for a new node in the list
785 * could not be allocated.
788 * Memory allocataed and modifies the link list for
789 * 'Tcl_OpenFileChannel' functions.
791 *----------------------------------------------------------------------
795 TclOpenFileChannelInsertProc(proc)
796 TclOpenFileChannelProc_ *proc;
798 int retVal = TCL_ERROR;
801 OpenFileChannelProc *newOpenFileChannelProcPtr;
803 newOpenFileChannelProcPtr =
804 (OpenFileChannelProc *)ckalloc(sizeof(OpenFileChannelProc));
806 if (newOpenFileChannelProcPtr != NULL) {
807 newOpenFileChannelProcPtr->proc = proc;
808 Tcl_MutexLock(&hookMutex);
809 newOpenFileChannelProcPtr->nextPtr = openFileChannelProcList;
810 openFileChannelProcList = newOpenFileChannelProcPtr;
811 Tcl_MutexUnlock(&hookMutex);
821 *----------------------------------------------------------------------
823 * TclOpenFileChannelDeleteProc --
825 * Removed the passed function pointer from the list of
826 * 'Tcl_OpenFileChannel' functions. Ensures that the built-in
827 * open file channel function is not removvable.
830 * TCL_OK if the procedure pointer was successfully removed,
831 * TCL_ERROR otherwise.
834 * Memory is deallocated and the respective list updated.
836 *----------------------------------------------------------------------
840 TclOpenFileChannelDeleteProc(proc)
841 TclOpenFileChannelProc_ *proc;
843 int retVal = TCL_ERROR;
844 OpenFileChannelProc *tmpOpenFileChannelProcPtr = openFileChannelProcList;
845 OpenFileChannelProc *prevOpenFileChannelProcPtr = NULL;
848 * Traverse the 'openFileChannelProcList' looking for the particular
849 * node whose 'proc' member matches 'proc' and remove that one from
850 * the list. Ensure that the "default" node cannot be removed.
853 Tcl_MutexLock(&hookMutex);
854 tmpOpenFileChannelProcPtr = openFileChannelProcList;
855 while ((retVal == TCL_ERROR) &&
856 (tmpOpenFileChannelProcPtr != &defaultOpenFileChannelProc)) {
857 if (tmpOpenFileChannelProcPtr->proc == proc) {
858 if (prevOpenFileChannelProcPtr == NULL) {
859 openFileChannelProcList = tmpOpenFileChannelProcPtr->nextPtr;
861 prevOpenFileChannelProcPtr->nextPtr =
862 tmpOpenFileChannelProcPtr->nextPtr;
865 Tcl_Free((char *)tmpOpenFileChannelProcPtr);
869 prevOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr;
870 tmpOpenFileChannelProcPtr = tmpOpenFileChannelProcPtr->nextPtr;
873 Tcl_MutexUnlock(&hookMutex);