/* * Copyright 1993, 1995 Christopher Seiwald. * * This file is part of Jam - see jam.c for Copyright information. */ # include "jam.h" # include "execcmd.h" # include "lists.h" # include <errno.h> # if defined( unix ) || defined( NT ) || defined( __OS2__ ) || defined(AMIGA) # if defined( _AIX) || \ (defined (COHERENT) && defined (_I386)) || \ defined(__sgi) || \ defined(__Lynx__) || \ defined(M_XENIX) || \ defined(__QNX__) || \ defined(__BEOS__) || \ defined(__ISC) # define vfork() fork() # endif # if defined( NT ) || defined( __OS2__ ) # include <process.h> # if !defined( __BORLANDC__ ) && !defined( __OS2__ ) # define wait my_wait static int my_wait(int *status); # endif # endif /* * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS * * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). * The default is: * * /bin/sh -c % [ on UNIX/AmigaOS ] * cmd.exe /c % [ on OS2/WinNT ] * * Each word must be an individual element in a jam variable value. * * In $(JAMSHELL), % expands to the command string and ! expands to * the slot number (starting at 1) for multiprocess (-j) invocations. * If $(JAMSHELL) doesn't include a %, it is tacked on as the last * argument. * * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work! * * External routines: * execcmd() - launch an async command execution * execwait() - wait and drive at most one execution completion * * Internal routines: * onintr() - bump intr to note command interruption * * 04/08/94 (seiwald) - Coherent/386 support added. * 05/04/94 (seiwald) - async multiprocess interface * 01/22/95 (seiwald) - $(JAMSHELL) support * 06/02/97 (gsar) - full async multiprocess support for Win32 */ static int intr = 0; static int cmdsrunning = 0; # ifdef NT static void (*istat)( int ); void onintr( int ); # else static void (*istat)(); #endif static struct { int pid; /* on win32, a real process handle */ void (*func)(); void *closure; # if defined( NT ) || defined( __OS2__ ) char *tempfile; # endif } cmdtab[ MAXJOBS ] = {{0}}; /* * onintr() - bump intr to note command interruption */ void onintr( disp ) int disp; { intr++; printf( "...interrupted\n" ); } /* * execcmd() - launch an async command execution */ void execcmd( string, func, closure, shell ) char *string; void (*func)(); void *closure; LIST *shell; { int pid; int slot; char *argv[ MAXARGC + 1 ]; /* +1 for NULL */ # if defined( NT ) || defined( __OS2__ ) static char *comspec; char *p; /* XXX this is questionable practice, since COMSPEC has * a high degree of variability, resulting in Jamfiles * that frequently won't work. COMSPEC also denotes a shell * fit for interative use, not necessarily/merely a shell * capable of launching commands. Besides, people can * just set JAMSHELL instead. */ if( !comspec && !( comspec = getenv( "COMSPEC" ) ) ) comspec = "cmd.exe"; # endif /* Find a slot in the running commands table for this one. */ for( slot = 0; slot < MAXJOBS; slot++ ) if( !cmdtab[ slot ].pid ) break; if( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } # if defined( NT ) || defined( __OS2__ ) if( !cmdtab[ slot ].tempfile ) { char *tempdir; if( !( tempdir = getenv( "TEMP" ) ) && !( tempdir = getenv( "TMP" ) ) ) tempdir = "\\temp"; cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 ); sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat", tempdir, slot ); } /* Trim leading, ending white space */ while( isspace( *string ) ) ++string; p = strchr( string, '\n' ); while( p && isspace( *p ) ) ++p; /* If multi line, or too long, or JAMSHELL is set, write to bat file. */ /* Otherwise, exec directly. */ /* Frankly, if it is a single long line I don't think the */ /* command interpreter will do any better -- it will fail. */ if( p && *p || strlen( string ) > MAXLINE || shell ) { FILE *f; /* Write command to bat file. */ f = fopen( cmdtab[ slot ].tempfile, "w" ); fputs( string, f ); fclose( f ); string = cmdtab[ slot ].tempfile; } # endif /* Forumulate argv */ /* If shell was defined, be prepared for % and ! subs. */ /* Otherwise, use stock /bin/sh (on unix) or comspec (on NT). */ if( shell ) { int i; char jobno[4]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) ) { switch( shell->string[0] ) { case '%': argv[i] = string; gotpercent++; break; case '!': argv[i] = jobno; break; default: argv[i] = shell->string; } if( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[i] ); } if( !gotpercent ) argv[i++] = string; argv[i] = 0; } else { # if defined( NT ) || defined( __OS2__ ) argv[0] = comspec; argv[1] = "/Q/C"; /* anything more is non-portable */ # else argv[0] = "/bin/sh"; argv[1] = "-c"; # endif argv[2] = string; argv[3] = 0; } /* Catch interrupts whenever commands are running. */ if( !cmdsrunning++ ) istat = signal( SIGINT, onintr ); /* Start the command */ # if defined( NT ) || defined( __OS2__ ) if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) < 0 ) { perror( "spawn" ); exit( EXITBAD ); } # else if ((pid = vfork()) == 0) { execvp( argv[0], argv ); _exit(127); } if( pid == -1 ) { perror( "vfork" ); exit( EXITBAD ); } # endif /* Save the operation for execwait() to find. */ cmdtab[ slot ].pid = pid; cmdtab[ slot ].func = func; cmdtab[ slot ].closure = closure; /* Wait until we're under the limit of concurrent commands. */ /* Don't trust globs.jobs alone. */ while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs ) if( !execwait() ) break; } /* * execwait() - wait and drive at most one execution completion */ int execwait() { int i; int status, w; int rstat; /* Handle naive make1() which doesn't know if cmds are running. */ if( !cmdsrunning ) return 0; /* Pick up process pid and status */ while( ( w = wait( &status ) ) == -1 && errno == EINTR ) ; if( w == -1 ) { printf( "child process(es) lost!\n" ); perror("wait"); exit( EXITBAD ); } /* Find the process in the cmdtab. */ for( i = 0; i < MAXJOBS; i++ ) if( w == cmdtab[ i ].pid ) break; if( i == MAXJOBS ) { printf( "waif child found!\n" ); exit( EXITBAD ); } /* Drive the completion */ if( !--cmdsrunning ) signal( SIGINT, istat ); if( intr ) rstat = EXEC_CMD_INTR; else if( w == -1 || status != 0 ) rstat = EXEC_CMD_FAIL; else rstat = EXEC_CMD_OK; cmdtab[ i ].pid = 0; (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat ); return 1; } # if defined( NT ) && !defined( __BORLANDC__ ) # define WIN32_LEAN_AND_MEAN # include <windows.h> /* do the ugly deed */ static int my_wait( status ) int *status; { int i, num_active = 0; DWORD exitcode, waitcode; static HANDLE *active_handles = 0; if (!active_handles) active_handles = (HANDLE *)malloc(globs.jobs * sizeof(HANDLE) ); /* first see if any non-waited-for processes are dead, * and return if so. */ for ( i = 0; i < globs.jobs; i++ ) { if ( cmdtab[i].pid ) { if ( GetExitCodeProcess((HANDLE)cmdtab[i].pid, &exitcode) ) { if ( exitcode == STILL_ACTIVE ) active_handles[num_active++] = (HANDLE)cmdtab[i].pid; else { CloseHandle((HANDLE)cmdtab[i].pid); *status = (int)((exitcode & 0xff) << 8); return cmdtab[i].pid; } } else goto FAILED; } } /* if a child exists, wait for it to die */ if ( !num_active ) { errno = ECHILD; return -1; } waitcode = WaitForMultipleObjects( num_active, active_handles, FALSE, INFINITE ); if ( waitcode != WAIT_FAILED ) { if ( waitcode >= WAIT_ABANDONED_0 && waitcode < WAIT_ABANDONED_0 + num_active ) i = waitcode - WAIT_ABANDONED_0; else i = waitcode - WAIT_OBJECT_0; if ( GetExitCodeProcess(active_handles[i], &exitcode) ) { CloseHandle(active_handles[i]); *status = (int)((exitcode & 0xff) << 8); return (int)active_handles[i]; } } FAILED: errno = GetLastError(); return -1; } # endif /* NT && !__BORLANDC__ */ # endif /* unix || NT || __OS2__ || AMIGA */
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#20 | 23298 | Nick Poole |
Updating Jam source The changes here include a simplified Windows build process: the makefile now has 2 MSVC sections, one 32-bit the other 64-bit. |
||
#19 | 9902 | Perforce staff |
Build jam without so many warnings on macosxx86_64. p4transfer.py: Transferred from production |
||
#18 | 9901 | Perforce staff |
Compute max line length on windows by OS type using GetVersionEx() call in execmax() function. Now instead of just 996 for the default, Win NT 4.0 and Win 2K get 2047, and Win XP and 2003+ get 8191. The wheels of progress grind slowly. Bumped JAMVERSION to 2.5.1. New feature documented in RELNOTES. p4transfer.py: Transferred from production |
||
#17 | 9900 | Perforce staff |
Maximum command line length now determined by execmax() function, which still just returns the compiled-in MAXLINE. p4transfer.py: Transferred from production |
||
#16 | 9899 | tony |
Port Jam to Zeta. Zeta is a fork of BeOS so this port is broadly the same as the BeOS port. p4transfer.py: Transferred from production |
||
#15 | 9898 | Perforce maintenance |
HPUX 11 jam porting: a cast for exec() and include unistd.h. Porting change. p4transfer.py: Transferred from production |
||
#14 | 3069 | Perforce staff |
Remove temp .bat files created on NT. Bug fix documented in RELNOTES. |
||
#13 | 2845 | rmg |
Make jam 2.5rc2 build with BorlandC 5.5 Porting Change, note in RELNOTES. === computer:1666: Change 40450 by anton@anton-diogenes on 2003/01/21 10:26:05 |
||
#12 | 2562 | rmg |
Grist jam temp file names with pid on NT so as to ensure uniqueness. Bug fix documented in RELNOTES. === computer:1666: Change 39577 by seiwald@tricks-seiwald on 2002/12/27 15:55:21 |
||
#11 | 2493 | rmg |
Rewrite the past: update all jam's source with comments to reflect changes since about 2.3, very early 2001. Whitespace only change. === computer:1666: Change 37660 by seiwald@play-seiwald on 2002/11/06 22:41:35 Note: I regenerated jamgram.c on my linux 7.3 system prior to the submit, since patch was so unhappy trying to lay down the changes from Christopher's change. Presumably this is just due to different yacc/bison/whatever particulars on the system where Christopher made the changes originally. - rmg |
||
#10 | 2491 | rmg |
Some consting in jam to make it more compilable by C++ compilers. No functional change. === computer:1666: Change 37433 by perforce@perforce on 2002/10/30 16:08:51 Recreational const-ing of jam, for compilers that don't allow "string" to be passed as a non-const char *. This included a few places where we were modifying what could possibly have been read-only storage, oddly enough. No functional change. === computer:1666: Change 37602 by seiwald@play-seiwald on 2002/11/04 17:25:40 |
||
#9 | 1351 | rmg |
This change is integration history only. (Accept -ay'ed integrating: Change 216 by peter_glasscock@peter_glasscock on 1999/07/27 03:25:01 Integrate recent changes to public source So, these were apparently Matt's tweaks to changes being integrated from //public/jam, and we'll ignore them here. |
||
#8 | 1350 | rmg |
No change - this is just to record integration history. (Resolve -ay'ed from Change 292 by Matt Armstrong |
||
#7 | 486 | Perforce staff |
Jam 2.3. See RELNOTES for a list of changes from 2.2.x. Just about every source file was touched when jam got ANSI-fied. |
||
#6 | 212 | Perforce staff |
An interpretative integration of Peter Glasscock's -o file support. This is handled in the make1() routine, rather than in all the exec*.c files. -o x writes the actions to file x rather than actually running them. Implies -n (but not -d2). |
||
#5 | 67 | Laura Wingerd |
Integrate Perforce's jam changes & project page update (change 59, change 60, change 61, change 62, change 63, change 64, change 66) |
||
#4 | 5 | Perforce maintenance | Jam/MR 2.2.4 (HDRPATTERN, JAMUNAME, JAMSHELL, plus misc tweaks) | ||
#3 | 4 | Perforce maintenance | Jam/MR 2.2.2 (AmigaOS support) | ||
#2 | 3 | Perforce maintenance | Jam/MR 2.2.1 (fix for NT handle leak) | ||
#1 | 2 | laura | Add Jam/MR 2.2 source |