📄 tclwinpipe.c
字号:
* The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32 * if the filename referred to the corresponding application type. * If the file name could not be found or did not refer to any known * application type, APPL_NONE is returned and an error message is * left in interp. .bat files are identified as APPL_DOS. * * Side effects: * None. * *---------------------------------------------------------------------- */static intApplicationType(interp, originalName, fullPath) Tcl_Interp *interp; /* Interp, for error message. */ const char *originalName; /* Name of the application to find. */ char fullPath[MAX_PATH]; /* Filled with complete path to * application. */{ int applType, i; HANDLE hFile; char *ext, *rest; char buf[2]; DWORD read; IMAGE_DOS_HEADER header; static char extensions[][5] = {"", ".com", ".exe", ".bat"}; /* Look for the program as an external program. First try the name * as it is, then try adding .com, .exe, and .bat, in that order, to * the name, looking for an executable. * * Using the raw SearchPath() procedure doesn't do quite what is * necessary. If the name of the executable already contains a '.' * character, it will not try appending the specified extension when * searching (in other words, SearchPath will not find the program * "a.b.exe" if the arguments specified "a.b" and ".exe"). * So, first look for the file as it is named. Then manually append * the extensions, looking for a match. */ applType = APPL_NONE; for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { lstrcpyn(fullPath, originalName, MAX_PATH - 5); lstrcat(fullPath, extensions[i]); SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, &rest); /* * Ignore matches on directories or data files, return if identified * a known type. */ if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) { continue; } ext = strrchr(fullPath, '.'); if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) { applType = APPL_DOS; break; } hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { continue; } header.e_magic = 0; ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL); if (header.e_magic != IMAGE_DOS_SIGNATURE) { /* * Doesn't have the magic number for relocatable executables. If * filename ends with .com, assume it's a DOS application anyhow. * Note that we didn't make this assumption at first, because some * supposed .com files are really 32-bit executables with all the * magic numbers and everything. */ CloseHandle(hFile); if ((ext != NULL) && (strcmpi(ext, ".com") == 0)) { applType = APPL_DOS; break; } continue; } if (header.e_lfarlc != sizeof(header)) { /* * All Windows 3.X and Win32 and some DOS programs have this value * set here. If it doesn't, assume that since it already had the * other magic number it was a DOS application. */ CloseHandle(hFile); applType = APPL_DOS; break; } /* * The DWORD at header.e_lfanew points to yet another magic number. */ buf[0] = '\0'; SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN); ReadFile(hFile, (void *) buf, 2, &read, NULL); CloseHandle(hFile); if ((buf[0] == 'N') && (buf[1] == 'E')) { applType = APPL_WIN3X; } else if ((buf[0] == 'P') && (buf[1] == 'E')) { applType = APPL_WIN32; } else { /* * Strictly speaking, there should be a test that there * is an 'L' and 'E' at buf[0..1], to identify the type as * DOS, but of course we ran into a DOS executable that * _doesn't_ have the magic number -- specifically, one * compiled using the Lahey Fortran90 compiler. */ applType = APPL_DOS; } break; } if (applType == APPL_NONE) { TclWinConvertError(GetLastError()); Tcl_AppendResult(interp, "couldn't execute \"", originalName, "\": ", Tcl_PosixError(interp), (char *) NULL); return APPL_NONE; } if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) { /* * Replace long path name of executable with short path name for * 16-bit applications. Otherwise the application may not be able * to correctly parse its own command line to separate off the * application name from the arguments. */ GetShortPathName(fullPath, fullPath, MAX_PATH); } return applType;}/* *---------------------------------------------------------------------- * * BuildCommandLine -- * * The command line arguments are stored in linePtr separated * by spaces, in a form that CreateProcess() understands. Special * characters in individual arguments from argv[] must be quoted * when being stored in cmdLine. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidBuildCommandLine(argc, argv, linePtr) int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ Tcl_DString *linePtr; /* Initialized Tcl_DString that receives the * command line. */{ char *start, *special; int quote, i; for (i = 0; i < argc; i++) { if (i > 0) { Tcl_DStringAppend(linePtr, " ", 1); } quote = 0; if (argv[i][0] == '\0') { quote = 1; } else { for (start = argv[i]; *start != '\0'; start++) { if (isspace(*start)) { quote = 1; break; } } } if (quote) { Tcl_DStringAppend(linePtr, "\"", 1); } start = argv[i]; for (special = argv[i]; ; ) { if ((*special == '\\') && (special[1] == '\\' || special[1] == '"')) { Tcl_DStringAppend(linePtr, start, special - start); start = special; while (1) { special++; if (*special == '"') { /* * N backslashes followed a quote -> insert * N * 2 + 1 backslashes then a quote. */ Tcl_DStringAppend(linePtr, start, special - start); break; } if (*special != '\\') { break; } } Tcl_DStringAppend(linePtr, start, special - start); start = special; } if (*special == '"') { Tcl_DStringAppend(linePtr, start, special - start); Tcl_DStringAppend(linePtr, "\\\"", 2); start = special + 1; } if (*special == '\0') { break; } special++; } Tcl_DStringAppend(linePtr, start, special - start); if (quote) { Tcl_DStringAppend(linePtr, "\"", 1); } }}/* *---------------------------------------------------------------------- * * MakeTempFile -- * * Helper function for TclpCreateProcess under Win32s. Makes a * temporary file that _won't_ go away automatically when it's file * handle is closed. Used for simulated pipes, which are written * in one pass and reopened and read in the next pass. * * Results: * namePtr is filled with the name of the temporary file. * * Side effects: * A temporary file with the name specified by namePtr is created. * The caller is responsible for deleting this temporary file. * *---------------------------------------------------------------------- */static char *MakeTempFile(namePtr) Tcl_DString *namePtr; /* Initialized Tcl_DString that is filled * with the name of the temporary file that * was created. */{ char name[MAX_PATH]; if (TempFileName(name) == 0) { return NULL; } Tcl_DStringAppend(namePtr, name, -1); return Tcl_DStringValue(namePtr);}/* *---------------------------------------------------------------------- * * CopyChannel -- * * Helper function used by TclpCreateProcess under Win32s. Copies * what remains of source file to destination file; source file * pointer need not be positioned at the beginning of the file if * all of source file is not desired, but data is copied up to end * of source file. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidCopyChannel(dst, src) HANDLE dst; /* Destination file. */ HANDLE src; /* Source file. */{ char buf[8192]; DWORD dwRead, dwWrite; while (ReadFile(src, buf, sizeof(buf), &dwRead, NULL) != FALSE) { if (dwRead == 0) { break; } if (WriteFile(dst, buf, dwRead, &dwWrite, NULL) == FALSE) { break; } }}/* *---------------------------------------------------------------------- * * TclpCreateCommandChannel -- * * This function is called by Tcl_OpenCommandChannel to perform * the platform specific channel initialization for a command * channel. * * Results: * Returns a new channel or NULL on failure. * * Side effects: * Allocates a new channel. * *---------------------------------------------------------------------- */Tcl_ChannelTclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr) TclFile readFile; /* If non-null, gives the file for reading. */ TclFile writeFile; /* If non-null, gives the file for writing. */ TclFile errorFile; /* If non-null, gives the file where errors * can be read. */ int numPids; /* The number of pids in the pid array. */ Tcl_Pid *pidPtr; /* An array of process identifiers. */{ char channelName[20]; int channelId; PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo)); if (!initialized) { PipeInit(); } infoPtr->watchMask = 0; infoPtr->flags = 0; infoPtr->readFile = readFile; infoPtr->writeFile = writeFile; infoPtr->errorFile = errorFile; infoPtr->numPids = numPids; infoPtr->pidPtr = pidPtr; /* * Use one of the fds associated with the channel as the * channel id. */ if (readFile) { WinPipe *pipePtr = (WinPipe *) readFile; if (pipePtr->file.type == WIN32S_PIPE && pipePtr->file.handle == INVALID_HANDLE_VALUE) { pipePtr->file.handle = CreateFile(pipePtr->fileName, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } channelId = (int) pipePtr->file.handle; } else if (writeFile) { channelId = (int) ((WinFile*)writeFile)->handle; } else if (errorFile) { channelId = (int) ((WinFile*)errorFile)->handle; } else { channelId = 0; } infoPtr->validMask = 0; if (readFile != NULL) { infoPtr->validMask |= TCL_READABLE; } if (writeFile != NULL) { infoPtr->validMask |= TCL_WRITABLE; } /* * For backward compatibility with previous versions of Tcl, we * use "file%d" as the base name for pipes even though it would * be more natural to use "pipe%d". */ sprintf(channelName, "file%d", channelId); infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, (ClientData) infoPtr, infoPtr->validMask); /* * Pipes have AUTO translation mode on Windows and ^Z eof char, which * means that a ^Z will be appended to them at close. This is needed * for Windows programs that expect a ^Z at EOF. */ Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, "-translation", "auto"); Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel, "-eofchar", "\032 {}"); return infoPtr->channel;}/* *---------------------------------------------------------------------- * * TclGetAndDetachPids -- * * Stores a list of the command PIDs for a command channel in * interp->result. * * Results: * None. * * Side effects: * Modifies interp->result. * *---------------------------------------------------------------------- */voidTclGetAndDetachPids(interp, chan) Tcl_Interp *interp; Tcl_Channel chan;{ PipeInfo *pipePtr; Tcl_ChannelType *chanTypePtr; int i; char buf[20]; /* * Punt if the channel is not a command channel. */ chanTypePtr = Tcl_GetChannelType(chan); if (chanTypePtr != &pipeChannelType) { return; } pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan); for (i = 0; i < pipePtr->numPids; i++) { sprintf(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i])); Tcl_AppendElement(interp, buf); Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); } if (pipePtr->numPids > 0) { ckfree((char *) pipePtr->pidPtr); pipePtr->numPids = 0; }}/* *---------------------------------------------------------------------- * * PipeBlockModeProc -- * * Set blocking or non-blocking mode on channel. * * Results: * 0 if successful, errno when failed. * * Side effects: * Sets the device into blocking or non-blocking mode. * *---------------------------------------------------------------------- */static intPipeBlockModeProc(instanceData, mode) ClientData instanceData; /* Instance data for channel. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -