diff options
author | dkf <donal.k.fellows@manchester.ac.uk> | 2019-04-23 13:47:52 (GMT) |
---|---|---|
committer | dkf <donal.k.fellows@manchester.ac.uk> | 2019-04-23 13:47:52 (GMT) |
commit | ba3eca6d7e1ad5c6a643052f7cc496d25272e3a5 (patch) | |
tree | efc172eb276545141d240b3c73550ce924f6ca79 /win | |
parent | eaf90743ee516f8a7f4b05720416fb49f20dfca2 (diff) | |
download | tcl-ba3eca6d7e1ad5c6a643052f7cc496d25272e3a5.zip tcl-ba3eca6d7e1ad5c6a643052f7cc496d25272e3a5.tar.gz tcl-ba3eca6d7e1ad5c6a643052f7cc496d25272e3a5.tar.bz2 |
Minor code style cleanup.
Diffstat (limited to 'win')
-rwxr-xr-x | win/tclWinFile.c | 309 | ||||
-rw-r--r-- | win/tclWinPipe.c | 459 |
2 files changed, 501 insertions, 267 deletions
diff --git a/win/tclWinFile.c b/win/tclWinFile.c index 809bcf0..2f35d4a 100755 --- a/win/tclWinFile.c +++ b/win/tclWinFile.c @@ -682,7 +682,8 @@ NativeReadReparse( HANDLE hFile; DWORD returnedLength; - hFile = CreateFile(linkDirPath, desiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, + hFile = CreateFile(linkDirPath, desiredAccess, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile == INVALID_HANDLE_VALUE) { @@ -844,7 +845,7 @@ tclWinDebugPanic( #endif abort(); } - + /* *--------------------------------------------------------------------------- * @@ -1461,11 +1462,16 @@ TclpGetUserHome( if (domain == NULL) { const char *ptr; - /* no domain - firstly check it's the current user */ - if ( (ptr = TclpGetUserName(&ds)) != NULL - && strcasecmp(name, ptr) == 0 - ) { - /* try safest and fastest way to get current user home */ + /* + * No domain. Firstly check it's the current user + */ + + ptr = TclpGetUserName(&ds); + if (ptr != NULL && strcasecmp(name, ptr) == 0) { + /* + * Try safest and fastest way to get current user home + */ + ptr = TclGetEnv("HOME", &ds); if (ptr != NULL) { Tcl_JoinPath(1, &ptr, bufferPtr); @@ -1486,18 +1492,28 @@ TclpGetUserHome( wName = Tcl_UtfToUniCharDString(name, nameLen, &ds); while (NetUserGetInfo(wDomain, wName, 1, (LPBYTE *) &uiPtr) != 0) { /* - * user does not exists - if domain was not specified, - * try again using current domain. + * User does not exist; if domain was not specified, try again + * using current domain. */ + rc = 1; - if (domain != NULL) break; - /* get current domain */ + if (domain != NULL) { + break; + } + + /* + * Get current domain + */ + rc = NetGetDCName(NULL, NULL, (LPBYTE *) &wDomain); - if (rc != 0) break; + if (rc != 0) { + break; + } domain = INT2PTR(-1); /* repeat once */ } if (rc == 0) { DWORD i, size = MAX_PATH; + wHomeDir = uiPtr->usri1_home_dir; if ((wHomeDir != NULL) && (wHomeDir[0] != L'\0')) { size = lstrlenW(wHomeDir); @@ -1507,15 +1523,22 @@ TclpGetUserHome( * User exists but has no home dir. Return * "{GetProfilesDirectory}/<user>". */ + GetProfilesDirectoryW(buf, &size); Tcl_UniCharToUtfDString(buf, size-1, bufferPtr); Tcl_DStringAppend(bufferPtr, "/", 1); Tcl_DStringAppend(bufferPtr, name, nameLen); } result = Tcl_DStringValue(bufferPtr); - /* be sure we returns normalized path */ - for (i = 0; i < size; ++i){ - if (result[i] == '\\') result[i] = '/'; + + /* + * Be sure we returns normalized path + */ + + for (i = 0; i < size; ++i) { + if (result[i] == '\\') { + result[i] = '/'; + } } NetApiBufferFree((void *) uiPtr); } @@ -1603,48 +1626,72 @@ NativeAccess( /* * If it's not a directory (assume file), do several fast checks: */ + if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { /* * If the attributes say this is not writable at all. The file is a * regular file (i.e., not a directory), then the file is not - * writable, full stop. For directories, the read-only bit is + * writable, full stop. For directories, the read-only bit is * (mostly) ignored by Windows, so we can't ascertain anything about * directory access from the attrib data. However, if we have the - * advanced 'getFileSecurityProc', then more robust ACL checks - * will be done below. + * advanced 'getFileSecurityProc', then more robust ACL checks will be + * done below. */ + if ((mode & W_OK) && (attr & FILE_ATTRIBUTE_READONLY)) { Tcl_SetErrno(EACCES); return -1; } - /* If doesn't have the correct extension, it can't be executable */ + /* + * If doesn't have the correct extension, it can't be executable + */ + if ((mode & X_OK) && !NativeIsExec(nativePath)) { Tcl_SetErrno(EACCES); return -1; } - /* Special case for read/write/executable check on file */ + + /* + * Special case for read/write/executable check on file + */ + if ((mode & (R_OK|W_OK|X_OK)) && !(mode & ~(R_OK|W_OK|X_OK))) { DWORD mask = 0; HANDLE hFile; - if (mode & R_OK) { mask |= GENERIC_READ; } - if (mode & W_OK) { mask |= GENERIC_WRITE; } - if (mode & X_OK) { mask |= GENERIC_EXECUTE; } + + if (mode & R_OK) { + mask |= GENERIC_READ; + } + if (mode & W_OK) { + mask |= GENERIC_WRITE; + } + if (mode & X_OK) { + mask |= GENERIC_EXECUTE; + } hFile = CreateFile(nativePath, mask, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, - OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL); + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL); if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); return 0; } - /* fast exit if access was denied */ + + /* + * Fast exit if access was denied + */ + if (GetLastError() == ERROR_ACCESS_DENIED) { Tcl_SetErrno(EACCES); return -1; } } - /* We cannnot verify the access fast, check it below using security info. */ + + /* + * We cannnot verify the access fast, check it below using security + * info. + */ } /* @@ -2021,13 +2068,12 @@ NativeStat( * 'getFileAttributesExProc', and if that isn't available, then on even * simpler routines. * - * Special consideration must be given to Windows hardcoded names - * like CON, NULL, COM1, LPT1 etc. For these, we still need to - * do the CreateFile as some may not exist (e.g. there is no CON - * in wish by default). However the subsequent GetFileInformationByHandle - * will fail. We do a WinIsReserved to see if it is one of the special - * names, and if successful, mock up a BY_HANDLE_FILE_INFORMATION - * structure. + * Special consideration must be given to Windows hardcoded names like + * CON, NULL, COM1, LPT1 etc. For these, we still need to do the + * CreateFile as some may not exist (e.g. there is no CON in wish by + * default). However the subsequent GetFileInformationByHandle will + * fail. We do a WinIsReserved to see if it is one of the special names, + * and if successful, mock up a BY_HANDLE_FILE_INFORMATION structure. */ fileHandle = CreateFile(nativePath, GENERIC_READ, @@ -2045,7 +2091,11 @@ NativeStat( Tcl_SetErrno(ENOENT); return -1; } - /* Mock up the expected structure */ + + /* + * Mock up the expected structure + */ + memset(&data, 0, sizeof(data)); statPtr->st_atime = 0; statPtr->st_mtime = 0; @@ -2328,7 +2378,7 @@ TclpGetNativeCwd( } if (clientData != NULL) { - if (_tcscmp((const TCHAR*)clientData, buffer) == 0) { + if (_tcscmp((const TCHAR *) clientData, buffer) == 0) { return clientData; } } @@ -2556,10 +2606,12 @@ TclpObjNormalizePath( (int)(sizeof(WCHAR) * len)); lastValidPathEnd = currentPathEndPosition; } else if (nextCheckpoint == 0) { - /* Path starts with a drive designation - * that's not actually on the system. - * We still must normalize up past the - * first separator. [Bug 3603434] */ + /* + * Path starts with a drive designation that's not + * actually on the system. We still must normalize up + * past the first separator. [Bug 3603434] + */ + currentPathEndPosition++; } } @@ -2574,11 +2626,10 @@ TclpObjNormalizePath( */ /* - * Check for symlinks, except at last component of path (we - * don't follow final symlinks). Also a drive (C:/) for - * example, may sometimes have the reparse flag set for some - * reason I don't understand. We therefore don't perform this - * check for drives. + * Check for symlinks, except at last component of path (we don't + * follow final symlinks). Also a drive (C:/) for example, may + * sometimes have the reparse flag set for some reason I don't + * understand. We therefore don't perform this check for drives. */ if (cur != 0 && !isDrive && @@ -2587,8 +2638,8 @@ TclpObjNormalizePath( if (to != NULL) { /* - * Read the reparse point ok. Now, reparse points need - * not be normalized, otherwise we could use: + * Read the reparse point ok. Now, reparse points need not + * be normalized, otherwise we could use: * * Tcl_GetStringFromObj(to, &pathLen); * nextCheckpoint = pathLen; @@ -2628,9 +2679,9 @@ TclpObjNormalizePath( #ifndef TclNORM_LONG_PATH /* - * Now we convert the tail of the current path to its 'long - * form', and append it to 'dsNorm' which holds the current - * normalized path + * Now we convert the tail of the current path to its 'long form', + * and append it to 'dsNorm' which holds the current normalized + * path */ if (isDrive) { @@ -2659,10 +2710,10 @@ TclpObjNormalizePath( int dotLen = currentPathEndPosition-lastValidPathEnd; /* - * Path is just dots. We shouldn't really ever see a - * path like that. However, to be nice we at least - * don't mangle the path - we just add the dots as a - * path segment and continue. + * Path is just dots. We shouldn't really ever see a path + * like that. However, to be nice we at least don't mangle + * the path - we just add the dots as a path segment and + * continue. */ Tcl_DStringAppend(&dsNorm, ((const char *)nativePath) @@ -2680,8 +2731,7 @@ TclpObjNormalizePath( handle = FindFirstFileW((WCHAR *) nativePath, &fData); if (handle == INVALID_HANDLE_VALUE) { /* - * This is usually the '/' in 'c:/' at end of - * string. + * This is usually the '/' in 'c:/' at end of string. */ Tcl_DStringAppend(&dsNorm, (const char *) L"/", @@ -2711,8 +2761,8 @@ TclpObjNormalizePath( } /* - * If we get here, we've got past one directory delimiter, so - * we know it is no longer a drive. + * If we get here, we've got past one directory delimiter, so we + * know it is no longer a drive. */ isDrive = 0; @@ -3007,7 +3057,11 @@ TclNativeCreateNativeRep( if (validPathPtr == NULL) { return NULL; } - /* refCount of validPathPtr was already incremented in Tcl_FSGetTranslatedPath */ + + /* + * refCount of validPathPtr was already incremented in + * Tcl_FSGetTranslatedPath + */ } else { /* * Make sure the normalized path is set. @@ -3017,73 +3071,101 @@ TclNativeCreateNativeRep( if (validPathPtr == NULL) { return NULL; } - /* validPathPtr returned from Tcl_FSGetNormalizedPath is owned by Tcl, so incr refCount here */ + + /* + * validPathPtr returned from Tcl_FSGetNormalizedPath is owned by Tcl, + * so incr refCount here + */ + Tcl_IncrRefCount(validPathPtr); } str = Tcl_GetString(validPathPtr); len = validPathPtr->length; - if (strlen(str)!=(unsigned int)len) { - /* String contains NUL-bytes. This is invalid. */ + if (strlen(str) != (unsigned int) len) { + /* + * String contains NUL-bytes. This is invalid. + */ + goto done; } - /* For a reserved device, strip a possible postfix ':' */ + + /* + * For a reserved device, strip a possible postfix ':' + */ + len = WinIsReserved(str); if (len == 0) { - /* Let MultiByteToWideChar check for other invalid sequences, like - * 0xC0 0x80 (== overlong NUL). See bug [3118489]: NUL in filenames */ + /* + * Let MultiByteToWideChar check for other invalid sequences, like + * 0xC0 0x80 (== overlong NUL). See bug [3118489]: NUL in filenames + */ + len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, 0, 0); if (len==0) { goto done; } } - /* Overallocate 6 chars, making some room for extended paths */ - wp = nativePathPtr = ckalloc( (len+6) * sizeof(WCHAR) ); + + /* + * Overallocate 6 chars, making some room for extended paths + */ + + wp = nativePathPtr = ckalloc((len + 6) * sizeof(WCHAR)); if (nativePathPtr==0) { goto done; } - MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nativePathPtr, len+1); + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, nativePathPtr, + len + 1); + /* - ** If path starts with "//?/" or "\\?\" (extended path), translate - ** any slashes to backslashes but leave the '?' intact - */ - if ((str[0]=='\\' || str[0]=='/') && (str[1]=='\\' || str[1]=='/') - && str[2]=='?' && (str[3]=='\\' || str[3]=='/')) { + * If path starts with "//?/" or "\\?\" (extended path), translate any + * slashes to backslashes but leave the '?' intact + */ + + if ((str[0] == '\\' || str[0] == '/') && (str[1] == '\\' || str[1] == '/') + && str[2] == '?' && (str[3] == '\\' || str[3] == '/')) { wp[0] = wp[1] = wp[3] = '\\'; str += 4; wp += 4; } + /* - ** If there is no "\\?\" prefix but there is a drive or UNC - ** path prefix and the path is larger than MAX_PATH chars, - ** no Win32 API function can handle that unless it is - ** prefixed with the extended path prefix. See: - ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath> - **/ - if (((str[0]>='A'&&str[0]<='Z') || (str[0]>='a'&&str[0]<='z')) - && str[1]==':') { - if (wp==nativePathPtr && len>MAX_PATH && (str[2]=='\\' || str[2]=='/')) { - memmove(wp+4, wp, len*sizeof(WCHAR)); - memcpy(wp, L"\\\\?\\", 4*sizeof(WCHAR)); + * If there is no "\\?\" prefix but there is a drive or UNC path prefix + * and the path is larger than MAX_PATH chars, no Win32 API function can + * handle that unless it is prefixed with the extended path prefix. See: + * <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath> + */ + + if (((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z')) + && str[1] == ':') { + if (wp == nativePathPtr && len > MAX_PATH + && (str[2] == '\\' || str[2] == '/')) { + memmove(wp + 4, wp, len * sizeof(WCHAR)); + memcpy(wp, L"\\\\?\\", 4 * sizeof(WCHAR)); wp += 4; } + /* - ** If (remainder of) path starts with "<drive>:", - ** leave the ':' intact. + * If (remainder of) path starts with "<drive>:", leave the ':' + * intact. */ + wp += 2; - } else if (wp==nativePathPtr && len>MAX_PATH - && (str[0]=='\\' || str[0]=='/') - && (str[1]=='\\' || str[1]=='/') && str[2]!='?') { - memmove(wp+6, wp, len*sizeof(WCHAR)); - memcpy(wp, L"\\\\?\\UNC", 7*sizeof(WCHAR)); + } else if (wp == nativePathPtr && len > MAX_PATH + && (str[0] == '\\' || str[0] == '/') + && (str[1] == '\\' || str[1] == '/') && str[2] != '?') { + memmove(wp + 6, wp, len * sizeof(WCHAR)); + memcpy(wp, L"\\\\?\\UNC", 7 * sizeof(WCHAR)); wp += 7; } + /* - ** In the remainder of the path, translate invalid characters to - ** characters in the Unicode private use area. - */ + * In the remainder of the path, translate invalid characters to + * characters in the Unicode private use area. + */ + while (*wp != '\0') { if ((*wp < ' ') || wcschr(L"\"*:<>?|", *wp)) { *wp |= 0xF000; @@ -3094,7 +3176,6 @@ TclNativeCreateNativeRep( } done: - TclDecrRefCount(validPathPtr); return nativePathPtr; } @@ -3220,21 +3301,28 @@ TclWinFileOwned( native = Tcl_FSGetNativePath(pathPtr); if (GetNamedSecurityInfo((LPTSTR) native, SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION, &ownerSid, - NULL, NULL, NULL, &secd) != ERROR_SUCCESS) { - /* Either not a file, or we do not have access to it in which - case we are in all likelihood not the owner */ + OWNER_SECURITY_INFORMATION, &ownerSid, NULL, NULL, NULL, + &secd) != ERROR_SUCCESS) { + /* + * Either not a file, or we do not have access to it in which case we + * are in all likelihood not the owner. + */ + return 0; } /* - * Getting the current process SID is a multi-step process. - * We make the assumption that if a call fails, this process is - * so underprivileged it could not possibly own anything. Normally - * a process can *always* look up its own token. + * Getting the current process SID is a multi-step process. We make the + * assumption that if a call fails, this process is so underprivileged it + * could not possibly own anything. Normally a process can *always* look + * up its own token. */ + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { - /* Find out how big the buffer needs to be */ + /* + * Find out how big the buffer needs to be. + */ + bufsz = 0; GetTokenInformation(token, TokenUser, NULL, 0, &bufsz); if (bufsz) { @@ -3246,15 +3334,20 @@ TclWinFileOwned( CloseHandle(token); } - /* Free allocations and be done */ - if (secd) + /* + * Free allocations and be done. + */ + + if (secd) { LocalFree(secd); /* Also frees ownerSid */ - if (buf) + } + if (buf) { ckfree(buf); + } return (owned != 0); /* Convert non-0 to 1 */ } - + /* * Local Variables: * mode: c diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c index 83bd26e..ce3e746 100644 --- a/win/tclWinPipe.c +++ b/win/tclWinPipe.c @@ -124,8 +124,7 @@ typedef struct PipeInfo { * write. Set to 0 if no error has been * detected. This word is shared with the * writer thread so access must be - * synchronized with the writable object. - */ + * synchronized with the writable object. */ char *writeBuf; /* Current background output buffer. Access is * synchronized with the writable object. */ int writeBufLen; /* Size of write buffer. Access is @@ -218,7 +217,7 @@ static const Tcl_ChannelType pipeChannelType = { NULL, /* handler proc. */ NULL, /* wide seek proc */ PipeThreadActionProc, /* thread action proc */ - NULL /* truncate */ + NULL /* truncate */ }; /* @@ -1445,9 +1444,12 @@ ApplicationType( static const char * BuildCmdLineBypassBS( const char *current, - const char **bspos -) { - /* mark first backslash possition */ + const char **bspos) +{ + /* + * Mark first backslash position. + */ + if (!*bspos) { *bspos = current; } @@ -1462,14 +1464,14 @@ QuoteCmdLineBackslash( Tcl_DString *dsPtr, const char *start, const char *current, - const char *bspos -) { + const char *bspos) +{ if (!bspos) { - if (current > start) { /* part before current (special) */ + if (current > start) { /* part before current (special) */ Tcl_DStringAppend(dsPtr, start, (int) (current - start)); } } else { - if (bspos > start) { /* part before first backslash */ + if (bspos > start) { /* part before first backslash */ Tcl_DStringAppend(dsPtr, start, (int) (bspos - start)); } while (bspos++ < current) { /* each backslash twice */ @@ -1484,38 +1486,59 @@ QuoteCmdLinePart( const char *start, const char *special, const char *specMetaChars, - const char **bspos -) { + const char **bspos) +{ if (!*bspos) { - /* rest before special (before quote) */ + /* + * Rest before special (before quote). + */ + QuoteCmdLineBackslash(dsPtr, start, special, NULL); start = special; } else { - /* rest before first backslash and backslashes into new quoted block */ + /* + * Rest before first backslash and backslashes into new quoted block. + */ + QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL); start = *bspos; } + /* - * escape all special chars enclosed in quotes like `"..."`, note that here we - * don't must escape `\` (with `\`), because it's outside of the main quotes, - * so `\` remains `\`, but important - not at end of part, because results as - * before the quote, so `%\%\` should be escaped as `"%\%"\\`). + * escape all special chars enclosed in quotes like `"..."`, note that + * here we don't must escape `\` (with `\`), because it's outside of the + * main quotes, so `\` remains `\`, but important - not at end of part, + * because results as before the quote, so `%\%\` should be escaped as + * `"%\%"\\`). */ + TclDStringAppendLiteral(dsPtr, "\""); /* opening escape quote-char */ do { *bspos = NULL; special++; if (*special == '\\') { - /* bypass backslashes (and mark first backslash possition)*/ + /* + * Bypass backslashes (and mark first backslash position). + */ + special = BuildCmdLineBypassBS(special, bspos); - if (*special == '\0') break; + if (*special == '\0') { + break; + } } } while (*special && strchr(specMetaChars, *special)); if (!*bspos) { - /* unescaped rest before quote */ + /* + * Unescaped rest before quote. + */ + QuoteCmdLineBackslash(dsPtr, start, special, NULL); } else { - /* unescaped rest before first backslash (rather belongs to the main block) */ + /* + * Unescaped rest before first backslash (rather belongs to the main + * block). + */ + QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL); } TclDStringAppendLiteral(dsPtr, "\""); /* closing escape quote-char */ @@ -1534,13 +1557,14 @@ BuildCommandLine( const char *arg, *start, *special, *bspos; int quote = 0, i; Tcl_DString ds; - - /* characters to enclose in quotes if unpaired quote flag set */ static const char specMetaChars[] = "&|^<>!()%"; - /* character to enclose in quotes in any case (regardless unpaired-flag) */ + /* Characters to enclose in quotes if unpaired + * quote flag set. */ static const char specMetaChars2[] = "%"; - - /* Quote flags: + /* Character to enclose in quotes in any case + * (regardless of unpaired-flag). */ + /* + * Quote flags: * CL_ESCAPE - escape argument; * CL_QUOTE - enclose in quotes; * CL_UNPAIRED - previous arguments chain contains unpaired quote-char; @@ -1572,30 +1596,31 @@ BuildCommandLine( quote = CL_QUOTE; } else { for (start = arg; - *start != '\0' && - (quote & (CL_ESCAPE|CL_QUOTE)) != (CL_ESCAPE|CL_QUOTE); - start++ - ) { - if (*start & 0x80) continue; + *start != '\0' && + (quote & (CL_ESCAPE|CL_QUOTE)) != (CL_ESCAPE|CL_QUOTE); + start++) { + if (*start & 0x80) { + continue; + } if (TclIsSpaceProc(*start)) { - quote |= CL_QUOTE; /* quote only */ - if (bspos) { /* if backslash found - escape & quote */ + quote |= CL_QUOTE; /* quote only */ + if (bspos) { /* if backslash found, escape & quote */ quote |= CL_ESCAPE; break; } continue; } if (strchr(specMetaChars, *start)) { - quote |= (CL_ESCAPE|CL_QUOTE); /*escape & quote */ + quote |= (CL_ESCAPE|CL_QUOTE); /* escape & quote */ break; } if (*start == '"') { - quote |= CL_ESCAPE; /* escape only */ + quote |= CL_ESCAPE; /* escape only */ continue; } if (*start == '\\') { bspos = start; - if (quote & CL_QUOTE) { /* if quote - escape & quote */ + if (quote & CL_QUOTE) { /* if quote, escape & quote */ quote |= CL_ESCAPE; break; } @@ -1605,56 +1630,116 @@ BuildCommandLine( bspos = NULL; } if (quote & CL_QUOTE) { - /* start of argument (main opening quote-char) */ + /* + * Start of argument (main opening quote-char). + */ + TclDStringAppendLiteral(&ds, "\""); } if (!(quote & CL_ESCAPE)) { - /* nothing to escape */ + /* + * Nothing to escape. + */ + Tcl_DStringAppend(&ds, arg, -1); } else { start = arg; for (special = arg; *special != '\0'; ) { - /* position of `\` is important before quote or at end (equal `\"` because quoted) */ + /* + * Position of `\` is important before quote or at end (equal + * `\"` because quoted). + */ + if (*special == '\\') { - /* bypass backslashes (and mark first backslash possition)*/ + /* + * Bypass backslashes (and mark first backslash position) + */ + special = BuildCmdLineBypassBS(special, &bspos); - if (*special == '\0') break; + if (*special == '\0') { + break; + } } /* ["] */ if (*special == '"') { - quote ^= CL_UNPAIRED; /* invert unpaired flag - observe unpaired quotes */ - /* add part before (and escape backslashes before quote) */ + /* + * Invert the unpaired flag - observe unpaired quotes + */ + + quote ^= CL_UNPAIRED; + + /* + * Add part before (and escape backslashes before quote). + */ + QuoteCmdLineBackslash(&ds, start, special, bspos); bspos = NULL; - /* escape using backslash */ + + /* + * Escape using backslash + */ + TclDStringAppendLiteral(&ds, "\\\""); start = ++special; continue; } - /* unpaired (escaped) quote causes special handling on meta-chars */ + + /* + * Unpaired (escaped) quote causes special handling on + * meta-chars + */ + if ((quote & CL_UNPAIRED) && strchr(specMetaChars, *special)) { - special = QuoteCmdLinePart(&ds, start, special, specMetaChars, &bspos); - /* start to current or first backslash */ + special = QuoteCmdLinePart(&ds, start, special, + specMetaChars, &bspos); + + /* + * Start to current or first backslash + */ + start = !bspos ? special : bspos; continue; } - /* special case for % - should be enclosed always (paired also) */ + + /* + * Special case for % - should be enclosed always (paired + * also) + */ + if (strchr(specMetaChars2, *special)) { - special = QuoteCmdLinePart(&ds, start, special, specMetaChars2, &bspos); - /* start to current or first backslash */ + special = QuoteCmdLinePart(&ds, start, special, + specMetaChars2, &bspos); + + /* + * Start to current or first backslash. + */ + start = !bspos ? special : bspos; continue; } - /* other not special (and not meta) character */ - bspos = NULL; /* reset last backslash possition (not interesting) */ + + /* + * Other not special (and not meta) character + */ + + bspos = NULL; /* reset last backslash position (not + * interesting) */ special++; } - /* rest of argument (and escape backslashes before closing main quote) */ + + /* + * Rest of argument (and escape backslashes before closing main + * quote) + */ + QuoteCmdLineBackslash(&ds, start, special, - (quote & CL_QUOTE) ? bspos : NULL); + (quote & CL_QUOTE) ? bspos : NULL); } if (quote & CL_QUOTE) { - /* end of argument (main closing quote-char) */ + /* + * End of argument (main closing quote-char) + */ + TclDStringAppendLiteral(&ds, "\""); } } @@ -2192,8 +2277,9 @@ PipeOutputProc( *errorCode = 0; /* avoid blocking if pipe-thread exited */ - timeout = ((infoPtr->flags & PIPE_ASYNC) || !TclPipeThreadIsAlive(&infoPtr->writeTI) - || TclInExit() || TclInThreadExit()) ? 0 : INFINITE; + timeout = ((infoPtr->flags & PIPE_ASYNC) + || !TclPipeThreadIsAlive(&infoPtr->writeTI) + || TclInExit() || TclInThreadExit()) ? 0 : INFINITE; if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) { /* * The writer thread is blocked waiting for a write to complete and @@ -2379,6 +2465,7 @@ PipeWatchProc( infoPtr->watchMask = mask & infoPtr->validMask; if (infoPtr->watchMask) { Tcl_Time blockTime = { 0, 0 }; + if (!oldMask) { infoPtr->nextPtr = tsdPtr->firstPipePtr; tsdPtr->firstPipePtr = infoPtr; @@ -2848,7 +2935,7 @@ static DWORD WINAPI PipeReaderThread( LPVOID arg) { - TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg; + TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *) arg; PipeInfo *infoPtr = NULL; /* access info only after success init/wait */ HANDLE handle = NULL; DWORD count, err; @@ -2859,13 +2946,14 @@ PipeReaderThread( * Wait for the main thread to signal before attempting to wait on the * pipe becoming readable. */ + if (!TclPipeThreadWaitForSignal(&pipeTI)) { /* exit */ break; } if (!infoPtr) { - infoPtr = (PipeInfo *)pipeTI->clientData; + infoPtr = (PipeInfo *) pipeTI->clientData; handle = ((WinFile *) infoPtr->readFile)->handle; } @@ -3211,7 +3299,7 @@ TclPipeThreadCreateTI( pipeTI = malloc(sizeof(TclPipeThreadInfo)); #else pipeTI = ckalloc(sizeof(TclPipeThreadInfo)); -#endif +#endif /* !_PTI_USE_CKALLOC */ pipeTI->evControl = CreateEvent(NULL, FALSE, FALSE, NULL); pipeTI->state = PTI_STATE_IDLE; pipeTI->clientData = clientData; @@ -3250,40 +3338,64 @@ TclPipeThreadWaitForSignal( } wakeEvent = pipeTI->evWakeUp; + /* * Wait for the main thread to signal before attempting to do the work. */ - /* reset work state of thread (idle/waiting) */ - if ((state = InterlockedCompareExchange(&pipeTI->state, - PTI_STATE_IDLE, PTI_STATE_WORK)) & (PTI_STATE_STOP|PTI_STATE_END)) { - /* end of work, check the owner of structure */ + /* + * Reset work state of thread (idle/waiting) + */ + + state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_IDLE, + PTI_STATE_WORK); + if (state & (PTI_STATE_STOP|PTI_STATE_END)) { + /* + * End of work, check the owner of structure. + */ + goto end; } - /* entering wait */ - waitResult = WaitForSingleObject(pipeTI->evControl, INFINITE); - if (waitResult != WAIT_OBJECT_0) { + /* + * Entering wait + */ + waitResult = WaitForSingleObject(pipeTI->evControl, INFINITE); + if (waitResult != WAIT_OBJECT_0) { /* * The control event was not signaled, so end of work (unexpected * behaviour, main thread can be dead?). */ + goto end; } - /* try to set work state of thread */ - if ((state = InterlockedCompareExchange(&pipeTI->state, - PTI_STATE_WORK, PTI_STATE_IDLE)) & (PTI_STATE_STOP|PTI_STATE_END)) { - /* end of work */ + /* + * Try to set work state of thread + */ + + state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_WORK, + PTI_STATE_IDLE); + if (state & (PTI_STATE_STOP|PTI_STATE_END)) { + /* + * End of work + */ + goto end; } - /* signaled to work */ + /* + * Signaled to work. + */ + return 1; -end: - /* end of work, check the owner of the TI structure */ + end: + /* + * End of work, check the owner of the TI structure. + */ + if (state != PTI_STATE_STOP) { *pipeTIPtr = NULL; } else { @@ -3313,7 +3425,8 @@ end: int TclPipeThreadStopSignal( - TclPipeThreadInfo **pipeTIPtr, HANDLE wakeEvent) + TclPipeThreadInfo **pipeTIPtr, + HANDLE wakeEvent) { TclPipeThreadInfo *pipeTI = *pipeTIPtr; HANDLE evControl; @@ -3324,28 +3437,27 @@ TclPipeThreadStopSignal( } evControl = pipeTI->evControl; pipeTI->evWakeUp = wakeEvent; - switch ( - (state = InterlockedCompareExchange(&pipeTI->state, - PTI_STATE_STOP, PTI_STATE_IDLE)) - ) { - - case PTI_STATE_IDLE: - - /* Thread was idle/waiting, notify it goes teardown */ - SetEvent(evControl); - - *pipeTIPtr = NULL; - - case PTI_STATE_DOWN: + state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_STOP, + PTI_STATE_IDLE); + switch (state) { + case PTI_STATE_IDLE: + /* + * Thread was idle/waiting, notify it goes teardown + */ + SetEvent(evControl); + *pipeTIPtr = NULL; + case PTI_STATE_DOWN: return 1; - default: - /* - * Thread works currently, we should try to end it, own the TI structure - * (because of possible sharing the joint structures with thread) - */ - InterlockedExchange(&pipeTI->state, PTI_STATE_END); + default: + /* + * Thread works currently, we should try to end it, own the TI + * structure (because of possible sharing the joint structures with + * thread) + */ + + InterlockedExchange(&pipeTI->state, PTI_STATE_END); break; } @@ -3388,46 +3500,63 @@ TclPipeThreadStop( pipeTI = *pipeTIPtr; evControl = pipeTI->evControl; pipeTI->evWakeUp = NULL; + /* * Try to sane stop the pipe worker, corresponding its current state */ - switch ( - (state = InterlockedCompareExchange(&pipeTI->state, - PTI_STATE_STOP, PTI_STATE_IDLE)) - ) { - case PTI_STATE_IDLE: + state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_STOP, + PTI_STATE_IDLE); + switch (state) { + case PTI_STATE_IDLE: + /* + * Thread was idle/waiting, notify it goes teardown + */ - /* Thread was idle/waiting, notify it goes teardown */ - SetEvent(evControl); + SetEvent(evControl); - /* we don't need to wait for it at all, thread frees himself (owns the TI structure) */ - pipeTI = NULL; + /* + * We don't need to wait for it at all, thread frees himself (owns the + * TI structure) + */ + + pipeTI = NULL; break; - case PTI_STATE_STOP: - /* already stopped, thread frees himself (owns the TI structure) */ - pipeTI = NULL; + case PTI_STATE_STOP: + /* + * Already stopped, thread frees himself (owns the TI structure) + */ + + pipeTI = NULL; break; - case PTI_STATE_DOWN: - /* Thread already down (?), do nothing */ + case PTI_STATE_DOWN: + /* + * Thread already down (?), do nothing + */ - /* we don't need to wait for it, but we should free pipeTI */ - hThread = NULL; + /* + * We don't need to wait for it, but we should free pipeTI + */ + hThread = NULL; break; /* case PTI_STATE_WORK: */ - default: + default: + /* + * Thread works currently, we should try to end it, own the TI + * structure (because of possible sharing the joint structures with + * thread) + */ + + state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_END, + PTI_STATE_WORK); + if (state == PTI_STATE_DOWN) { /* - * Thread works currently, we should try to end it, own the TI structure - * (because of possible sharing the joint structures with thread) + * We don't need to wait for it, but we should free pipeTI */ - if ((state = InterlockedCompareExchange(&pipeTI->state, - PTI_STATE_END, PTI_STATE_WORK)) == PTI_STATE_DOWN - ) { - /* we don't need to wait for it, but we should free pipeTI */ - hThread = NULL; - }; + hThread = NULL; + } break; } @@ -3442,8 +3571,8 @@ TclPipeThreadStop( GetExitCodeThread(hThread, &exitCode); if (exitCode == STILL_ACTIVE) { - int inExit = (TclInExit() || TclInThreadExit()); + /* * Set the stop event so that if the pipe thread is blocked * somewhere, it may hereafter sane exit cleanly. @@ -3454,59 +3583,69 @@ TclPipeThreadStop( /* * Cancel all sync-IO of this thread (may be blocked there). */ + if (tclWinProcs.cancelSynchronousIo) { tclWinProcs.cancelSynchronousIo(hThread); } /* - * Wait at most 20 milliseconds for the reader thread to - * close (regarding TIP#398-fast-exit). + * Wait at most 20 milliseconds for the reader thread to close + * (regarding TIP#398-fast-exit). */ - /* if we want TIP#398-fast-exit. */ - if (WaitForSingleObject(hThread, inExit ? 0 : 20) == WAIT_TIMEOUT) { + /* + * If we want TIP#398-fast-exit. + */ + if (WaitForSingleObject(hThread, inExit ? 0 : 20) == WAIT_TIMEOUT) { /* - * The thread must be blocked waiting for the pipe to - * become readable in ReadFile(). There isn't a clean way - * to exit the thread from this condition. We should - * terminate the child process instead to get the reader - * thread to fall out of ReadFile with a FALSE. (below) is - * not the correct way to do this, but will stay here - * until a better solution is found. + * The thread must be blocked waiting for the pipe to become + * readable in ReadFile(). There isn't a clean way to exit the + * thread from this condition. We should terminate the child + * process instead to get the reader thread to fall out of + * ReadFile with a FALSE. (below) is not the correct way to do + * this, but will stay here until a better solution is found. * - * Note that we need to guard against terminating the - * thread while it is in the middle of Tcl_ThreadAlert - * because it won't be able to release the notifier lock. + * Note that we need to guard against terminating the thread + * while it is in the middle of Tcl_ThreadAlert because it + * won't be able to release the notifier lock. * - * Also note that terminating threads during their initialization or teardown phase - * may result in ntdll.dll's LoaderLock to remain locked indefinitely. - * This causes ntdll.dll's LdrpInitializeThread() to deadlock trying to acquire LoaderLock. - * LdrpInitializeThread() is executed within new threads to perform - * initialization and to execute DllMain() of all loaded dlls. - * As a result, all new threads are deadlocked in their initialization phase and never execute, - * even though CreateThread() reports successful thread creation. - * This results in a very weird process-wide behavior, which is extremely hard to debug. + * Also note that terminating threads during their + * initialization or teardown phase may result in ntdll.dll's + * LoaderLock to remain locked indefinitely. This causes + * ntdll.dll's LdrpInitializeThread() to deadlock trying to + * acquire LoaderLock. LdrpInitializeThread() is executed + * within new threads to perform initialization and to execute + * DllMain() of all loaded dlls. As a result, all new threads + * are deadlocked in their initialization phase and never + * execute, even though CreateThread() reports successful + * thread creation. This results in a very weird process-wide + * behavior, which is extremely hard to debug. * * THREADS SHOULD NEVER BE TERMINATED. Period. * - * But for now, check if thread is exiting, and if so, let it die peacefully. + * But for now, check if thread is exiting, and if so, let it + * die peacefully. * - * Also don't terminate if in exit (otherwise deadlocked in ntdll.dll's). + * Also don't terminate if in exit (otherwise deadlocked in + * ntdll.dll's). */ - if ( pipeTI->state != PTI_STATE_DOWN - && WaitForSingleObject(hThread, - inExit ? 50 : 5000) != WAIT_OBJECT_0 - ) { + if (pipeTI->state != PTI_STATE_DOWN + && WaitForSingleObject(hThread, + inExit ? 50 : 5000) != WAIT_OBJECT_0) { /* BUG: this leaks memory */ if (inExit || !TerminateThread(hThread, 0)) { - /* in exit or terminate fails, just give thread a chance to exit */ + /* + * in exit or terminate fails, just give thread a + * chance to exit + */ + if (InterlockedExchange(&pipeTI->state, PTI_STATE_STOP) != PTI_STATE_DOWN) { pipeTI = NULL; } - }; + } } } } @@ -3518,11 +3657,11 @@ TclPipeThreadStop( SetEvent(pipeTI->evWakeUp); } CloseHandle(pipeTI->evControl); - #ifndef _PTI_USE_CKALLOC +#ifndef _PTI_USE_CKALLOC free(pipeTI); - #else +#else ckfree(pipeTI); - #endif +#endif /* !_PTI_USE_CKALLOC */ } } @@ -3551,28 +3690,30 @@ TclPipeThreadExit( { LONG state; TclPipeThreadInfo *pipeTI = *pipeTIPtr; + /* * If state of thread was set to stop (exactly), we can sane free its info * structure, otherwise it is shared with main thread, so main thread will * own it. */ + if (!pipeTI) { return; } *pipeTIPtr = NULL; - if ((state = InterlockedExchange(&pipeTI->state, - PTI_STATE_DOWN)) == PTI_STATE_STOP) { + state = InterlockedExchange(&pipeTI->state, PTI_STATE_DOWN); + if (state == PTI_STATE_STOP) { CloseHandle(pipeTI->evControl); if (pipeTI->evWakeUp) { SetEvent(pipeTI->evWakeUp); } - #ifndef _PTI_USE_CKALLOC +#ifndef _PTI_USE_CKALLOC free(pipeTI); - #else +#else ckfree(pipeTI); /* be sure all subsystems used are finalized */ Tcl_FinalizeThread(); - #endif +#endif /* !_PTI_USE_CKALLOC */ } } |