1 /* The routines in this file provide support for external program
2 execution under the Microsoft Windows environment on an IBM-PC or
3 compatible computer.
4
5 Must be compiled with Borland C++ 2.0 or MSC 6.0 or later versions
6
7 It should not be compiled if the WINDOW_MSWIN symbol is not set */
8
9 #include "estruct.h"
10 #include <stdio.h>
11 #include "eproto.h"
12 #include "edef.h"
13 #include "elang.h"
14
15 #include "mswin.h"
16
17 #define CMDLENGTH 256+NFILEN
18
19 /* local data */
20 #if WINDOW_MSWIN32
21 static STARTUPINFO suInfo = { 0 };
22 static char suTitle [] = PROGNAME "'s subshell";
23 static PROCESS_INFORMATION pInfo = { 0 };
24 #else
25 static HWND hPrgWnd; /* window handle of the external program task */
26 #endif
27
28
29 /* HandleTimer: checks the existence of the external program window */
30 /* =========== */
31
HandleTimer(HWND hDlg)32 static void PASCAL HandleTimer (HWND hDlg)
33
34 /* This function uses a 200ms timeout to check the existence of the window
35 indicated by hPrgWnd. When that window handle becomes invalid, it is
36 assumed that the external program has exited and the WAITFORPRG
37 dialog box is terminated with a TRUE result. If there is a problem
38 setting the timer, the dialog box is terminated with a FALSE result
39 after a message box announcing the problem has been displayed. */
40 {
41 #if WINDOW_MSWIN32
42 /* check for process completion */
43 if (WaitForSingleObject(pInfo.hProcess, 0) == WAIT_TIMEOUT) {
44 #else
45 /* look for the program window */
46 if (IsWindow (hPrgWnd)) {
47 #endif
48 while (!SetTimer (hDlg, 1, 200, NULL)) {
49 /* bad: problem setting the timer */
50 if (MessageBox (hDlg, TEXT337, NULL, MB_RETRYCANCEL) == IDCANCEL) {
51 /* "cannot monitor external program" */
52 EndDialog (hDlg, FALSE); /* give up! */
53 return;
54 }
55 /* else: attempt a retry */
56 }
57 }
58 else {
59 EndDialog (hDlg, TRUE);
60 }
61 } /* HandleTimer */
62
63 /* WAITFORPRGDlgProc: dialog proc for WAITFORPRG dialog box */
64 /* ================= */
65 int EXPORT FAR PASCAL WAITFORPRGDlgProc (HWND hDlg, UINT wMsg,
66 UINT wParam, LONG lParam)
67 {
68 switch (wMsg) {
69 case WM_INITDIALOG:
70 SetWindowText (hDlg, PROGNAME);
71 HandleTimer (hDlg);
72 return TRUE;
73 case WM_TIMER:
74 HandleTimer (hDlg);
75 break;
76 case WM_COMMAND:
77 if (LOWORD(wParam) == 2) { /* Cancel */
78 KillTimer (hDlg, 1);
79 EndDialog (hDlg, FALSE);
80 }
81 break;
82 default:
83 return FALSE;
84 }
85 return FALSE;
86 } /* WAITFORPRGDlgProc */
87
88 /* LaunchPrgEnumProc: used by LaunchPrg */
89 /* ================= */
90 BOOL EXPORT FAR PASCAL LaunchPrgEnumProc (HWND hWnd, LONG lParam)
91
92 /* this function sets hPrgWnd when it finds a window that matches the
93 module instance handle passed in lParam */
94 {
95 #if !WINDOW_MSWIN32
96 if (GetWindowWord (hWnd, GWW_HINSTANCE) == LOWORD(lParam)) {
97 hPrgWnd = hWnd;
98 return FALSE; /* found it, stop enumerating */
99 }
100 #endif
101 return TRUE;
102 } /* LaunchPrgEnumProc */
103
104 /* LaunchPrg: launches and monitors an external program */
105 /* ========= */
106
107 static BOOL PASCAL LaunchPrg (char *Cmd, BOOL DOSApp,
108 char *InFile, char *OutFile)
109
110 /* Returns TRUE if all went well, FALSE if wait cancelled and FAILD if
111 failed to launch.
112
113 Cmd is the command string to launch.
114
115 DOSApp is TRUE if the external program is a DOS program to be run
116 under a DOS shell. If DOSApp is FALSE, the program is launched
117 directly as a Windows application. In that case, the InFile parameter
118 is ignored, and the value of the OutFile parameter is used only to
119 determine if the program should be monitored. the text of the string
120 referenced by OutFile is irrelevant.
121
122 InFile is the name of the file to pipe into stdin (if NULL, nothing
123 is piped in)
124
125 OutFile is the name of the file where stdout is expected to be
126 redirected. If it is NULL or an empty string, stdout is not redirected
127
128 If Outfile is NULL, LaunchPrg returns immediately after starting the
129 DOS box.
130
131 If OutFile is not NULL, the external program is monitored.
132 LaunchPrg returns only when the external program has terminated or
133 the user has cancelled the wait (in which case LaunchPrg returns
134 FALSE). */
135 {
136 char FullCmd [CMDLENGTH];
137 HANDLE hModule;
138 int nCmdShow;
139 BOOL Synchronize;
140 #if !WINDOW_MSWIN32
141 FARPROC ProcInstance;
142 #endif
143
144 if (OutFile) {
145 Synchronize = TRUE;
146 if (*OutFile == '\0') OutFile = NULL; /* stop worrying about that
147 empty string */
148 }
149 else Synchronize = FALSE;
150
151 if (SetWorkingDir () != 0) {
152 mlwrite (TEXT334); /* "[No such directory]" */
153 return FALSE;
154 }
155 if (DOSApp) {
156 #if WINDOW_MSWIN32
157 GetPrivateProfileString (ProgName, "Shell", "cmd.exe", FullCmd,
158 CMDLENGTH, IniFile);
159
160 /* the Shell profile string should contain the name of the shell
161 to be used for pipe-command, filter buffer, shell-command and
162 i-shell. The ShellExecOption profile string should contain
163 the option flags that cause the execution under that shell of
164 a single command (the text of which is appended to the profile
165 string) so that the shell terminates with that command. */
166 if (Cmd) {
167 char ExecOption [10];
168
169 GetPrivateProfileString (ProgName, "ShellExecOption", " /c ",
170 ExecOption, 10, IniFile);
171
172 if ((strlen (FullCmd) + strlen (ExecOption) + strlen (Cmd)) >=
173 CMDLENGTH) return FALSE;
174 strcat (FullCmd, ExecOption);
175 strcat (FullCmd, Cmd);
176 }
177 #else
178 if (Synchronize || !Cmd) {
179 GetPrivateProfileString (ProgName, "DOSExec", "",
180 FullCmd, CMDLENGTH, IniFile);
181
182 if (FullCmd[0] == '\0') { /* try to find it on the "path" */
183 char *s;
184
185 if ((s = flook ("DOSEXEC.PIF", TRUE)) != NULL) {
186 strcpy (FullCmd, s);
187 }
188 }
189 }
190 else FullCmd[0] = '\0';
191 if (FullCmd[0] == '\0') {
192 GetPrivateProfileString (ProgName, "DOSBox", "",
193 FullCmd, CMDLENGTH, IniFile);
194
195 if (FullCmd[0] == '\0') { /* try to find it on the "path" */
196 char *s;
197
198 if ((s = flook ("DOSBOX.PIF", TRUE)) != NULL) {
199 strcpy (FullCmd, s);
200 }
201 else strcpy (FullCmd, "command.com");
202 }
203 }
204 /* the DOSBox profileString should be the name of a PIF file for
205 command.com that specifies no arguments and no starting dir.
206 The DOSExec should be similar but specify "Close window on
207 exit" so that synchronization can work */
208 if (Cmd) {
209 if ((strlen (FullCmd) + strlen (Cmd) + 4) >=
210 CMDLENGTH) return FALSE;
211 strcat (FullCmd, " /c ");
212 strcat (FullCmd, Cmd);
213 }
214 #endif
215 if (InFile) {
216 if ((strlen (FullCmd) + strlen (InFile) + 2) >=
217 CMDLENGTH) return FALSE;
218 strcat (FullCmd, " <");
219 strcat (FullCmd, InFile);
220 }
221 if (OutFile) {
222 if ((strlen (FullCmd) + strlen (OutFile) + 2) >=
223 CMDLENGTH) return FALSE;
224 strcat (FullCmd, " >");
225 strcat (FullCmd, OutFile);
226 }
227 }
228 #if WINDOW_MSWIN32
229 /* set the startup window size */
230 suInfo.cb = sizeof(STARTUPINFO);
231 if (DOSApp && Cmd) suInfo.lpTitle = suTitle;
232 suInfo.wShowWindow = (DOSApp && Synchronize) ? SW_SHOWMINIMIZED :
233 SW_SHOWNORMAL;
234 suInfo.dwFlags = STARTF_USESHOWWINDOW;
235
236 /* start the process and get a handle on it */
237 if (CreateProcess (NULL, DOSApp ? FullCmd : Cmd, NULL, NULL,
238 DETACHED_PROCESS,
239 FALSE, NULL, NULL, &suInfo, &pInfo)) {
240 int Result;
241
242 if (Synchronize) {
243 /* put up a dialog box to wait for termination */
244 Result = DialogBox (hEmacsInstance, "WAITFORPRG",
245 hFrameWnd, WAITFORPRGDlgProc);
246 }
247 else {
248 /* no need to synchronize */
249 Result = TRUE;
250 }
251 CloseHandle(pInfo.hThread);
252 CloseHandle(pInfo.hProcess);
253
254 return Result;
255 } else return FALSE;
256 #else
257 if (Win386Enhanced) {
258 if (DOSApp && Synchronize) nCmdShow = SW_SHOWMINIMIZED;
259 else nCmdShow = SW_SHOWNORMAL;
260 }
261 else nCmdShow = SW_SHOWNORMAL;
262
263 hModule = WinExec (DOSApp ? FullCmd : Cmd, nCmdShow);
264 /* here we GOoooo */
265
266 if (hModule < 32) {
267 mlwrite (TEXT3); /* "[Execution failed]" */
268 return FAILD;
269 }
270 if (!Synchronize) return TRUE; /* no synchronization */
271 hPrgWnd = 0;
272 ProcInstance = MakeProcInstance ((FARPROC)LaunchPrgEnumProc,
273 hEmacsInstance);
274 EnumWindows (ProcInstance, (DWORD)hModule);
275 FreeProcInstance (ProcInstance);
276 if (hPrgWnd != 0) {
277 /*-put up a dialog box to wait for the external program termination */
278 int Result;
279
280 ProcInstance = MakeProcInstance ((FARPROC)WAITFORPRGDlgProc,
281 hEmacsInstance);
282 Result = DialogBox (hEmacsInstance, "WAITFORPRG",
283 hFrameWnd, ProcInstance);
284 FreeProcInstance (ProcInstance);
285 return Result;
286 }
287 else return TRUE; /* we assume it has zipped past us! */
288 #endif
289 } /* LaunchPrg */
290
291 /* spawncli: launch DOS shell. Bound to ^X-C */
292 /* ======== */
293
294 PASCAL spawncli (int f, int n)
295 {
296 /*-don't allow this command if restricted */
297 if (restflag) return resterr();
298
299 return LaunchPrg (NULL, TRUE, NULL, NULL);
300 } /* spawncli */
301
302 /* spawn: run a one-liner in a DOS box. Bound to ^X-! */
303 /* ===== */
304
305 PASCAL spawn (int f, int n)
306 {
307 char Line[NLINE];
308 int Result;
309 char *SynchOption;
310 static char empty[] = "";
311
312 /*-don't allow this command if restricted */
313 if (restflag) return resterr();
314
315 if ((Result = mlreply ("!", Line, NLINE)) != TRUE) return Result;
316 if (f) SynchOption = &empty[0];
317 else SynchOption = NULL;
318 return LaunchPrg (Line, TRUE, NULL, SynchOption);
319 } /* spawn */
320
321 /* execprg: run another program with arguments. Bound to ^X-$ */
322 /* ======= */
323
324 PASCAL execprg (int f, int n)
325 {
326 char Line[NLINE];
327 int Result;
328 char *SynchOption;
329
330 /*-don't allow this command if restricted */
331 if (restflag) return resterr();
332
333 /*-get the program command line */
334 if (mlreply ("$", Line, NLINE) != TRUE) return FALSE;
335
336 if (f) SynchOption = &Line[0]; /* any not NULL will do */
337 else SynchOption = NULL;
338 Result = LaunchPrg (Line, FALSE, NULL, SynchOption);
339 if (Result == FAILD) {
340 mlwrite (TEXT3); /* "[Execution failed]" */
341 }
342 return Result;
343 } /* execprg */
344
345 /* pipecmd: pipe a one-liner into a window. Bound to ^X-@ */
346 /* ======= */
347
348 PASCAL pipecmd (int f, int n)
349
350 /* this function fills a buffer named "command" with the output of the
351 DOS one-liner. If the command buffer already exist, it is overwritten
352 only if it has not been changed */
353 {
354 char Line[NLINE];
355 char OutFile[NFILEN];
356 static char bname[] = "command";
357 BUFFER *bp;
358 EWINDOW *wp;
359 int Result;
360 int bmode;
361 char bflag;
362 #if WINDOW_MSWIN32
363 char TempDir[NFILEN] = "\\";
364 #endif
365
366 /*-don't allow this command if restricted */
367 if (restflag) return resterr();
368
369 /*-get the command to pipe-in */
370 if (mlreply ("@", Line, NLINE) != TRUE) return FALSE;
371
372 /*-find the "command" buffer */
373 if ((bp = bfind (bname, FALSE, 0)) != NULL) {
374 /*-make sure the contents can safely be blown away */
375 if (bp->b_flag & BFCHG) {
376 if (mlyesno (TEXT32) != TRUE) return FALSE;
377 /* discard changes */
378 }
379 }
380 else if ((bp = bfind (bname, TRUE, 0)) == NULL) {
381 mlwrite (TEXT137);
382 /* cannot create buffer */
383 return FALSE;
384 }
385 #if WINDOW_MSWIN32
386 GetTempPath (NFILEN, TempDir);
387 GetTempFileName (TempDir, "UE", 0, OutFile);
388 #else
389 GetTempFileName (0, "UE", 0, OutFile);
390 #endif
391 Result = LaunchPrg (Line, TRUE, NULL, OutFile);
392 if (Result == FAILD) {
393 mlwrite (TEXT3);
394 /* [execution failed] */
395 unlink (OutFile);
396 }
397 else {
398 if (Result == TRUE) {
399 BUFFER *temp_bp;
400
401 temp_bp = curbp;
402 swbuffer (bp); /* make this buffer the current one */
403 bmode = bp->b_mode;
404 bp->b_mode &= ~MDVIEW;
405 bflag = bp->b_flag;
406 bp->b_flag &= ~BFCHG;
407 Result = readin (OutFile, FALSE);
408 bp->b_fname[0] = '\0'; /* clear file name */
409 if (Result == TRUE) {
410 bp->b_mode |= MDVIEW; /* force VIEW mode */
411 lchange (WFMODE); /* update all relevant mode lines */
412 bp->b_flag &= ~BFCHG; /* remove by-product BFCHG flag */
413 }
414 else {
415 bp->b_mode = bmode; /* restore mode */
416 bp->b_flag = bflag;
417 swbuffer (temp_bp);
418 }
419 unlink (OutFile);
420 /* note that the file is not deleted if the wait was cancelled */
421 }
422 }
423 return Result;
424 } /* pipecmd */
425
426 /* filter: filter a buffer through a DOS box. Bound to ^X-# */
427 /* ====== */
428
429 PASCAL filter (int f, int n)
430 {
431 char Line[NLINE];
432 char InFile[NFILEN];
433 char OutFile[NFILEN];
434 char fname[NFILEN];
435 BUFFER *bp;
436 EWINDOW *wp;
437 int Result;
438 #if WINDOW_MSWIN32
439 char TempDir[NFILEN] = "\\";
440 #endif
441
442 /*-don't allow this command if restricted */
443 if (restflag) return resterr();
444
445 /*-get the filter command line */
446 if (mlreply ("#", Line, NLINE) != TRUE) return FALSE;
447
448 bp = curbp;
449 strcpy (fname, bp->b_fname);
450 #if WINDOW_MSWIN32
451 GetTempPath (NFILEN, TempDir);
452 GetTempFileName (TempDir, "UE", 0, InFile);
453 #else
454 GetTempFileName (0, "UE", 0, InFile);
455 #endif
456 Result = writeout (InFile, "w");
457 if (Result != TRUE) {
458 mlwrite (TEXT2);
459 /* cannot write filter file */
460 }
461 else {
462 #if WINDOW_MSWIN32
463 GetTempFileName (TempDir, "UE", 0, OutFile);
464 #else
465 GetTempFileName (0, "UE", 0, OutFile);
466 #endif
467 Result = LaunchPrg (Line, TRUE, InFile, OutFile);
468 if (Result == FAILD) {
469 mlwrite (TEXT3);
470 /* [execution failed] */
471 unlink (OutFile);
472 unlink (InFile);
473 }
474 else {
475 if (Result == TRUE) {
476 Result = readin (OutFile, FALSE);
477 unlink (OutFile);
478 unlink (InFile);
479 }
480 /* note that he files are not deleted if the wait was cancelled */
481 if (Result == TRUE) {
482 lchange (WFMODE); /* update all relevant mode lines */
483 }
484 }
485 }
486 strcpy (bp->b_fname, fname); /* restore original file name */
487 return Result;
488 } /* filter */
489