update dev300-m58
[ooovba.git] / dmake / msdos / spawn.c
blobc5e995565c78629c1d06e954d2f2a9aa3e3b9dc1
1 /* RCS $Id: spawn.c,v 1.3 2007-10-15 15:43:28 ihi Exp $
2 --
3 -- SYNOPSIS
4 -- Spawnvpe code to emulate spawnvpe call common to DOS compilers.
5 --
6 -- DESCRIPTION
7 -- This implementation is further integrated into dmake in that it
8 -- determines the program to execute and if it's extension is either
9 -- .bat or .ksh it executes it using the appropriate shell based on the
10 -- setting of .MKSARGS. If .MKSARGS is set then in addition
11 -- to the command tail getting built the arguments are also passed in the
12 -- environment pursuant to the published MKS argument passing conventions.
13 -- If the variable Swap_on_exec is set and the DOS OS supports it
14 -- then the dmake executable image is swapped to secondary storage prior
15 -- to running the child process. This is requested by setting the
16 -- appropriate flag in the call to exec.
18 -- This and the exec.asm routine are derived from work that was supplied
19 -- to me by Kent Williams (williams@umaxc.weeg.uiowa.edu) and by
20 -- Len Reed, (..!gatech!holos0!lbr or holos0!lbr@gatech.edu., Holos
21 -- Software, Inc., Tucker, Ga.). I sincerely acknowledge their help since
22 -- their Turbo C, and MSC 6.0 code lead directly to this combined
23 -- swapping exec that hopefully works with either compiler in all memory
24 -- models.
26 -- AUTHOR
27 -- Dennis Vadura, dvadura@dmake.wticorp.com
29 -- WWW
30 -- http://dmake.wticorp.com/
32 -- COPYRIGHT
33 -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
34 --
35 -- This program is NOT free software; you can redistribute it and/or
36 -- modify it under the terms of the Software License Agreement Provided
37 -- in the file <distribution-root>/readme/license.txt.
39 -- LOG
40 -- Use cvs log to obtain detailed change logs.
43 #include <stdio.h>
44 #include <stdlib.h>
46 #if defined(_MSC_VER) && _MSC_VER >= 600
47 /* Ignore the MSC 6.0 library's "const"-riddled prototype
48 for spawnvpe.
50 # define spawnvpe _ignore_msc_spawnvpe
51 # include <process.h>
52 # undef spawnvpe
53 int spawnvpe(int, char *, char **, char **);
54 #else
55 # include <process.h>
56 #endif
58 #include <dos.h>
59 #include <errno.h>
60 #include <string.h>
61 #include <alloc.h>
62 #include <fcntl.h>
63 #include "extern.h"
64 #include "dosdta.h"
65 #include "exec.h"
66 #include "sysintf.h"
68 extern int Interrupted;
70 /* variables and functions local to this file */
71 static char *_findexec ANSI((char *, int *));
72 static char **_getpath ANSI(());
73 static char far *_dos_alloc ANSI((uint16));
75 static uint16 _swap_mask;
76 static int _mks_args;
77 static char dot_com[] = ".COM",
78 dot_exe[] = ".EXE",
79 dot_bat[] = ".BAT",
80 dot_ksh[] = ".KSH";
82 /* Kinds of executables */
83 #define SCR 1
84 #define COM 2
85 #define EXE 4
86 #define ALL (SCR|COM|EXE)
88 /* How to make a long pointer */
89 #define CF(x) (char far *)x
91 /* Make sure we know how to get a segment out of a long pointer */
92 #ifndef FP_SEG
93 #define FP_SEG(fp) ((unsigned)((unsigned long)(fp) >> 16))
94 #endif
96 iz81252 changed the parameters for Pack_argv() but this file did not get fixed!
97 PUBLIC int
98 spawnvpe(mode, program, av, ep)/*
99 =================================
100 Spawn a process using an environment and a vector of arguments.
101 The code computes a new environment, puts the MKS arguments into
102 it if need be, and calls the appropriate routines to search the
103 path and to invoke the process. */
104 int mode;
105 char *program;
106 char **av;
107 char **ep;
109 char pwd[PATH_MAX+1];
110 char **envp = ep; /* Cause we are going to mess with it. */
111 char **argv = av; /* Same with this one. */
112 char cmdtail[129];
113 char far *environment;
114 char *tail;
115 char *swptmp;
116 unsigned int envsize;
117 unsigned int cmdsize;
118 int cmdtailen;
119 int i;
120 int doswap;
122 /* First check to see if we can find the program to execute this way we
123 * don't alloc the environment and other such stuff prior to figuring out
124 * we don't know how to run the program. */
125 find_program:
126 if((program = _findexec(program, &i)) == NIL(char)) {
127 errno = ENOENT;
128 return( -1 );
131 /* i is set to TRUE in _findexec if the exec is a shell
132 * script (either .BAT or .KSH file), returns FALSE for all others. */
133 if( i && !Packed_shell ) {
134 /* Restore the spaces into the command line that were erased by
135 * the previous call to Pack_argv. This enables us to repack the
136 * command as a shell command using Pack_argv again. */
137 for( i=0; argv[i] != NIL(char); i++ ) {
138 int x = strlen(argv[i]);
139 if( argv[i+1] != NIL(char) ) argv[i][x] = ' ';
142 argv = Pack_argv( FALSE, TRUE, *argv );
143 Packed_shell = TRUE; /* Previous call implies shell = TRUE. */
145 /* Go and find the program again, I hate goto's but it seems silly to
146 * use tail recursion here just for aesthetic purity. */
147 program = *argv;
148 goto find_program;
151 /* Compute size of *argv vector for passing as MKS style arguments */
152 cmdsize = strlen(*argv)+2;
154 /* So we have decided on a program to run, therefore pack the command tail
155 * and build the environment to pass to the exec code. This loop packs the
156 * DOS command tail, and computes the size of all arguments for the MKS
157 * argument passing convention. Note that we reserve one less byte in the
158 * command tail if we are not using MKS style argument passing.
160 * Make sure the command tail contains at leat a space. Some commands fail
161 * to work if the command tail is only a \r, STUPID DOS! */
162 cmdtailen = ((_mks_args = ((Glob_attr & A_MKSARGS) != 0)) != 0)?3:2;
163 tail = cmdtail+1;
165 if( argv[1] != NIL(char) )
166 for( i=1; argv[i] != NIL(char); i++ ) {
167 int arglen = strlen(argv[i]);
169 cmdsize += arglen+2; /* Compute all args size for MKS */
171 if( (cmdtailen += arglen+1) <= 128 ) {
172 register char *p = argv[i];
173 tail[-1] = ' '; /* put in the space */
174 while( *tail++ = *p++ ); /* put in the arg */
176 else if( !_mks_args ) {
177 errno = E2BIG; /* unless its MKS exit if arglist */
178 return(-1); /* is too long. */
181 else
182 *tail++ = ' ';
184 /* Finish the command tail set up, placing the length in the first byte,
185 * and the \r \n \0 at the end for DOS, MKS and us respectively. */
186 *cmdtail = tail-cmdtail-2;
187 tail[-1] = '\r';
188 if( _mks_args ) *tail++ = '\n';
189 *tail = '\0';
191 /* Compute size of environment, skipping any MKS arguments passed in our
192 * environment */
193 for(; *envp && **envp == '~'; envp++ );
194 for(i=0, envsize=_mks_args?cmdsize:1; envp[i] != NIL(char); i++ )
195 envsize += strlen(envp[i]) + 1;
197 /* Check the DOS version number here. If it is < 3.0 then we don't
198 * even want to think about executing the swapping code. Permanently
199 * set swap to 0. */
200 doswap = (_osmajor < 3) ? 0 : Swap_on_exec;
202 /* Set up temporary file for swapping */
203 swptmp = doswap?tempnam(NIL(char),"mk"):"";
205 /* Allocate an appropriate sized environment block and align it on a
206 * paragraph boundary. It will later get copied to an appropriately low
207 * place in the executable image so that when we swap out the environment
208 * is still present. Use
209 * _dos_alloc
210 * to allocate the environment segment. The segment is freed by the call
211 * to exec. */
212 environment = _dos_alloc( envsize = ((envsize+16)>>4) );
213 *environment = '\0';
215 /* First copy the arguments preceeded by ~ character if we are using
216 * MKS style argument passing */
217 if( _mks_args )
218 for(; *argv; argv++) {
219 register char *p = *argv;
221 *environment++ = '~';
222 while( *environment++ = *p++ ); /* Far dest, poss near ptr */
225 /* Now stick in the current evironment vectors. */
226 for(; *envp; envp++) {
227 register char *p = *envp;
228 while( *environment++ = *p++ ); /* Far dest, poss near ptr */
231 /* Clear the interrupted flag, and exec */
232 Interrupted = 0;
234 /* Preserve the current working directory accross a spawn call
235 * DOS is brain dead about this. This way we have some hope of cleaning
236 * up the swapping tempfiles after we return. */
237 strcpy(pwd,Get_current_dir());
238 i = exec(doswap,CF(program),CF(cmdtail),FP_SEG(environment),CF(swptmp));
239 Set_dir(pwd);
241 /* Now free the temporary file name */
242 if( doswap ) FREE(swptmp);
244 /* If swap was interrupted then quit properly from dmake. */
245 if( Interrupted ) Quit();
247 return(i);
251 PUBLIC void
252 Hook_std_writes( file )
253 char *file;
255 if( file!= NIL(char) ) {
256 int mode = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
257 int handle;
259 if (*file == '+') {
260 ++file; /* -F +file means append to file */
261 mode = O_BINARY | O_WRONLY | O_CREAT | O_APPEND;
263 handle = open(file, mode, S_IREAD | S_IWRITE);
264 if (handle < 0) {
265 Fatal( "Could not open -F file");
267 (void) lseek(handle, 0L, SEEK_END);
268 do_hook_std_writes(handle);
270 else
271 do_unhook_std_writes();
276 ** _findexec finds executables on the path.
277 ** Note that it is pretty simple to add support for other executable types
278 ** shell scripts, etc.
280 ** This follows the command.com behavior very closely.
282 static char *
283 _findexec( s, is_shell )/*
284 ==========================
285 Cloned closely from code provided by Kent Williams. Stripped his down to
286 a reduced search since dmake doesn't need to recompute the PATH vector
287 each time it does the search since it cannot alter the path vector once
288 it begins to make recipes. Also modified it to use findfirst and findnext
289 as provided for dirlib package that I got off the net. */
290 char *s;
291 int *is_shell;
293 unsigned found_flags;
294 char **pathv = NIL(char *);
295 char *ext = NIL(char);
296 char *buf = NIL(char);
297 char *p[2];
298 char *dot_scr;
299 char *dot;
301 p[0] = ""; p[1] = NIL(char);
302 if( strchr("./\\", *s) || s[1] == ':' )
303 pathv = p;
304 else if( (pathv = _getpath()) == NIL(char *) )
305 return( NIL(char) );
307 /* Compute the extension we need if any. */
308 if( (dot = strrchr(s,'.')) != NIL(char) &&
309 dot > strrchr(s,'/') && dot > strrchr(s,'\\') )
310 ext = dot+1;
312 dot_scr = _mks_args ? dot_ksh : dot_bat;
313 *is_shell = FALSE;
315 for( found_flags = 0; *pathv && !found_flags; pathv++ ) {
316 DTA dta;
318 if( !ext ) {
319 char *name;
320 buf = Build_path( *pathv, name=DmStrJoin(s, ".???", -1, FALSE) );
321 FREE(name);
323 else
324 buf = Build_path( *pathv, s );
326 if( findfirst((char *)strupr(buf), &dta) != NIL(DTA) ) {
327 if( !ext ) {
328 char *dot;
330 /* search order is .com .exe (.ksh || .bat)
331 * there has to be a '.' */
332 do {
333 dot = strrchr(dta.name,'.');
334 if(0 == strcmp(dot,dot_com))
335 found_flags |= COM;
336 else if(0 == strcmp(dot,dot_exe))
337 found_flags |= EXE;
338 else if( 0 == strcmp(dot,dot_scr) )
339 found_flags |= SCR;
340 } while( found_flags != ALL && findnext(&dta) != NIL(DTA) );
342 if(found_flags & COM) ext = dot_com;
343 else if(found_flags & EXE) ext = dot_exe;
344 else if(found_flags & SCR) {
345 ext = dot_scr;
346 *is_shell = TRUE;
349 if( found_flags ) {
350 char *name;
351 buf = Build_path( *pathv, name=DmStrJoin(s,ext,-1,FALSE) );
352 FREE(name);
353 strupr(buf);
356 else
357 found_flags++;
361 return( found_flags ? buf : NIL(char) );
366 ** getpath turns the DOS path into a char *vector, It is gotten and
367 ** transformed only once since dmake can't modify the value of PATH while
368 ** it is making targets.
370 static char **
371 _getpath()
373 static char **dir = NIL(char *);
374 register char *p;
376 if( !dir ) {
377 register char *t;
378 int i;
379 char *semi = NIL(char);
381 if( (p = getenv("PATH")) == NIL(char) ) p = "";
382 for( i=1, t=p; *t; t++ ) if( *t == ';' ) i++;
384 TALLOC(dir, i+1, char *);
385 p = DmStrDup(p);
387 for( i=0; p; p = semi ? (semi+1):NIL(char),i++ ){
388 if( (semi = strchr(p,';')) != NIL(char) ) *semi = '\0';
389 dir[i] = p;
391 dir[i]=NIL(char);
394 return( dir );
398 static char far *
399 _dos_alloc( size )/*
400 ====================
401 This routine allocates size paragraphs from DOS. It changes the memory
402 allocation strategy to allocate from the tail and then changes it back.
403 to using first fit. */
404 uint16 size;
406 union REGS r;
408 r.h.ah = 0x48;
409 r.x.bx = size;
411 intdos( &r, &r );
412 if( r.x.cflag ) No_ram();
414 return( (char far *) MK_FP(r.x.ax, 0) );