5e2c193310db06a4a7dcc9f8747246865020b7d5
[reactos.git] / lib / crtdll / process / spawnve.c
1 /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
2 /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
3 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
4
5 #include <windows.h>
6 #include <msvcrt/stdio.h>
7 #include <msvcrt/stdlib.h>
8 #include <msvcrt/string.h>
9 #include <msvcrt/errno.h>
10 #include <msvcrt/process.h>
11 #include <msvcrt/ctype.h>
12 #include <msvcrt/io.h>
13
14
15 #ifndef F_OK
16  #define F_OK   0x01
17 #endif
18 #ifndef R_OK
19  #define R_OK   0x02
20 #endif
21 #ifndef W_OK
22  #define W_OK   0x04
23 #endif
24 #ifndef X_OK
25  #define X_OK   0x08
26 #endif
27 #ifndef D_OK
28  #define D_OK   0x10
29 #endif
30
31 // information about crtdll file handles is not passed to child
32 int _fileinfo_dll = 0;
33
34 static int
35 direct_exec_tail(const char* program, const char* args,
36                  char* const envp[],
37                  PROCESS_INFORMATION* ProcessInformation)
38 {
39     static STARTUPINFO StartupInfo;
40
41     StartupInfo.cb = sizeof(STARTUPINFO);
42     StartupInfo.lpReserved= NULL;
43     StartupInfo.dwFlags = 0;
44     StartupInfo.wShowWindow = SW_SHOWDEFAULT; 
45     StartupInfo.lpReserved2 = NULL;
46     StartupInfo.cbReserved2 = 0; 
47     if (!CreateProcessA((char*)program,(char*)args,NULL,NULL,FALSE,0,(char**)envp,NULL,&StartupInfo,ProcessInformation)) {
48         __set_errno( GetLastError() );
49         return -1;
50     }
51     return (int)ProcessInformation->hProcess;
52 }
53
54 static int vdm_exec(const char* program, char** argv, char** envp,
55                     PROCESS_INFORMATION* ProcessInformation)
56 {
57     static char args[1024];
58     int i = 0;
59     args[0] = 0;
60
61     strcpy(args, "vdm.exe ");
62     while (argv[i] != NULL) {
63         strcat(args, argv[i]);
64         strcat(args, " ");
65         i++; 
66     }
67     return direct_exec_tail(program,args,envp,ProcessInformation);
68 }
69
70 static int go32_exec(const char* program, char** argv, char** envp,
71                      PROCESS_INFORMATION* ProcessInformation)
72 {
73     static char args[1024];
74     static char envblock[2048];
75     char* penvblock;
76     int i = 0;
77
78     envblock[0] = 0;
79     penvblock=envblock;
80     while(envp[i] != NULL ) {
81           strcat(penvblock,envp[i]);
82           penvblock+=strlen(envp[i])+1;
83           i++; 
84     }
85     penvblock[0]=0;
86     args[0] = 0;
87     i = 0;
88     while(argv[i] != NULL ) {
89           strcat(args,argv[i]);
90           strcat(args," ");
91           i++; 
92     }
93     return direct_exec_tail(program,args,envp,ProcessInformation);
94 }
95
96 int command_exec(const char* program, char** argv, char** envp,
97                  PROCESS_INFORMATION* ProcessInformation)
98 {
99     static char args[1024];
100     int i = 0;
101
102     args[0] = 0;
103     strcpy(args,"cmd.exe  /c ");
104     while(argv[i] != NULL ) {
105         strcat(args,argv[i]);
106         strcat(args," ");
107         i++; 
108     }
109     return direct_exec_tail(program,args,envp,ProcessInformation);
110 }
111
112 static int script_exec(const char* program, char** argv, char** envp,
113                        PROCESS_INFORMATION* ProcessInformation)
114 {
115     return 0;
116 }
117
118
119 /* Note: the following list is not supposed to mention *every*
120    possible extension of an executable file.  It only mentions
121    those extensions that can be *omitted* when you invoke the
122    executable from one of the shells used on MSDOS.  */
123 static struct {
124     const char* extension;
125     int (*interp)(const char*, char**, char**, PROCESS_INFORMATION*);
126 } interpreters[] = {
127     { ".com", vdm_exec },
128     { ".exe", go32_exec },
129     { ".dll", go32_exec },
130     { ".cmd", command_exec },
131     { ".bat", command_exec },
132     { ".btm", command_exec },
133     { ".sh",  script_exec },   /* for compatibility with ms_sh */
134     { ".ksh", script_exec },
135     { ".pl",  script_exec },   /* Perl */
136     { ".sed", script_exec },
137     { "",     go32_exec },
138     { 0,      script_exec },   /* every extension not mentioned above calls it */
139     { 0,      0 },
140 };
141
142 /* This is the index into the above array of the interpreter
143    which is called when the program filename has no extension.  */
144 #define INTERP_NO_EXT (sizeof(interpreters)/sizeof(interpreters[0]) - 3)
145
146 /*-------------------------------------------------*/
147
148 int _spawnve(int mode, const char* path, char* const argv[], char* const envp[])
149 {
150     /* This is the one that does the work! */
151     PROCESS_INFORMATION ProcessInformation;
152     union { char* const* x; char** p; } u;
153     int i = -1;
154     char** argvp;
155     char** envpp;
156     char rpath[FILENAME_MAX], *rp, *rd = 0;
157     int e = errno;
158     int is_dir = 0;
159     int found = 0;
160     DWORD ExitCode;
161
162     if (path == 0 || argv[0] == 0) {
163         errno = EINVAL;
164         return -1;
165     }
166     if (strlen(path) > FILENAME_MAX - 1) {
167         errno = ENAMETOOLONG;
168         return -1;
169     }
170     u.x = argv; argvp = u.p;
171     u.x = envp; envpp = u.p;
172     fflush(stdout); /* just in case */
173     for (rp=rpath; *path; *rp++ = *path++) {
174     if (*path == '.')
175         rd = rp;
176         if (*path == '\\' || *path == '/')
177             rd = 0;
178     }
179     *rp = 0;
180
181     /* If LFN is supported on the volume where rpath resides, we
182        might have something like foo.bar.exe or even foo.exe.com.
183        If so, look for RPATH.ext before even trying RPATH itself. */
184     if (!rd) {
185         for (i=0; interpreters[i].extension; i++) {
186             strcpy(rp, interpreters[i].extension);
187             if (_access(rpath, F_OK) == 0 && !(is_dir = (_access(rpath, D_OK) == 0))) {
188                 found = 1;
189                 break;
190             }
191         }
192     }
193
194     if (!found) {
195         const char *rpath_ext;
196
197         if (rd) {
198             i = 0;
199             rpath_ext = rd;
200         } else {
201             i = INTERP_NO_EXT;
202             rpath_ext = "";
203         }
204         for ( ; interpreters[i].extension; i++)
205             if (_stricmp(rpath_ext, interpreters[i].extension) == 0
206                 && _access(rpath, F_OK) == 0
207                 && !(is_dir = (_access(rpath, D_OK) == 0)))
208             {
209                 found = 1;
210                 break;
211             }
212     }
213     if (!found) {
214         errno = is_dir ? EISDIR : ENOENT;
215         return -1;
216     }
217     errno = e;
218     i = interpreters[i].interp(rpath, argvp, envpp, &ProcessInformation);
219     if (mode == P_OVERLAY)
220         exit(i);
221     if (mode == P_WAIT) {
222         WaitForSingleObject(ProcessInformation.hProcess,INFINITE);
223         GetExitCodeProcess(ProcessInformation.hProcess,&ExitCode);
224         i = (int)ExitCode;
225     }
226     return i;
227 }
228
229 const char* find_exec(char* path, char* rpath)
230 {
231     char *rp, *rd=0;
232     int i;
233     int is_dir = 0;
234     int found = 0;
235
236     if (path == 0 )
237         return 0;
238     if (strlen(path) > FILENAME_MAX - 1)
239         return path;
240
241     /* copy path in rpath */
242     for (rd=path,rp=rpath; *rd; *rp++ = *rd++)
243     ;
244     *rp = 0;
245     /* try first with the name as is */
246     for (i=0; interpreters[i].extension; i++) {
247         strcpy(rp, interpreters[i].extension);
248         if (_access(rpath, F_OK) == 0 && !(is_dir = (_access(rpath, D_OK) == 0))) {
249             found = 1;
250             break;
251         }
252     }
253     if (!found) {
254         /* search in the PATH */
255         char winpath[MAX_PATH];
256         if (GetEnvironmentVariableA("PATH", winpath, MAX_PATH)) {
257             char* ep = winpath;
258             while (*ep) {
259                 if (*ep == ';') ep++;
260                 rp = rpath;
261                 for ( ; *ep && (*ep != ';') ; *rp++ = *ep++)
262                 ;
263                 *rp++ = '/';
264                 for (rd = path ; *rd ; *rp++ = *rd++)
265                 ;
266                 for (i = 0; interpreters[i].extension; i++) {
267                     strcpy(rp, interpreters[i].extension);
268                     if (_access(rpath, F_OK) == 0 && !(is_dir = (_access(rpath, D_OK) == 0))) {
269                         found = 1;
270                         break;
271                     }
272                 }
273                 if (found) break;
274             }
275         }
276     }
277     if (!found)
278         return path;
279     return rpath;
280 }
281
282 int _spawnvpe(int nMode, const char* szPath, char* const* szaArgv, char* const* szaEnv)
283 {
284     char rpath[FILENAME_MAX];
285
286     return _spawnve(nMode, find_exec((char*)szPath,rpath), szaArgv, szaEnv);
287 }