diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2024-04-10 13:18:37 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2024-04-10 13:18:37 (GMT) |
commit | 31a903a7f272c9b6fc395c3cd92b2653a1c1d88b (patch) | |
tree | b9560d57cef620034ac72591b80818e357c6c974 | |
parent | 5f873c0783342767b38a977bf2e3ebd8eb23f19f (diff) | |
download | tcl-core-bug-e155cedf33.zip tcl-core-bug-e155cedf33.tar.gz tcl-core-bug-e155cedf33.tar.bz2 |
[e155cedf33]: Error-handling in TclGetOpenMode()core-bug-e155cedf33
-rw-r--r-- | generic/tclIOCmd.c | 21 | ||||
-rw-r--r-- | generic/tclIOUtil.c | 180 | ||||
-rw-r--r-- | generic/tclInt.h | 3 | ||||
-rw-r--r-- | tests/ioCmd.test | 15 |
4 files changed, 116 insertions, 103 deletions
diff --git a/generic/tclIOCmd.c b/generic/tclIOCmd.c index 808ce97..f9d85b6 100644 --- a/generic/tclIOCmd.c +++ b/generic/tclIOCmd.c @@ -10,6 +10,7 @@ */ #include "tclInt.h" +#include "tclIO.h" /* * Callback structure for accept callback in a TCP server. @@ -40,9 +41,9 @@ static Tcl_ExitProc FinalizeIOCmdTSD; static Tcl_TcpAcceptProc AcceptCallbackProc; static Tcl_ObjCmdProc ChanPendingObjCmd; static Tcl_ObjCmdProc ChanTruncateObjCmd; -static void RegisterTcpServerInterpCleanup( - Tcl_Interp *interp, - AcceptCallback *acceptCallbackPtr); +static void RegisterTcpServerInterpCleanup( + Tcl_Interp *interp, + AcceptCallback *acceptCallbackPtr); static Tcl_InterpDeleteProc TcpAcceptCallbacksDeleteProc; static void TcpServerCloseProc(void *callbackData); static void UnregisterTcpServerInterpCleanupProc( @@ -546,7 +547,7 @@ Tcl_SeekObjCmd( if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { return TCL_ERROR; } - if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) { + if (TclGetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) { return TCL_ERROR; } mode = SEEK_SET; @@ -1154,7 +1155,7 @@ Tcl_OpenObjCmd( if (!pipeline) { chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot); } else { - int mode, seekFlag, binary; + int mode, modeFlags; Tcl_Size cmdObjc; const char **cmdArgv; @@ -1162,13 +1163,13 @@ Tcl_OpenObjCmd( return TCL_ERROR; } - mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary); + mode = TclGetOpenMode(interp, modeString, &modeFlags); if (mode == -1) { chan = NULL; } else { int flags = TCL_STDERR | TCL_ENFORCE_MODE; - switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { + switch (mode & O_ACCMODE) { case O_RDONLY: flags |= TCL_STDOUT; break; @@ -1183,7 +1184,7 @@ Tcl_OpenObjCmd( break; } chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags); - if (binary && chan) { + if ((modeFlags & CHANNEL_RAW_MODE) && chan) { Tcl_SetChannelOption(interp, chan, "-translation", "binary"); } } @@ -1774,7 +1775,7 @@ Tcl_FcopyObjCmd( } switch (index) { case FcopySize: - if (Tcl_GetWideIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) { + if (TclGetWideIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) { return TCL_ERROR; } if (toRead < 0) { @@ -1901,7 +1902,7 @@ ChanTruncateObjCmd( * User is supplying an explicit length. */ - if (Tcl_GetWideIntFromObj(interp, objv[2], &length) != TCL_OK) { + if (TclGetWideIntFromObj(interp, objv[2], &length) != TCL_OK) { return TCL_ERROR; } if (length < 0) { diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c index 4f6a008..ec24535 100644 --- a/generic/tclIOUtil.c +++ b/generic/tclIOUtil.c @@ -16,6 +16,7 @@ */ #include "tclInt.h" +#include "tclIO.h" #ifdef _WIN32 # include "tclWinInt.h" #endif @@ -1342,14 +1343,20 @@ TclFSNormalizeToUniquePath( */ path = TclGetStringFromObj(pathPtr, &i); - if ( (i >= 3) && ( (path[0] == '/' && path[1] == '/') - || (path[0] == '\\' && path[1] == '\\') ) ) { - for ( i = 2; ; i++) { - if (path[i] == '\0') break; - if (path[i] == path[0]) break; + if ((i >= 3) && ((path[0] == '/' && path[1] == '/') + || (path[0] == '\\' && path[1] == '\\'))) { + for (i = 2; ; i++) { + if (path[i] == '\0') { + break; + } + if (path[i] == path[0]) { + break; + } } --i; - if (path[i] == ':') isVfsPath = 1; + if (path[i] == ':') { + isVfsPath = 1; + } } /* @@ -1414,51 +1421,20 @@ TclFSNormalizeToUniquePath( * * TclGetOpenMode -- * - * Obsolete. A limited version of TclGetOpenModeEx() which exists only to - * satisfy any extensions imprudently using it via Tcl's internal stubs - * table. - * - * Results: - * See TclGetOpenModeEx(). - * - * Side effects: - * See TclGetOpenModeEx(). - * - *--------------------------------------------------------------------------- - */ - -int -TclGetOpenMode( - Tcl_Interp *interp, /* Interpreter to use for error reporting. May - * be NULL. */ - const char *modeString, /* e.g. "r+" or "RDONLY CREAT". */ - int *seekFlagPtr) /* Sets this to 1 to tell the caller to seek to - EOF after opening the file, and - * 0 otherwise. */ -{ - int binary = 0; - return TclGetOpenModeEx(interp, modeString, seekFlagPtr, &binary); -} - -/* - *--------------------------------------------------------------------------- - * - * TclGetOpenModeEx -- - * * Computes a POSIX mode mask for opening a file. * * Results: * The mode to pass to "open", or -1 if an error occurs. * * Side effects: - * Sets *seekFlagPtr to 1 to tell the caller to + * Sets *modeFlagPtr to 1 to tell the caller to * seek to EOF after opening the file, or to 0 otherwise. * - * Sets *binaryPtr to 1 to tell the caller to configure the channel as a - * binary channel, or to 0 otherwise. + * Adds CHANNEL_RAW_MODE to *modeFlagPtr to tell the caller + * to configure the channel as a binary channel. * - * If there is an error and interp is not NULL, sets interpreter result to - * an error message. + * If there is an error and interp is not NULL, sets + * interpreter result to an error message. * * Special note: * Based on a prototype implementation contributed by Mark Diekhans. @@ -1467,20 +1443,15 @@ TclGetOpenMode( */ int -TclGetOpenModeEx( +TclGetOpenMode( Tcl_Interp *interp, /* Interpreter, possibly NULL, to use for * error reporting. */ const char *modeString, /* Mode string, e.g. "r+" or "RDONLY CREAT" */ - int *seekFlagPtr, /* Sets this to 1 to tell the the caller to seek to - * EOF after opening the file, and 0 otherwise. */ - int *binaryPtr) /* Sets this to 1 to tell the caller to - * configure the channel for binary - * operations after opening the file. */ + int *modeFlagPtr) { int mode, c, gotRW; Tcl_Size modeArgc, i; - const char **modeArgv, *flag; -#define RW_MODES (O_RDONLY|O_WRONLY|O_RDWR) + const char **modeArgv = NULL, *flag; /* * Check for the simpler fopen-like access modes like "r" which are @@ -1488,9 +1459,8 @@ TclGetOpenModeEx( * lower-case first letter. */ - *seekFlagPtr = 0; - *binaryPtr = 0; - mode = 0; + *modeFlagPtr = 0; + mode = O_RDONLY; /* * Guard against wide characters before using byte-oriented routines. @@ -1500,7 +1470,6 @@ TclGetOpenModeEx( && islower(UCHAR(modeString[0]))) { /* INTL: ISO only. */ switch (modeString[0]) { case 'r': - mode = O_RDONLY; break; case 'w': mode = O_WRONLY|O_CREAT|O_TRUNC; @@ -1512,7 +1481,7 @@ TclGetOpenModeEx( */ mode = O_WRONLY|O_CREAT|O_APPEND; - *seekFlagPtr = 1; + *modeFlagPtr |= 1; break; default: goto error; @@ -1529,11 +1498,10 @@ TclGetOpenModeEx( * 1773127] */ - mode &= ~(O_RDONLY|O_WRONLY|O_APPEND); - mode |= O_RDWR; + mode = (mode & ~(O_ACCMODE|O_APPEND)) | O_RDWR; break; case 'b': - *binaryPtr = 1; + *modeFlagPtr |= CHANNEL_RAW_MODE; break; default: goto error; @@ -1545,11 +1513,11 @@ TclGetOpenModeEx( return mode; error: - *seekFlagPtr = 0; - *binaryPtr = 0; + *modeFlagPtr = 0; if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "illegal access mode \"%s\"", modeString)); + Tcl_SetErrorCode(interp, "TCL", "OPENMODE", "INVALID", (char *)NULL); } return -1; } @@ -1561,11 +1529,16 @@ TclGetOpenModeEx( */ if (Tcl_SplitList(interp, modeString, &modeArgc, &modeArgv) != TCL_OK) { + invAccessMode: if (interp != NULL) { Tcl_AddErrorInfo(interp, "\n while processing open access modes \""); Tcl_AddErrorInfo(interp, modeString); Tcl_AddErrorInfo(interp, "\""); + Tcl_SetErrorCode(interp, "TCL", "OPENMODE", "INVALID", (char *)NULL); + } + if (modeArgv) { + ckfree((void *)modeArgv); } return -1; } @@ -1575,24 +1548,55 @@ TclGetOpenModeEx( flag = modeArgv[i]; c = flag[0]; if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) { - mode = (mode & ~RW_MODES) | O_RDONLY; + if (gotRW) { + invRW: + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "invalid access mode \"%s\": modes RDONLY, " + "RDWR, and WRONLY cannot be combined", flag)); + } + goto invAccessMode; + } + mode = (mode & ~O_ACCMODE) | O_RDONLY; gotRW = 1; } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) { - mode = (mode & ~RW_MODES) | O_WRONLY; + if (gotRW) { + goto invRW; + } + mode = (mode & ~O_ACCMODE) | O_WRONLY; gotRW = 1; } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) { - mode = (mode & ~RW_MODES) | O_RDWR; + if (gotRW) { + goto invRW; + } + mode = (mode & ~O_ACCMODE) | O_RDWR; gotRW = 1; } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) { + if (mode & O_APPEND) { + accessFlagRepeated: + if (interp) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "access mode \"%s\" repeated", flag)); + } + goto invAccessMode; + } mode |= O_APPEND; - *seekFlagPtr = 1; + *modeFlagPtr |= 1; } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) { + if (mode & O_CREAT) { + goto accessFlagRepeated; + } mode |= O_CREAT; } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) { + if (mode & O_EXCL) { + goto accessFlagRepeated; + } mode |= O_EXCL; - } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) { #ifdef O_NOCTTY + if (mode & O_NOCTTY) { + goto accessFlagRepeated; + } mode |= O_NOCTTY; #else if (interp != NULL) { @@ -1600,12 +1604,14 @@ TclGetOpenModeEx( "access mode \"%s\" not supported by this system", flag)); } - ckfree(modeArgv); - return -1; + goto invAccessMode; #endif } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) { #ifdef O_NONBLOCK + if (mode & O_NONBLOCK) { + goto accessFlagRepeated; + } mode |= O_NONBLOCK; #else if (interp != NULL) { @@ -1613,33 +1619,35 @@ TclGetOpenModeEx( "access mode \"%s\" not supported by this system", flag)); } - ckfree(modeArgv); - return -1; + goto invAccessMode; #endif - } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) { + if (mode & O_TRUNC) { + goto accessFlagRepeated; + } mode |= O_TRUNC; } else if ((c == 'B') && (strcmp(flag, "BINARY") == 0)) { - *binaryPtr = 1; + if (*modeFlagPtr & CHANNEL_RAW_MODE) { + goto accessFlagRepeated; + } + *modeFlagPtr |= CHANNEL_RAW_MODE; } else { - if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "invalid access mode \"%s\": must be RDONLY, WRONLY, " - "RDWR, APPEND, BINARY, CREAT, EXCL, NOCTTY, NONBLOCK," - " or TRUNC", flag)); + "invalid access mode \"%s\": must be APPEND, BINARY, " + "CREAT, EXCL, NOCTTY, NONBLOCK, RDONLY, RDWR, " + "TRUNC, or WRONLY", flag)); } - ckfree(modeArgv); - return -1; + goto invAccessMode; } } - ckfree(modeArgv); + ckfree((void *)modeArgv); if (!gotRW) { if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj( - "access mode must include either RDONLY, WRONLY, or RDWR", + "access mode must include either RDONLY, RDWR, or WRONLY", -1)); } return -1; @@ -2204,14 +2212,14 @@ Tcl_FSOpenFileChannel( fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); if (fsPtr != NULL && fsPtr->openFileChannelProc != NULL) { - int mode, seekFlag, binary; + int mode, modeFlags; /* * Parse the mode to determine whether to seek at the outset * and/or set the channel into binary mode. */ - mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary); + mode = TclGetOpenMode(interp, modeString, &modeFlags); if (mode == -1) { return NULL; } @@ -2230,7 +2238,7 @@ Tcl_FSOpenFileChannel( * Seek and/or set binary mode as determined above. */ - if (seekFlag && Tcl_Seek(retVal, (Tcl_WideInt) 0, SEEK_END) + if ((modeFlags & 1) && Tcl_Seek(retVal, (Tcl_WideInt) 0, SEEK_END) < (Tcl_WideInt) 0) { if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( @@ -2240,7 +2248,7 @@ Tcl_FSOpenFileChannel( Tcl_Close(NULL, retVal); return NULL; } - if (binary) { + if (modeFlags & CHANNEL_RAW_MODE) { Tcl_SetChannelOption(interp, retVal, "-translation", "binary"); } return retVal; @@ -4353,16 +4361,14 @@ Tcl_FSCopyDirectory( int Tcl_FSRemoveDirectory( - Tcl_Obj *pathPtr, /* The pathname of the directory to be removed. - */ + Tcl_Obj *pathPtr, /* The pathname of the directory to be removed. */ int recursive, /* If zero, removes only an empty directory. * Otherwise, removes the directory and all its * contents. */ Tcl_Obj **errorPtr) /* If not NULL and an error occurs, stores a * place to store a a pointer to a new * object having a refCount of 1 and containing - * the name of the file that produced an error. - * */ + * the name of the file that produced an error. */ { const Tcl_Filesystem *fsPtr = Tcl_FSGetFileSystemForPath(pathPtr); diff --git a/generic/tclInt.h b/generic/tclInt.h index 09c47d5..077708e 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -3222,9 +3222,6 @@ MODULE_SCOPE int TclGetCompletionCodeFromObj(Tcl_Interp *interp, Tcl_Obj *value, int *code); MODULE_SCOPE Proc * TclGetLambdaFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Obj **nsObjPtrPtr); -MODULE_SCOPE int TclGetOpenModeEx(Tcl_Interp *interp, - const char *modeString, int *seekFlagPtr, - int *binaryPtr); MODULE_SCOPE Tcl_Obj * TclGetProcessGlobalValue(ProcessGlobalValue *pgvPtr); MODULE_SCOPE Tcl_Obj * TclGetSourceFromFrame(CmdFrame *cfPtr, int objc, Tcl_Obj *const objv[]); diff --git a/tests/ioCmd.test b/tests/ioCmd.test index 2b9aed6..91e53fe 100644 --- a/tests/ioCmd.test +++ b/tests/ioCmd.test @@ -497,14 +497,14 @@ unmatched open brace in list \"open \$path(test3) \"FOO \\{BAR BAZ\"\"" test iocmd-12.7 {POSIX open access modes: errors} { list [catch {open $path(test3) {FOO BAR BAZ}} msg] $msg -} {1 {invalid access mode "FOO": must be RDONLY, WRONLY, RDWR, APPEND, BINARY, CREAT, EXCL, NOCTTY, NONBLOCK, or TRUNC}} +} {1 {invalid access mode "FOO": must be APPEND, BINARY, CREAT, EXCL, NOCTTY, NONBLOCK, RDONLY, RDWR, TRUNC, or WRONLY}} test iocmd-12.8 {POSIX open access modes: errors} { list [catch {open $path(test3) {TRUNC CREAT}} msg] $msg -} {1 {access mode must include either RDONLY, WRONLY, or RDWR}} +} {1 {access mode must include either RDONLY, RDWR, or WRONLY}} close [open $path(test3) w] test iocmd-12.9 {POSIX open access modes: BINARY} { list [catch {open $path(test1) BINARY} msg] $msg -} {1 {access mode must include either RDONLY, WRONLY, or RDWR}} +} {1 {access mode must include either RDONLY, RDWR, or WRONLY}} test iocmd-12.10 {POSIX open access modes: BINARY} { set f [open $path(test1) {WRONLY BINARY TRUNC}] puts $f a @@ -527,6 +527,15 @@ test iocmd-12.11 {POSIX open access modes: BINARY} { close $f set result } H +test iocmd-12.12 {POSIX open access modes: errors} { + list [catch {open $path(test3) {RDWR WRONLY}} msg] $msg +} {1 {invalid access mode "WRONLY": modes RDONLY, RDWR, and WRONLY cannot be combined}} +test iocmd-12.13 {POSIX open access modes: errors} { + list [catch {open $path(test3) {BINARY BINARY}} msg] $msg +} {1 {access mode "BINARY" repeated}} +test iocmd-12.14 {POSIX open access modes: errors} { + list [catch {open $path(test3) {TRUNC}} msg] $msg +} {1 {access mode must include either RDONLY, RDWR, or WRONLY}} test iocmd-13.1 {errors in open command} { list [catch {open} msg] $msg |