| ︙ | | |
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
-
-
|
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tclWinPipe.c,v 1.65 2007/02/20 23:24:07 nijtmans Exp $
*/
#include "tclWinInt.h"
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
/*
* The following variable is used to tell whether this module has been
* initialized.
*/
|
| ︙ | | |
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
-
+
-
+
-
+
|
int toRead, int *errorCode);
static int PipeOutputProc(ClientData instanceData,
const char *buf, int toWrite, int *errorCode);
static DWORD WINAPI PipeReaderThread(LPVOID arg);
static void PipeSetupProc(ClientData clientData, int flags);
static void PipeWatchProc(ClientData instanceData, int mask);
static DWORD WINAPI PipeWriterThread(LPVOID arg);
static int TempFileName(WCHAR name[MAX_PATH]);
static int TempFileName(TCHAR name[MAX_PATH]);
static int WaitForRead(PipeInfo *infoPtr, int blocking);
static void PipeThreadActionProc(ClientData instanceData,
int action);
/*
* This structure describes the channel type structure for command pipe based
* I/O.
*/
static Tcl_ChannelType pipeChannelType = {
static const Tcl_ChannelType pipeChannelType = {
"pipe", /* Type name. */
TCL_CHANNEL_VERSION_5, /* v5 channel */
TCL_CLOSE2PROC, /* Close proc. */
PipeInputProc, /* Input proc. */
PipeOutputProc, /* Output proc. */
NULL, /* Seek proc. */
NULL, /* Set option proc. */
NULL, /* Get option proc. */
PipeWatchProc, /* Set up notifier to watch the channel. */
PipeGetHandleProc, /* Get an OS handle from channel. */
PipeClose2Proc, /* close2proc */
PipeBlockModeProc, /* Set blocking or non-blocking mode.*/
NULL, /* flush proc. */
NULL, /* handler proc. */
NULL, /* wide seek proc */
PipeThreadActionProc, /* thread action proc */
NULL, /* truncate */
NULL /* truncate */
};
/*
*----------------------------------------------------------------------
*
* PipeInit --
*
|
| ︙ | | |
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
|
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
|
-
+
-
+
-
-
-
+
-
-
+
-
-
-
-
-
-
+
+
-
-
+
|
* None.
*
*----------------------------------------------------------------------
*/
static int
TempFileName(
WCHAR name[MAX_PATH]) /* Buffer in which name for temporary file
TCHAR name[MAX_PATH]) /* Buffer in which name for temporary file
* gets stored. */
{
TCHAR *prefix;
TCHAR *prefix = TEXT("TCL");
prefix = (tclWinProcs->useWide) ? (TCHAR *) L"TCL" : (TCHAR *) "TCL";
if (tclWinProcs->getTempPathProc(MAX_PATH, name) != 0) {
if (GetTempPath(MAX_PATH, name) != 0) {
if (tclWinProcs->getTempFileNameProc((TCHAR *) name, prefix, 0,
name) != 0) {
if (GetTempFileName(name, prefix, 0, name) != 0) {
return 1;
}
}
if (tclWinProcs->useWide) {
((WCHAR *) name)[0] = '.';
((WCHAR *) name)[1] = '\0';
} else {
((char *) name)[0] = '.';
((char *) name)[1] = '\0';
name[0] = '.';
name[1] = '\0';
}
return tclWinProcs->getTempFileNameProc((TCHAR *) name, prefix, 0, name);
return GetTempFileName(name, prefix, 0, name);
}
/*
*----------------------------------------------------------------------
*
* TclpMakeFile --
*
|
| ︙ | | |
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
|
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
|
-
+
-
+
|
/*
* If the file is not being created, use the existing file attributes.
*/
flags = 0;
if (!(mode & O_CREAT)) {
flags = tclWinProcs->getFileAttributesProc(nativePath);
flags = GetFileAttributes(nativePath);
if (flags == 0xFFFFFFFF) {
flags = 0;
}
}
/*
* Set up the file sharing mode. We want to allow simultaneous access.
*/
shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
/*
* Now we get to create the file.
*/
handle = tclWinProcs->createFileProc(nativePath, accessMode, shareMode,
handle = CreateFile(nativePath, accessMode, shareMode,
NULL, createMode, flags, NULL);
Tcl_DStringFree(&ds);
if (handle == INVALID_HANDLE_VALUE) {
DWORD err;
err = GetLastError();
|
| ︙ | | |
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
|
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
|
-
+
-
+
|
*----------------------------------------------------------------------
*/
TclFile
TclpCreateTempFile(
const char *contents) /* String to write into temp file, or NULL. */
{
WCHAR name[MAX_PATH];
TCHAR name[MAX_PATH];
const char *native;
Tcl_DString dstring;
HANDLE handle;
if (TempFileName(name) == 0) {
return NULL;
}
handle = tclWinProcs->createFileProc((TCHAR *) name,
handle = CreateFile(name,
GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (handle == INVALID_HANDLE_VALUE) {
goto error;
}
/*
|
| ︙ | | |
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
|
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
|
-
+
|
if (contents != NULL) {
Tcl_DStringFree(&dstring);
}
TclWinConvertError(GetLastError());
CloseHandle(handle);
tclWinProcs->deleteFileProc((TCHAR *) name);
DeleteFile(name);
return NULL;
}
/*
*----------------------------------------------------------------------
*
* TclpTempFileName --
|
| ︙ | | |
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
|
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
|
-
+
-
+
|
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
TclpTempFileName(void)
{
WCHAR fileName[MAX_PATH];
TCHAR fileName[MAX_PATH];
if (TempFileName(fileName) == 0) {
return NULL;
}
return TclpNativeToNormalized((ClientData) fileName);
return TclpNativeToNormalized(fileName);
}
/*
*----------------------------------------------------------------------
*
* TclpCreatePipe --
*
|
| ︙ | | |
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
|
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
|
-
+
|
* may be the same as outputFile. */
Tcl_Pid *pidPtr) /* If this function is successful, pidPtr is
* filled with the process id of the child
* process. */
{
int result, applType, createFlags;
Tcl_DString cmdLine; /* Complete command line (TCHAR). */
STARTUPINFOA startInfo;
STARTUPINFO startInfo;
PROCESS_INFORMATION procInfo;
SECURITY_ATTRIBUTES secAtts;
HANDLE hProcess, h, inputHandle, outputHandle, errorHandle;
char execPath[MAX_PATH * TCL_UTF_MAX];
WinFile *filePtr;
PipeInit();
|
| ︙ | | |
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
|
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
|
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
+
-
-
-
-
-
|
if (HasConsole()) {
createFlags = 0;
} else {
createFlags = DETACHED_PROCESS;
}
if (applType == APPL_DOS) {
/*
* Under Windows 95, 16-bit DOS applications do not work well with
* pipes:
*
* 1. EOF on a pipe between a detached 16-bit DOS application and
* another application is not seen at the other end of the pipe,
* so the listening process blocks forever on reads. This inablity
* to detect EOF happens when either a 16-bit app or the 32-bit
* app is the listener.
*
* 2. If a 16-bit DOS application (detached or not) blocks when
* writing to a pipe, it will never wake up again, and it
* eventually brings the whole system down around it.
*
* The 16-bit application is run as a normal process inside of a
* hidden helper console app, and this helper may be run as a
* detached process. If any of the stdio handles is a pipe, the
* helper application accumulates information into temp files and
* forwards it to or from the DOS application as appropriate.
* This means that DOS apps must receive EOF from a stdin pipe
* before they will actually begin, and must finish generating
* stdout or stderr before the data will be sent to the next stage
* of the pipe.
*
* The helper app should be located in the same directory as the
* tcl dll.
*/
Tcl_Obj *tclExePtr, *pipeDllPtr;
char *start, *end;
int i, fileExists;
Tcl_DString pipeDll;
if (createFlags != 0) {
startInfo.wShowWindow = SW_HIDE;
startInfo.dwFlags |= STARTF_USESHOWWINDOW;
createFlags = CREATE_NEW_CONSOLE;
}
Tcl_DStringInit(&pipeDll);
Tcl_DStringAppend(&pipeDll, TCL_PIPE_DLL, -1);
tclExePtr = TclGetObjNameOfExecutable();
Tcl_IncrRefCount(tclExePtr);
start = Tcl_GetStringFromObj(tclExePtr, &i);
for (end = start + (i-1); end > start; end--) {
if (*end == '/') {
break;
}
}
if (*end != '/') {
Tcl_AppendResult(interp, "no / in executable path name \"",
start, "\"", (char *) NULL);
Tcl_DecrRefCount(tclExePtr);
Tcl_DStringFree(&pipeDll);
goto end;
}
i = (end - start) + 1;
pipeDllPtr = Tcl_NewStringObj(start, i);
Tcl_AppendToObj(pipeDllPtr, Tcl_DStringValue(&pipeDll), -1);
Tcl_IncrRefCount(pipeDllPtr);
if (Tcl_FSConvertToPathType(interp, pipeDllPtr) != TCL_OK) {
Tcl_Panic("Tcl_FSConvertToPathType failed");
}
fileExists = (Tcl_FSAccess(pipeDllPtr, F_OK) == 0);
if (!fileExists) {
Tcl_AppendResult(interp, "Tcl pipe dll \"",
Tcl_DStringValue(&pipeDll), "\" not found",
(char *) NULL);
Tcl_AppendResult(interp,
"DOS application process not supported on this platform",
(char *) NULL);
Tcl_DecrRefCount(tclExePtr);
Tcl_DecrRefCount(pipeDllPtr);
Tcl_DStringFree(&pipeDll);
goto end;
goto end;
}
Tcl_DStringAppend(&cmdLine, Tcl_DStringValue(&pipeDll), -1);
Tcl_DecrRefCount(tclExePtr);
Tcl_DecrRefCount(pipeDllPtr);
Tcl_DStringFree(&pipeDll);
}
}
/*
* cmdLine gets the full command line used to invoke the executable,
* including the name of the executable itself. The command line arguments
* in argv[] are stored in cmdLine separated by spaces. Special characters
|
| ︙ | | |
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
|
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
|
-
+
|
* Additionally, when calling a 16-bit dos or windows application, all
* path names must use the short, cryptic, path format (e.g., using
* ab~1.def instead of "a b.default").
*/
BuildCommandLine(execPath, argc, argv, &cmdLine);
if (tclWinProcs->createProcessProc(NULL,
if (CreateProcess(NULL,
(TCHAR *) Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE,
(DWORD) createFlags, NULL, NULL, &startInfo, &procInfo) == 0) {
TclWinConvertError(GetLastError());
Tcl_AppendResult(interp, "couldn't execute \"", argv[0],
"\": ", Tcl_PosixError(interp), (char *) NULL);
goto end;
}
|
| ︙ | | |
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
|
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
|
-
+
|
TCHAR *rest;
char *ext;
char buf[2];
DWORD attr, read;
IMAGE_DOS_HEADER header;
Tcl_DString nameBuf, ds;
const TCHAR *nativeName;
WCHAR nativeFullPath[MAX_PATH];
TCHAR nativeFullPath[MAX_PATH];
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.
*
|
| ︙ | | |
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
|
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
|
-
+
-
+
-
+
-
+
-
+
-
+
|
nameLen = Tcl_DStringLength(&nameBuf);
for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
Tcl_DStringSetLength(&nameBuf, nameLen);
Tcl_DStringAppend(&nameBuf, extensions[i], -1);
nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&nameBuf),
Tcl_DStringLength(&nameBuf), &ds);
found = tclWinProcs->searchPathProc(NULL, nativeName, NULL, MAX_PATH,
found = SearchPath(NULL, nativeName, NULL, MAX_PATH,
nativeFullPath, &rest);
Tcl_DStringFree(&ds);
if (found == 0) {
continue;
}
/*
* Ignore matches on directories or data files, return if identified a
* known type.
*/
attr = tclWinProcs->getFileAttributesProc((TCHAR *) nativeFullPath);
attr = GetFileAttributes(nativeFullPath);
if ((attr == 0xffffffff) || (attr & FILE_ATTRIBUTE_DIRECTORY)) {
continue;
}
strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds));
strcpy(fullName, Tcl_WinTCharToUtf(nativeFullPath, -1, &ds));
Tcl_DStringFree(&ds);
ext = strrchr(fullName, '.');
if ((ext != NULL) && (stricmp(ext, ".bat") == 0)) {
if ((ext != NULL) && (strcasecmp(ext, ".bat") == 0)) {
applType = APPL_DOS;
break;
}
hFile = tclWinProcs->createFileProc((TCHAR *) nativeFullPath,
hFile = CreateFile(nativeFullPath,
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) && (stricmp(ext, ".com") == 0)) {
if ((ext != NULL) && (strcasecmp(ext, ".com") == 0)) {
applType = APPL_DOS;
break;
}
continue;
}
if (header.e_lfarlc != sizeof(header)) {
/*
|
| ︙ | | |
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
|
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
|
-
-
-
+
+
|
/*
* 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.
*/
tclWinProcs->getShortPathNameProc((TCHAR *) nativeFullPath,
nativeFullPath, MAX_PATH);
strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds));
GetShortPathName(nativeFullPath, nativeFullPath, MAX_PATH);
strcpy(fullName, Tcl_WinTCharToUtf(nativeFullPath, -1, &ds));
Tcl_DStringFree(&ds);
}
return applType;
}
/*
*----------------------------------------------------------------------
|
| ︙ | | |
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
|
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
|
-
-
+
+
|
* knows about it.
*
*----------------------------------------------------------------------
*/
void
TclWinAddProcess(
HANDLE hProcess, /* Handle to process */
DWORD id) /* Global process identifier */
void *hProcess, /* Handle to process */
unsigned long id) /* Global process identifier */
{
ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
PipeInit();
procPtr->hProcess = hProcess;
procPtr->dwProcessId = id;
|
| ︙ | | |
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
|
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
|
-
+
-
+
-
-
+
-
-
-
-
+
-
-
+
-
-
-
+
-
-
+
-
-
+
|
Tcl_Channel
TclpOpenTemporaryFile(
Tcl_Obj *dirObj,
Tcl_Obj *basenameObj,
Tcl_Obj *extensionObj,
Tcl_Obj *resultingNameObj)
{
WCHAR name[MAX_PATH];
TCHAR name[MAX_PATH];
char *namePtr;
HANDLE handle;
DWORD flags = FILE_ATTRIBUTE_TEMPORARY;
int length, counter, counter2;
Tcl_DString buf;
if (!resultingNameObj) {
flags |= FILE_FLAG_DELETE_ON_CLOSE;
}
namePtr = (char *) name;
length = tclWinProcs->getTempPathProc(MAX_PATH, name);
length = GetTempPath(MAX_PATH, name);
if (length == 0) {
goto gotError;
}
if (tclWinProcs->useWide) {
namePtr += length * sizeof(WCHAR);
namePtr += length * sizeof(TCHAR);
} else {
namePtr += length;
}
if (basenameObj) {
const char *string = Tcl_GetStringFromObj(basenameObj, &length);
Tcl_WinUtfToTChar(string, length, &buf);
memcpy(namePtr, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf));
namePtr += Tcl_DStringLength(&buf);
Tcl_DStringFree(&buf);
} else {
TCHAR *baseStr = tclWinProcs->useWide ?
TCHAR *baseStr = TEXT("TCL");
(TCHAR *) L"TCL" : (TCHAR *) "TCL";
int length = tclWinProcs->useWide ? 3*sizeof(WCHAR) : 3;
int length = 3 * sizeof(TCHAR);
memcpy(namePtr, baseStr, length);
namePtr += length;
}
counter = TclpGetClicks() % 65533;
counter2 = 1024; /* Only try this many times! Prevents
* an infinite loop. */
do {
char number[TCL_INTEGER_SPACE + 4];
sprintf(number, "%d.TMP", counter);
counter = (unsigned short) (counter + 1);
Tcl_WinUtfToTChar(number, strlen(number), &buf);
memcpy(namePtr, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf));
if (tclWinProcs->useWide) {
*(WCHAR *)(namePtr + Tcl_DStringLength(&buf) + 1) = '\0';
Tcl_DStringSetLength(&buf, Tcl_DStringLength(&buf) + 1);
} else {
namePtr[Tcl_DStringLength(&buf) + 1] = '\0';
memcpy(namePtr, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf) + 1);
}
Tcl_DStringFree(&buf);
handle = tclWinProcs->createFileProc((TCHAR *) name,
handle = CreateFile(name,
GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, flags, NULL);
} while (handle == INVALID_HANDLE_VALUE
&& --counter2 > 0
&& GetLastError() == ERROR_FILE_EXISTS);
if (handle == INVALID_HANDLE_VALUE) {
goto gotError;
}
|
| ︙ | | |