-
Notifications
You must be signed in to change notification settings - Fork 41
/
conspawn.c
203 lines (164 loc) · 7.74 KB
/
conspawn.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* CONSPAWN.C (c) "Fish" (David B. Trout), 2005-2012 */
/* Spawn console command */
/*-------------------------------------------------------------------*/
/* This program is spawned by Hercules as a result of */
/* the 'sh' (shell) command. Its purpose is to simply */
/* call the host's shell (command interpreter) program */
/* with the arguments supplied (usually to invoke yet */
/* another program), redirecting the results back to */
/* Hercules for display. NOTE that this program does */
/* not perform the actual stdio redirection itself, but */
/* rather relies on Hercules to have set that up before */
/* invoking this program. */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#define PGMNAME "conspawn"
int main(int argc, char* argv[])
{
int i, k, rc;
char* p;
#ifdef _MSVC_
#pragma comment(lib,"shell32.lib") // (need ShellExecute)
// --------------------------------------------------------
// PROGRAMMING NOTE: We MUST use "ShellExecute" for Windows
// GUI programs since: 1) GUI programs don't use stdio,
// 2) they never exit until the user manually closes them.
//
// Erroneously using the 'system()' function to start a GUI
// program causes HercGUI to hang at PowerOff as it waits
// for its child process to close its stdio handles which
// GUI programs never do until they exit (which they never
// do until the user manually closes them).
//
// The reason this phenomenon occurs even though Hercules
// does indeed close ITS OWN stdio handles when it ends is
// because its child processes that it creates ALSO inherit
// the same stdio handles! (I.e. the GUI programs that Herc
// starts end up never closing "Herc's" [inherited] stdio
// handles, which are the same handles that HercGUI waits
// on! Thus GUI programs started by Herc using the 'system'
// API end up hanging HercGUI! (during Herc PowerOff))
//
// Thus, for GUI apps, we MUST use "ShellExecute" here
// to prevent HercGUI from hanging at PowerOff. Also note
// that this hang obviously does not occur when Hercules
// is run in command-line (non-HercGUI) mode even when
// the 'system()' API is erroneously used, since Herc is
// obviously (duh!) not being run under the control of an
// external GUI in such a situation. -- Fish, Aug. 2006
// --------------------------------------------------------
if (argc >= 2 && strcasecmp(argv[1],"startgui") == 0)
{
////////////////////////////////////////////////////////
// Windows GUI program; no stdio; use 'ShellExecute'...
////////////////////////////////////////////////////////
// REFERENCE: upon entry:
// argv[0] "conspawn"
// argv[1] "startgui"
// argv[2] (program to start)
// argv[3..n] (arguments for program)
HWND hwnd = NULL;
LPCTSTR lpOperation = NULL;
LPCTSTR lpFile = argv[2];
LPCTSTR lpParameters = NULL;
LPCTSTR lpDirectory = NULL;
INT nShowCmd = SW_SHOWNORMAL;
char* pszErrMsg = NULL;
// Build arguments string from passed args...
for (i=3, k=0; i < argc; i++)
k += strlen(argv[i]) + 1;
if (k)
{
// (allocate room for arguments string)
if (!(p = malloc(sizeof(char)*k)))
{
errno = ENOMEM;
perror( PGMNAME );
return -1;
}
*p = 0;
// (build arguments string from args)
for (i=3; i < argc; ++i)
{
strcat(p,argv[i]);
if (i != (argc-1)) strcat(p," ");
}
lpParameters = p;
}
else
p = NULL;
rc = (intptr_t) ShellExecute( hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd );
if (p)
free(p);
if ( rc > 32)
return 0; // rc > greater than 32 == success...
// rc <= less than or equal 32 == error...
switch (rc)
{
case 0: pszErrMsg = "The operating system is out of memory or resources"; break;
case SE_ERR_FNF: pszErrMsg = "The specified file was not found"; break;
case SE_ERR_PNF: pszErrMsg = "The specified path was not found"; break;
case SE_ERR_ACCESSDENIED: pszErrMsg = "The operating system denied access to the specified file"; break;
case ERROR_BAD_FORMAT: pszErrMsg = "The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image)"; break;
case SE_ERR_OOM: pszErrMsg = "There was not enough memory to complete the operation"; break;
case SE_ERR_DLLNOTFOUND: pszErrMsg = "The specified dynamic-link library (DLL) was not found"; break;
case SE_ERR_SHARE: pszErrMsg = "A sharing violation occurred"; break;
case SE_ERR_ASSOCINCOMPLETE: pszErrMsg = "The file name association is incomplete or invalid"; break;
case SE_ERR_DDETIMEOUT: pszErrMsg = "The DDE transaction could not be completed because the request timed out"; break;
case SE_ERR_DDEFAIL: pszErrMsg = "The DDE transaction failed"; break;
case SE_ERR_DDEBUSY: pszErrMsg = "The Dynamic Data Exchange (DDE) transaction could not be completed because other DDE transactions were being processed"; break;
case SE_ERR_NOASSOC: pszErrMsg = "There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable"; break;
default:
printf(PGMNAME": ShellExecute(\"%s\", \"%s\",...) failed: Unknown error; rc=%d (0x%08.8X).\n",
lpFile, lpParameters, rc, rc );
return -1;
}
printf( PGMNAME": ShellExecute(\"%s\", \"%s\",...) failed: %s.\n",
lpFile, lpParameters, pszErrMsg );
return -1;
}
#endif // _MSVC_
////////////////////////////////////////////////////////
// Command line program using stdio; use 'system()'...
////////////////////////////////////////////////////////
// Re-build a complete command line from passed args...
for (i=1, k=0; i < argc; i++)
k += strlen(argv[i]) + 1;
if (!k)
{
errno = EINVAL;
perror( PGMNAME );
printf( PGMNAME": Usage: command [args]\n");
return -1;
}
// (allocate room for command line)
if (!(p = malloc(sizeof(char)*k)))
{
errno = ENOMEM;
perror( PGMNAME );
return -1;
}
*p = 0;
// (rebuild command-line from args)
for (i=1; i < argc; ++i)
{
strcat(p,argv[i]);
if (i != (argc-1)) strcat(p," ");
}
// Ask system() to process command line...
// NOTE: the below call WILL NOT RETURN
// until the program being called exits!
rc = system(p);
free(p);
// --------------------------------------------------------
// PROGRAMMING NOTE: only rc == -1 need be reported since,
// if the command interpreter called a batch/cmd file for
// example, it could have set its own custom return code.
//
// Only -1 means the system() call itself failed, which
// is the only thing that actually needs to be reported.
// --------------------------------------------------------
if ( -1 == rc )
perror( PGMNAME );
return rc;
}