mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-27 05:44:15 +08:00
pex-common.c: New file.
libiberty: * pex-common.c: New file. * pex-one.c: New file. * pexecute.c: New file. * pex-common.h: Include <stdio.h>. (struct pex_obj): Define. (struct pex_funcs): Define. (pex_init_common): Declare. * pex-unix.c: Rewrite. * pex-win32.c: Rewrite. * pex-djgpp.c: Rewrite. * pex-msdos.c: Rewrite. * testsuite/text-pexecute.c: New file. * pexecute.txh: Rewrite. * configure.ac: Check for wait3 and wait4. Set CHECK to really-check rather than check-cplus-dem. * functions.texi: Rebuild. * Makefile.in: Rebuild dependencies. (CFILES): Add pexecute.c, pex-common.c, pex-one.c. (REQUIRED_OFILES): Add pexecute.o, pex-common.o, pex-one.o. * testsuite/Makefile.in (really-check): New target. (check-pexecute, test-pexecute): New targets. * configure: Rebuild. include: * libiberty.h: Include <stdio.h>. (PEX_RECORD_TIMES, PEX_USE_PIPES, PEX_SAVE_TEMPS): Define. (PEX_LAST, PEX_SEARCH, PEX_SUFFIX, PEX_STDERR_TO_STDOUT): Define. (PEX_BINARY_INPUT, PEX_BINARY_OUTPUT): Define. (pex_init, pex_run, pex_read_output): Declare. (pex_get_status, pex_get_times, pex_free, pex_one): Declare. (struct pex_time): Define. From-SVN: r97148
This commit is contained in:
parent
f78c1452ea
commit
a584cf65fa
@ -1,3 +1,13 @@
|
||||
2005-03-28 Ian Lance Taylor <ian@airs.com>
|
||||
|
||||
* libiberty.h: Include <stdio.h>.
|
||||
(PEX_RECORD_TIMES, PEX_USE_PIPES, PEX_SAVE_TEMPS): Define.
|
||||
(PEX_LAST, PEX_SEARCH, PEX_SUFFIX, PEX_STDERR_TO_STDOUT): Define.
|
||||
(PEX_BINARY_INPUT, PEX_BINARY_OUTPUT): Define.
|
||||
(pex_init, pex_run, pex_read_output): Declare.
|
||||
(pex_get_status, pex_get_times, pex_free, pex_one): Declare.
|
||||
(struct pex_time): Define.
|
||||
|
||||
2005-03-28 Mark Mitchell <mark@codesourcery.com>
|
||||
|
||||
* libiberty.h (ffs): Declare, if necessary.
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* Function declarations for libiberty.
|
||||
|
||||
Copyright 2001, 2002 Free Software Foundation, Inc.
|
||||
Copyright 2001, 2002, 2005 Free Software Foundation, Inc.
|
||||
|
||||
Note - certain prototypes declared in this header file are for
|
||||
functions whoes implementation copyright does not belong to the
|
||||
@ -46,6 +46,8 @@ extern "C" {
|
||||
/* Get a definition for va_list. */
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Build an argument vector from a string. Allocates memory using
|
||||
malloc. Use freeargv to free the vector. */
|
||||
|
||||
@ -314,6 +316,166 @@ extern void hex_init (void);
|
||||
the argument being performed exactly once. */
|
||||
#define hex_value(c) ((unsigned int) _hex_value[(unsigned char) (c)])
|
||||
|
||||
/* Flags for pex_init. These are bits to be or'ed together. */
|
||||
|
||||
/* Record subprocess times, if possible. */
|
||||
#define PEX_RECORD_TIMES 0x1
|
||||
|
||||
/* Use pipes for communication between processes, if possible. */
|
||||
#define PEX_USE_PIPES 0x2
|
||||
|
||||
/* Save files used for communication between processes. */
|
||||
#define PEX_SAVE_TEMPS 0x4
|
||||
|
||||
/* Prepare to execute one or more programs, with standard output of
|
||||
each program fed to standard input of the next.
|
||||
FLAGS As above.
|
||||
PNAME The name of the program to report in error messages.
|
||||
TEMPBASE A base name to use for temporary files; may be NULL to
|
||||
use a random name.
|
||||
Returns NULL on error. */
|
||||
|
||||
extern struct pex_obj *pex_init (int flags, const char *pname,
|
||||
const char *tempbase);
|
||||
|
||||
/* Flags for pex_run. These are bits to be or'ed together. */
|
||||
|
||||
/* Last program in pipeline. Standard output of program goes to
|
||||
OUTNAME, or, if OUTNAME is NULL, to standard output of caller. Do
|
||||
not set this if you want to call pex_read_output. After this is
|
||||
set, pex_run may no longer be called with the same struct
|
||||
pex_obj. */
|
||||
#define PEX_LAST 0x1
|
||||
|
||||
/* Search for program in executable search path. */
|
||||
#define PEX_SEARCH 0x2
|
||||
|
||||
/* OUTNAME is a suffix. */
|
||||
#define PEX_SUFFIX 0x4
|
||||
|
||||
/* Send program's standard error to standard output. */
|
||||
#define PEX_STDERR_TO_STDOUT 0x8
|
||||
|
||||
/* Input file should be opened in binary mode. This flag is ignored
|
||||
on Unix. */
|
||||
#define PEX_BINARY_INPUT 0x10
|
||||
|
||||
/* Output file should be opened in binary mode. This flag is ignored
|
||||
on Unix. For proper behaviour PEX_BINARY_INPUT and
|
||||
PEX_BINARY_OUTPUT have to match appropriately--i.e., a call using
|
||||
PEX_BINARY_OUTPUT should be followed by a call using
|
||||
PEX_BINARY_INPUT. */
|
||||
#define PEX_BINARY_OUTPUT 0x20
|
||||
|
||||
/* Execute one program. Returns NULL on success. On error returns an
|
||||
error string (typically just the name of a system call); the error
|
||||
string is statically allocated.
|
||||
|
||||
OBJ Returned by pex_init.
|
||||
|
||||
FLAGS As above.
|
||||
|
||||
EXECUTABLE The program to execute.
|
||||
|
||||
ARGV NULL terminated array of arguments to pass to the program.
|
||||
|
||||
OUTNAME Sets the output file name as follows:
|
||||
|
||||
PEX_SUFFIX set (OUTNAME may not be NULL):
|
||||
TEMPBASE parameter to pex_init not NULL:
|
||||
Output file name is the concatenation of TEMPBASE
|
||||
and OUTNAME.
|
||||
TEMPBASE is NULL:
|
||||
Output file name is a random file name ending in
|
||||
OUTNAME.
|
||||
PEX_SUFFIX not set:
|
||||
OUTNAME not NULL:
|
||||
Output file name is OUTNAME.
|
||||
OUTNAME NULL, TEMPBASE not NULL:
|
||||
Output file name is randomly chosen using
|
||||
TEMPBASE.
|
||||
OUTNAME NULL, TEMPBASE NULL:
|
||||
Output file name is randomly chosen.
|
||||
|
||||
If PEX_LAST is not set, the output file name is the
|
||||
name to use for a temporary file holding stdout, if
|
||||
any (there will not be a file if PEX_USE_PIPES is set
|
||||
and the system supports pipes). If a file is used, it
|
||||
will be removed when no longer needed unless
|
||||
PEX_SAVE_TEMPS is set.
|
||||
|
||||
If PEX_LAST is set, and OUTNAME is not NULL, standard
|
||||
output is written to the output file name. The file
|
||||
will not be removed. If PEX_LAST and PEX_SUFFIX are
|
||||
both set, TEMPBASE may not be NULL.
|
||||
|
||||
ERRNAME If not NULL, this is the name of a file to which
|
||||
standard error is written. If NULL, standard error of
|
||||
the program is standard error of the caller.
|
||||
|
||||
ERR On an error return, *ERR is set to an errno value, or
|
||||
to 0 if there is no relevant errno.
|
||||
*/
|
||||
|
||||
extern const char *pex_run (struct pex_obj *obj, int flags,
|
||||
const char *executable, char * const *argv,
|
||||
const char *outname, const char *errname,
|
||||
int *err);
|
||||
|
||||
/* Read the standard output of the last program to be executed.
|
||||
pex_run can not be called after this. BINARY should be non-zero if
|
||||
the file should be opened in binary mode; this is ignored on Unix.
|
||||
Returns NULL on error. Don't call fclose on the returned FILE; it
|
||||
will be closed by pex_free. */
|
||||
|
||||
extern FILE *pex_read_output (struct pex_obj *, int binary);
|
||||
|
||||
/* Return exit status of all programs in VECTOR. COUNT indicates the
|
||||
size of VECTOR. The status codes in the vector are in the order of
|
||||
the calls to pex_run. Returns 0 on error, 1 on success. */
|
||||
|
||||
extern int pex_get_status (struct pex_obj *, int count, int *vector);
|
||||
|
||||
/* Return times of all programs in VECTOR. COUNT indicates the size
|
||||
of VECTOR. struct pex_time is really just struct timeval, but that
|
||||
is not portable to all systems. Returns 0 on error, 1 on
|
||||
success. */
|
||||
|
||||
struct pex_time
|
||||
{
|
||||
unsigned long user_seconds;
|
||||
unsigned long user_microseconds;
|
||||
unsigned long system_seconds;
|
||||
unsigned long system_microseconds;
|
||||
};
|
||||
|
||||
extern int pex_get_times (struct pex_obj *, int count,
|
||||
struct pex_time *vector);
|
||||
|
||||
/* Clean up a pex_obj. */
|
||||
|
||||
extern void pex_free (struct pex_obj *);
|
||||
|
||||
/* Just execute one program. Return value is as for pex_run.
|
||||
FLAGS Combination of PEX_SEARCH and PEX_STDERR_TO_STDOUT.
|
||||
EXECUTABLE As for pex_run.
|
||||
ARGV As for pex_run.
|
||||
PNAME As for pex_init.
|
||||
OUTNAME As for pex_run when PEX_LAST is set.
|
||||
ERRNAME As for pex_run.
|
||||
STATUS Set to exit status on success.
|
||||
ERR As for pex_run.
|
||||
*/
|
||||
|
||||
extern const char *pex_one (int flags, const char *executable,
|
||||
char * const *argv, const char *pname,
|
||||
const char *outname, const char *errname,
|
||||
int *status, int *err);
|
||||
|
||||
/* pexecute and pwait are the old pexecute interface, still here for
|
||||
backward compatibility. Don't use these for new code. Instead,
|
||||
use pex_init/pex_run/pex_get_status/pex_free, or pex_one. */
|
||||
|
||||
/* Definitions used by the pexecute routine. */
|
||||
|
||||
#define PEXECUTE_FIRST 1
|
||||
|
@ -1,3 +1,28 @@
|
||||
2005-03-28 Ian Lance Taylor <ian@airs.com>
|
||||
|
||||
* pex-common.c: New file.
|
||||
* pex-one.c: New file.
|
||||
* pexecute.c: New file.
|
||||
* pex-common.h: Include <stdio.h>.
|
||||
(struct pex_obj): Define.
|
||||
(struct pex_funcs): Define.
|
||||
(pex_init_common): Declare.
|
||||
* pex-unix.c: Rewrite.
|
||||
* pex-win32.c: Rewrite.
|
||||
* pex-djgpp.c: Rewrite.
|
||||
* pex-msdos.c: Rewrite.
|
||||
* testsuite/text-pexecute.c: New file.
|
||||
* pexecute.txh: Rewrite.
|
||||
* configure.ac: Check for wait3 and wait4. Set CHECK to
|
||||
really-check rather than check-cplus-dem.
|
||||
* functions.texi: Rebuild.
|
||||
* Makefile.in: Rebuild dependencies.
|
||||
(CFILES): Add pexecute.c, pex-common.c, pex-one.c.
|
||||
(REQUIRED_OFILES): Add pexecute.o, pex-common.o, pex-one.o.
|
||||
* testsuite/Makefile.in (really-check): New target.
|
||||
(check-pexecute, test-pexecute): New targets.
|
||||
* configure: Rebuild.
|
||||
|
||||
2005-03-28 Mark Kettenis <kettenis@gnu.org>
|
||||
|
||||
* unlink-if-ordinary.c: Include <sys/types.h>.
|
||||
|
@ -142,8 +142,8 @@ CFILES = alloca.c argv.c asprintf.c atexit.c \
|
||||
make-temp-file.c md5.c memchr.c memcmp.c memcpy.c memmove.c \
|
||||
mempcpy.c memset.c mkstemps.c \
|
||||
objalloc.c obstack.c \
|
||||
partition.c \
|
||||
pex-djgpp.c pex-msdos.c \
|
||||
partition.c pexecute.c \
|
||||
pex-common.c pex-djgpp.c pex-msdos.c pex-one.c \
|
||||
pex-unix.c pex-win32.c \
|
||||
physmem.c putenv.c \
|
||||
random.c regex.c rename.c rindex.c \
|
||||
@ -170,7 +170,8 @@ REQUIRED_OFILES = ./regex.o ./cplus-dem.o ./cp-demangle.o ./md5.o \
|
||||
./lbasename.o ./lrealpath.o \
|
||||
./make-relative-prefix.o ./make-temp-file.o \
|
||||
./objalloc.o ./obstack.o \
|
||||
./partition.o ./physmem.o @pexecute@ \
|
||||
./partition.o ./pexecute.o ./physmem.o \
|
||||
./pex-common.o ./pex-one.o @pexecute@ \
|
||||
./safe-ctype.o ./sort.o ./spaces.o ./splay-tree.o ./strerror.o \
|
||||
./strsignal.o \
|
||||
./ternary.o \
|
||||
@ -756,6 +757,13 @@ $(CONFIGURED_OFILES): stamp-picdir
|
||||
else true; fi
|
||||
$(COMPILE.c) $(srcdir)/partition.c $(OUTPUT_OPTION)
|
||||
|
||||
./pex-common.o: $(srcdir)/pex-common.c config.h $(INCDIR)/ansidecl.h \
|
||||
$(INCDIR)/libiberty.h $(srcdir)/pex-common.h
|
||||
if [ x"$(PICFLAG)" != x ]; then \
|
||||
$(COMPILE.c) $(PICFLAG) $(srcdir)/pex-common.c -o pic/$@; \
|
||||
else true; fi
|
||||
$(COMPILE.c) $(srcdir)/pex-common.c $(OUTPUT_OPTION)
|
||||
|
||||
./pex-djgpp.o: $(srcdir)/pex-djgpp.c config.h $(INCDIR)/ansidecl.h \
|
||||
$(INCDIR)/libiberty.h $(srcdir)/pex-common.h
|
||||
if [ x"$(PICFLAG)" != x ]; then \
|
||||
@ -771,6 +779,13 @@ $(CONFIGURED_OFILES): stamp-picdir
|
||||
else true; fi
|
||||
$(COMPILE.c) $(srcdir)/pex-msdos.c $(OUTPUT_OPTION)
|
||||
|
||||
./pex-one.o: $(srcdir)/pex-one.c config.h $(INCDIR)/ansidecl.h \
|
||||
$(INCDIR)/libiberty.h
|
||||
if [ x"$(PICFLAG)" != x ]; then \
|
||||
$(COMPILE.c) $(PICFLAG) $(srcdir)/pex-one.c -o pic/$@; \
|
||||
else true; fi
|
||||
$(COMPILE.c) $(srcdir)/pex-one.c $(OUTPUT_OPTION)
|
||||
|
||||
./pex-unix.o: $(srcdir)/pex-unix.c config.h $(INCDIR)/ansidecl.h \
|
||||
$(INCDIR)/libiberty.h $(srcdir)/pex-common.h
|
||||
if [ x"$(PICFLAG)" != x ]; then \
|
||||
@ -785,6 +800,13 @@ $(CONFIGURED_OFILES): stamp-picdir
|
||||
else true; fi
|
||||
$(COMPILE.c) $(srcdir)/pex-win32.c $(OUTPUT_OPTION)
|
||||
|
||||
./pexecute.o: $(srcdir)/pexecute.c config.h $(INCDIR)/ansidecl.h \
|
||||
$(INCDIR)/libiberty.h
|
||||
if [ x"$(PICFLAG)" != x ]; then \
|
||||
$(COMPILE.c) $(PICFLAG) $(srcdir)/pexecute.c -o pic/$@; \
|
||||
else true; fi
|
||||
$(COMPILE.c) $(srcdir)/pexecute.c $(OUTPUT_OPTION)
|
||||
|
||||
./physmem.o: $(srcdir)/physmem.c config.h $(INCDIR)/ansidecl.h \
|
||||
$(INCDIR)/libiberty.h
|
||||
if [ x"$(PICFLAG)" != x ]; then \
|
||||
|
6
libiberty/configure
vendored
6
libiberty/configure
vendored
@ -4818,7 +4818,7 @@ vars="sys_errlist sys_nerr sys_siglist"
|
||||
|
||||
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
|
||||
checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp"
|
||||
checkfuncs="$checkfuncs getsysinfo table sysctl"
|
||||
checkfuncs="$checkfuncs getsysinfo table sysctl wait3 wait4"
|
||||
|
||||
# These are neither executed nor required, but they help keep
|
||||
# autoheader happy without adding a bunch of text to acconfig.h.
|
||||
@ -4895,7 +4895,7 @@ for ac_func in asprintf atexit basename bcmp bcopy bsearch bzero calloc clock \
|
||||
strtod strtol strtoul tmpnam vasprintf vfprintf vprintf \
|
||||
vsprintf waitpid getrusage on_exit psignal strerror strsignal \
|
||||
sysconf times sbrk gettimeofday ffs snprintf vsnprintf \
|
||||
pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl \
|
||||
pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl wait3 wait4 \
|
||||
realpath canonicalize_file_name
|
||||
do
|
||||
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||
@ -5133,7 +5133,7 @@ fi;
|
||||
else
|
||||
|
||||
# Not a target library, so we set things up to run the test suite.
|
||||
CHECK=check-cplus-dem
|
||||
CHECK=really-check
|
||||
|
||||
fi
|
||||
|
||||
|
@ -268,7 +268,7 @@ vars="sys_errlist sys_nerr sys_siglist"
|
||||
|
||||
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
|
||||
checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp"
|
||||
checkfuncs="$checkfuncs getsysinfo table sysctl"
|
||||
checkfuncs="$checkfuncs getsysinfo table sysctl wait3 wait4"
|
||||
|
||||
# These are neither executed nor required, but they help keep
|
||||
# autoheader happy without adding a bunch of text to acconfig.h.
|
||||
@ -280,7 +280,7 @@ if test "x" = "y"; then
|
||||
strtod strtol strtoul tmpnam vasprintf vfprintf vprintf \
|
||||
vsprintf waitpid getrusage on_exit psignal strerror strsignal \
|
||||
sysconf times sbrk gettimeofday ffs snprintf vsnprintf \
|
||||
pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl \
|
||||
pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl wait3 wait4 \
|
||||
realpath canonicalize_file_name)
|
||||
AC_DEFINE(HAVE_SYS_ERRLIST, 1, [Define if you have the sys_errlist variable.])
|
||||
AC_DEFINE(HAVE_SYS_NERR, 1, [Define if you have the sys_nerr variable.])
|
||||
@ -357,7 +357,7 @@ if test -n "${with_target_subdir}"; then
|
||||
else
|
||||
|
||||
# Not a target library, so we set things up to run the test suite.
|
||||
CHECK=check-cplus-dem
|
||||
CHECK=really-check
|
||||
|
||||
fi
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
@c Edit the *.c files, configure with --enable-maintainer-mode,
|
||||
@c and let gather-docs build you a new copy.
|
||||
|
||||
@c safe-ctype.c:24
|
||||
@c safe-ctype.c:25
|
||||
@defvr Extension HOST_CHARSET
|
||||
This macro indicates the basic character set and encoding used by the
|
||||
host: more precisely, the encoding used for character constants in
|
||||
@ -25,6 +25,139 @@ nineteen EBCDIC varying characters is tested; exercise caution.)
|
||||
@end ftable
|
||||
@end defvr
|
||||
|
||||
@c pexecute.txh:1
|
||||
@deftypefn Extension struct pex_obj *pex_init (int @var{flags}, const char *@var{pname}, const char *@var{tempbase})
|
||||
|
||||
Prepare to execute one or more programs, with standard output of each
|
||||
program fed to standard input of the next. This is a system
|
||||
independent interface to execute a pipeline.
|
||||
|
||||
@var{flags} is a bitwise combination of the following:
|
||||
|
||||
@table @code
|
||||
|
||||
@vindex PEX_RECORD_TIMES
|
||||
@item PEX_RECORD_TIMES
|
||||
Record subprocess times if possible.
|
||||
|
||||
@vindex PEX_USE_PIPES
|
||||
@item PEX_USE_PIPES
|
||||
Use pipes for communication between processes, if possible.
|
||||
|
||||
@vindex PEX_SAVE_TEMPS
|
||||
@item PEX_SAVE_TEMPS
|
||||
Don't delete temporary files used for communication between
|
||||
processes.
|
||||
|
||||
@end table
|
||||
|
||||
@var{pname} is the name of program to be executed, used in error
|
||||
messages. @var{tempbase} is a base name to use for any required
|
||||
temporary files; it may be @code{NULL} to use a randomly chosen name.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c pexecute.txh:161
|
||||
@deftypefn Extension const char *pex_one (int @var{flags}, const char *@var{executable}, char * const *@var{argv}, const char *@var{pname}, const char *@var{outname}, const char *@var{errname}, int *@var{status}, int *@var{err})
|
||||
|
||||
An interface to @code{pex_init} to permit the easy execution of a
|
||||
single program. The return value and most of the parameters are as
|
||||
for a call to @code{pex_run}. @var{flags} is restricted to a
|
||||
combination of @code{PEX_SEARCH}, @code{PEX_STDERR_TO_STDOUT}, and
|
||||
@code{PEX_BINARY_OUTPUT}. @var{outname} is interpreted as if
|
||||
@code{PEX_LAST} were set. On a successful return, *@var{status} will
|
||||
be set to the exit status of the program.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c pexecute.txh:32
|
||||
@deftypefn Extension const char *pex_run (struct pex_obj *@var{obj}, int @var{flags}, const char *@var{executable}, char * const *@var{argv}, const char *@var{outname}, const char *@var{errname}, int *@var{err})
|
||||
|
||||
Execute one program in a pipeline. On success this returns
|
||||
@code{NULL}. On failure it returns an error message, a statically
|
||||
allocated string.
|
||||
|
||||
@var{obj} is returned by a previous call to @code{pex_init}.
|
||||
|
||||
@var{flags} is a bitwise combination of the following:
|
||||
|
||||
@table @code
|
||||
|
||||
@vindex PEX_LAST
|
||||
@item PEX_LAST
|
||||
This must be set on the last program in the pipeline. In particular,
|
||||
it should be set when executing a single program. The standard output
|
||||
of the program will be sent to @var{outname}, or, if @var{outname} is
|
||||
@code{NULL}, to the standard output of the calling program. This
|
||||
should not be set if you want to call @code{pex_read_output}
|
||||
(described below). After a call to @code{pex_run} with this bit set,
|
||||
@var{pex_run} may no longer be called with the same @var{obj}.
|
||||
|
||||
@vindex PEX_SEARCH
|
||||
@item PEX_SEARCH
|
||||
Search for the program using the user's executable search path.
|
||||
|
||||
@vindex PEX_SUFFIX
|
||||
@item PEX_SUFFIX
|
||||
@var{outname} is a suffix. See the description of @var{outname},
|
||||
below.
|
||||
|
||||
@vindex PEX_STDERR_TO_STDOUT
|
||||
@item PEX_STDERR_TO_STDOUT
|
||||
Send the program's standard error to standard output, if possible.
|
||||
|
||||
@vindex PEX_BINARY_INPUT
|
||||
@vindex PEX_BINARY_OUTPUT
|
||||
@item PEX_BINARY_INPUT
|
||||
@itemx PEX_BINARY_OUTPUT
|
||||
The standard input (output) of the program should be read (written) in
|
||||
binary mode rather than text mode. These flags are ignored on systems
|
||||
which do not distinguish binary mode and text mode, such as Unix. For
|
||||
proper behavior these flags should match appropriately--a call to
|
||||
@code{pex_run} using @code{PEX_BINARY_OUTPUT} should be followed by a
|
||||
call using @code{PEX_BINARY_INPUT}.
|
||||
@end table
|
||||
|
||||
@var{executable} is the program to execute. @var{argv} is the set of
|
||||
arguments to pass to the program; normally @code{@var{argv}[0]} will
|
||||
be a copy of @var{executable}.
|
||||
|
||||
@var{outname} is used to set the name of the file to use for standard
|
||||
output. There are two cases in which no output file will be used: 1)
|
||||
if @code{PEX_LAST} is not set in @var{flags}, and @code{PEX_USE_PIPES}
|
||||
was set in the call to @code{pex_init}, and the system supports pipes;
|
||||
2) if @code{PEX_LAST} is set in @var{flags}, and @var{outname} is
|
||||
@code{NULL}. Otherwise the code will use a file to hold standard
|
||||
output. If @code{PEX_LAST} is not set, this file is considered to be
|
||||
a temporary file, and it will be removed when no longer needed, unless
|
||||
@code{PEX_SAVE_TEMPS} was set in the call to @code{pex_init}.
|
||||
|
||||
There are two cases to consider when setting the name of the file to
|
||||
hold standard output.
|
||||
|
||||
First case: @code{PEX_SUFFIX} is set in @var{flags}. In this case
|
||||
@var{outname} may not be @code{NULL}. If the @var{tempbase} parameter
|
||||
to @code{pex_init} was not @code{NULL}, then the output file name is
|
||||
the concatenation of @var{tempbase} and @var{outname}. If
|
||||
@var{tempbase} was @code{NULL}, then the output file name is a random
|
||||
file name ending in @var{outname}.
|
||||
|
||||
Second case: @code{PEX_SUFFIX} was not set in @var{flags}. In this
|
||||
case, if @var{outname} is not @code{NULL}, it is used as the output
|
||||
file name. If @var{outname} is @code{NULL}, and @var{tempbase} was
|
||||
not NULL, the output file name is randomly chosen using
|
||||
@var{tempbase}. Otherwise the output file name is chosen completely
|
||||
at random.
|
||||
|
||||
@var{errname} is the file name to use for standard error output. If
|
||||
it is @code{NULL}, standard error is the same as the caller.
|
||||
Otherwise, standard error is written to the named file.
|
||||
|
||||
On an error return, the code sets @code{*@var{err}} to an @code{errno}
|
||||
value, or to 0 if there is no relevant @code{errno}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c alloca.c:26
|
||||
@deftypefn Replacement void* alloca (size_t @var{size})
|
||||
|
||||
@ -43,7 +176,7 @@ the possibility of a GCC built-in function.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c asprintf.c:33
|
||||
@c asprintf.c:29
|
||||
@deftypefn Extension int asprintf (char **@var{resptr}, const char *@var{format}, ...)
|
||||
|
||||
Like @code{sprintf}, but instead of passing a pointer to a buffer, you
|
||||
@ -104,7 +237,7 @@ is respectively less than, matching, or greater than the array member.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c argv.c:139
|
||||
@c argv.c:121
|
||||
@deftypefn Extension char** buildargv (char *@var{sp})
|
||||
|
||||
Given a pointer to a string, parse the string extracting fields
|
||||
@ -158,7 +291,7 @@ not recommended.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c make-temp-file.c:88
|
||||
@c make-temp-file.c:87
|
||||
@deftypefn Replacement char* choose_tmpdir ()
|
||||
|
||||
Returns a pointer to a directory path suitable for creating temporary
|
||||
@ -185,7 +318,7 @@ pointer encountered. Pointers to empty strings are ignored.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c argv.c:65
|
||||
@c argv.c:49
|
||||
@deftypefn Extension char** dupargv (char **@var{vector})
|
||||
|
||||
Duplicate an argument vector. Simply scans through @var{vector},
|
||||
@ -288,7 +421,7 @@ Ignores case when performing the comparison.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c argv.c:111
|
||||
@c argv.c:94
|
||||
@deftypefn Extension void freeargv (char **@var{vector})
|
||||
|
||||
Free an argument vector that was built using @code{buildargv}. Simply
|
||||
@ -412,7 +545,7 @@ struct qelem @{
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c safe-ctype.c:45
|
||||
@c safe-ctype.c:46
|
||||
@deffn Extension ISALPHA (@var{c})
|
||||
@deffnx Extension ISALNUM (@var{c})
|
||||
@deffnx Extension ISBLANK (@var{c})
|
||||
@ -462,7 +595,7 @@ false for characters with numeric values from 128 to 255.
|
||||
@end itemize
|
||||
@end deffn
|
||||
|
||||
@c safe-ctype.c:94
|
||||
@c safe-ctype.c:95
|
||||
@deffn Extension ISIDNUM (@var{c})
|
||||
@deffnx Extension ISIDST (@var{c})
|
||||
@deffnx Extension IS_VSPACE (@var{c})
|
||||
@ -535,7 +668,7 @@ relative prefix can be found, return @code{NULL}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c make-temp-file.c:138
|
||||
@c make-temp-file.c:137
|
||||
@deftypefn Replacement char* make_temp_file (const char *@var{suffix})
|
||||
|
||||
Return a temporary file name (as a string) or @code{NULL} if unable to
|
||||
@ -618,46 +751,62 @@ reading and writing.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c pexecute.txh:1
|
||||
@deftypefn Extension int pexecute (const char *@var{program}, char * const *@var{argv}, const char *@var{this_pname}, const char *@var{temp_base}, char **@var{errmsg_fmt}, char **@var{errmsg_arg}, int flags)
|
||||
@c pexecute.txh:155
|
||||
@deftypefn Extension void pex_free (struct pex_obj @var{obj})
|
||||
|
||||
Executes a program.
|
||||
|
||||
@var{program} and @var{argv} are the arguments to
|
||||
@code{execv}/@code{execvp}.
|
||||
|
||||
@var{this_pname} is name of the calling program (i.e., @code{argv[0]}).
|
||||
|
||||
@var{temp_base} is the path name, sans suffix, of a temporary file to
|
||||
use if needed. This is currently only needed for MS-DOS ports that
|
||||
don't use @code{go32} (do any still exist?). Ports that don't need it
|
||||
can pass @code{NULL}.
|
||||
|
||||
(@code{@var{flags} & PEXECUTE_SEARCH}) is non-zero if @env{PATH}
|
||||
should be searched (??? It's not clear that GCC passes this flag
|
||||
correctly). (@code{@var{flags} & PEXECUTE_FIRST}) is nonzero for the
|
||||
first process in chain. (@code{@var{flags} & PEXECUTE_FIRST}) is
|
||||
nonzero for the last process in chain. The first/last flags could be
|
||||
simplified to only mark the last of a chain of processes but that
|
||||
requires the caller to always mark the last one (and not give up
|
||||
early if some error occurs). It's more robust to require the caller
|
||||
to mark both ends of the chain.
|
||||
|
||||
The result is the pid on systems like Unix where we
|
||||
@code{fork}/@code{exec} and on systems like WIN32 and OS/2 where we
|
||||
use @code{spawn}. It is up to the caller to wait for the child.
|
||||
|
||||
The result is the @code{WEXITSTATUS} on systems like MS-DOS where we
|
||||
@code{spawn} and wait for the child here.
|
||||
|
||||
Upon failure, @var{errmsg_fmt} and @var{errmsg_arg} are set to the
|
||||
text of the error message with an optional argument (if not needed,
|
||||
@var{errmsg_arg} is set to @code{NULL}), and @minus{}1 is returned.
|
||||
@code{errno} is available to the caller to use.
|
||||
Clean up and free all data associated with @var{obj}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strsignal.c:546
|
||||
@c pexecute.txh:131
|
||||
@deftypefn Extension int pex_get_status (struct pex_obj *@var{obj}, int @var{count}, int *@var{vector})
|
||||
|
||||
Returns the exit status of all programs run using @var{obj}.
|
||||
@var{count} is the number of results expected. The results will be
|
||||
placed into @var{vector}. The results are in the order of the calls
|
||||
to @code{pex_run}. Returns 0 on error, 1 on success.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c pexecute.txh:140
|
||||
@deftypefn Extension int pex_get_times (struct pex_obj *@var{obj}, int @var{count}, struct pex_time *@var{vector})
|
||||
|
||||
Returns the process execution times of all programs run using
|
||||
@var{obj}. @var{count} is the number of results expected. The
|
||||
results will be placed into @var{vector}. The results are in the
|
||||
order of the calls to @code{pex_run}. Returns 0 on error, 1 on
|
||||
success.
|
||||
|
||||
@code{struct pex_time} has the following fields: @code{user_seconds},
|
||||
@code{user_microseconds}, @code{system_seconds},
|
||||
@code{system_microseconds}. On systems which do not support reporting
|
||||
process times, all the fields will be set to @code{0}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c pexecute.txh:119
|
||||
@deftypefn Extension FILE * pex_read_output (struct pex_obj *@var{obj}, int @var{binary})
|
||||
|
||||
Returns a @code{FILE} pointer which may be used to read the standard
|
||||
output of the last program in the pipeline. When this is used,
|
||||
@code{PEX_LAST} should not be used in a call to @code{pex_run}. After
|
||||
this is called, @code{pex_run} may no longer be called with the same
|
||||
@var{obj}. @var{binary} should be non-zero if the file should be
|
||||
opened in binary mode. Don't call @code{fclose} on the returned file;
|
||||
it will be closed by @code{pex_free}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c pexecute.txh:173
|
||||
@deftypefn Extension int pexecute (const char *@var{program}, char * const *@var{argv}, const char *@var{this_pname}, const char *@var{temp_base}, char **@var{errmsg_fmt}, char **@var{errmsg_arg}, int flags)
|
||||
|
||||
This is the old interface to execute one or more programs. It is
|
||||
still supported for compatibility purposes, but is no longer
|
||||
documented.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strsignal.c:539
|
||||
@deftypefn Supplemental void psignal (unsigned @var{signo}, char *@var{message})
|
||||
|
||||
Print @var{message} to the standard error, followed by a colon,
|
||||
@ -676,23 +825,10 @@ name is unset/removed.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c pexecute.txh:39
|
||||
@c pexecute.txh:181
|
||||
@deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags})
|
||||
|
||||
Waits for a program started by @code{pexecute} to finish.
|
||||
|
||||
@var{pid} is the process id of the task to wait for. @var{status} is
|
||||
the `status' argument to wait. @var{flags} is currently unused
|
||||
(allows future enhancement without breaking upward compatibility).
|
||||
Pass 0 for now.
|
||||
|
||||
The result is the pid of the child reaped, or -1 for failure
|
||||
(@code{errno} says why).
|
||||
|
||||
On systems that don't support waiting for a particular child,
|
||||
@var{pid} is ignored. On systems like MS-DOS that don't really
|
||||
multitask @code{pwait} is just a mechanism to provide a consistent
|
||||
interface for the caller.
|
||||
Another part of the old execution interface.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@ -711,7 +847,7 @@ control over the state of the random number generator.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c concat.c:177
|
||||
@c concat.c:167
|
||||
@deftypefn Extension char* reconcat (char *@var{optr}, const char *@var{s1}, @dots{}, @code{NULL})
|
||||
|
||||
Same as @code{concat}, except that if @var{optr} is not @code{NULL} it
|
||||
@ -754,7 +890,7 @@ environment. This implementation is not safe for multithreaded code.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strsignal.c:352
|
||||
@c strsignal.c:348
|
||||
@deftypefn Extension int signo_max (void)
|
||||
|
||||
Returns the maximum signal value for which a corresponding symbolic
|
||||
@ -845,7 +981,7 @@ Returns a pointer to a copy of @var{s} in memory obtained from
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strerror.c:671
|
||||
@c strerror.c:670
|
||||
@deftypefn Replacement {const char*} strerrno (int @var{errnum})
|
||||
|
||||
Given an error number returned from a system call (typically returned
|
||||
@ -919,7 +1055,7 @@ null character, the results are undefined.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strsignal.c:387
|
||||
@c strsignal.c:383
|
||||
@deftypefn Supplemental {const char *} strsignal (int @var{signo})
|
||||
|
||||
Maps an signal number to an signal message string, the contents of
|
||||
@ -940,7 +1076,7 @@ call to @code{strsignal}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strsignal.c:451
|
||||
@c strsignal.c:446
|
||||
@deftypefn Extension {const char*} strsigno (int @var{signo})
|
||||
|
||||
Given an signal number, returns a pointer to a string containing the
|
||||
@ -982,7 +1118,7 @@ the location referenced by @var{endptr}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strerror.c:731
|
||||
@c strerror.c:729
|
||||
@deftypefn Extension int strtoerrno (const char *@var{name})
|
||||
|
||||
Given the symbolic name of a error number (e.g., @code{EACCES}), map it
|
||||
@ -1006,7 +1142,7 @@ that the converted value is unsigned.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c strsignal.c:506
|
||||
@c strsignal.c:500
|
||||
@deftypefn Extension int strtosigno (const char *@var{name})
|
||||
|
||||
Given the symbolic name of a signal, map it to a signal number. If no
|
||||
@ -1035,7 +1171,7 @@ was made to unlink the file because it is special.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@c vasprintf.c:51
|
||||
@c vasprintf.c:47
|
||||
@deftypefn Extension int vasprintf (char **@var{resptr}, const char *@var{format}, va_list @var{args})
|
||||
|
||||
Like @code{vsprintf}, but instead of passing a pointer to a buffer,
|
||||
|
472
libiberty/pex-common.c
Normal file
472
libiberty/pex-common.c
Normal file
@ -0,0 +1,472 @@
|
||||
/* Common code for executing a program in a sub-process.
|
||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor <ian@airs.com>.
|
||||
|
||||
This file is part of the libiberty library.
|
||||
Libiberty is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
Libiberty is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with libiberty; see the file COPYING.LIB. If not,
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "config.h"
|
||||
#include "libiberty.h"
|
||||
#include "pex-common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#ifdef NEED_DECLARATION_ERRNO
|
||||
extern int errno;
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
extern int mkstemps (char *, int);
|
||||
|
||||
/* This file contains subroutines for the program execution routines
|
||||
(pex_init, pex_run, etc.). This file is compiled on all
|
||||
systems. */
|
||||
|
||||
static void pex_add_remove (struct pex_obj *, const char *, int);
|
||||
static int pex_get_status_and_time (struct pex_obj *, int, const char **,
|
||||
int *);
|
||||
|
||||
/* Initialize a pex_obj structure. */
|
||||
|
||||
struct pex_obj *
|
||||
pex_init_common (int flags, const char *pname, const char *tempbase,
|
||||
const struct pex_funcs *funcs)
|
||||
{
|
||||
struct pex_obj *obj;
|
||||
|
||||
obj = xmalloc (sizeof (*obj));
|
||||
obj->flags = flags;
|
||||
obj->pname = pname;
|
||||
obj->tempbase = tempbase;
|
||||
obj->next_input = STDIN_FILE_NO;
|
||||
obj->next_input_name = NULL;
|
||||
obj->next_input_name_allocated = 0;
|
||||
obj->count = 0;
|
||||
obj->children = NULL;
|
||||
obj->status = NULL;
|
||||
obj->time = NULL;
|
||||
obj->number_waited = 0;
|
||||
obj->read_output = NULL;
|
||||
obj->remove_count = 0;
|
||||
obj->remove = NULL;
|
||||
obj->funcs = funcs;
|
||||
obj->sysdep = NULL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Add a file to be removed when we are done. */
|
||||
|
||||
static void
|
||||
pex_add_remove (struct pex_obj *obj, const char *name, int allocated)
|
||||
{
|
||||
char *add;
|
||||
|
||||
++obj->remove_count;
|
||||
obj->remove = xrealloc (obj->remove, obj->remove_count * sizeof (char *));
|
||||
if (allocated)
|
||||
add = (char *) name;
|
||||
else
|
||||
add = xstrdup (name);
|
||||
obj->remove[obj->remove_count - 1] = add;
|
||||
}
|
||||
|
||||
/* Run a program. */
|
||||
|
||||
const char *
|
||||
pex_run (struct pex_obj *obj, int flags, const char *executable,
|
||||
char * const * argv, const char *orig_outname, const char *errname,
|
||||
int *err)
|
||||
{
|
||||
const char *errmsg;
|
||||
int in, out, errdes;
|
||||
char *outname;
|
||||
int outname_allocated;
|
||||
int p[2];
|
||||
long pid;
|
||||
|
||||
in = -1;
|
||||
out = -1;
|
||||
errdes = -1;
|
||||
outname = (char *) orig_outname;
|
||||
outname_allocated = 0;
|
||||
|
||||
/* Set IN. */
|
||||
|
||||
if (obj->next_input_name != NULL)
|
||||
{
|
||||
/* We have to make sure that the previous process has completed
|
||||
before we try to read the file. */
|
||||
if (!pex_get_status_and_time (obj, 0, &errmsg, err))
|
||||
goto error_exit;
|
||||
|
||||
in = obj->funcs->open_read (obj, obj->next_input_name,
|
||||
(flags & PEX_BINARY_INPUT) != 0);
|
||||
if (in < 0)
|
||||
{
|
||||
*err = errno;
|
||||
errmsg = "open temporary file";
|
||||
goto error_exit;
|
||||
}
|
||||
if (obj->next_input_name_allocated)
|
||||
{
|
||||
free (obj->next_input_name);
|
||||
obj->next_input_name_allocated = 0;
|
||||
}
|
||||
obj->next_input_name = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
in = obj->next_input;
|
||||
if (in < 0)
|
||||
{
|
||||
*err = 0;
|
||||
errmsg = "pipeline already complete";
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set OUT and OBJ->NEXT_INPUT/OBJ->NEXT_INPUT_NAME. */
|
||||
|
||||
if ((flags & PEX_LAST) != 0)
|
||||
{
|
||||
if (outname == NULL)
|
||||
out = STDOUT_FILE_NO;
|
||||
else if ((flags & PEX_SUFFIX) != 0)
|
||||
{
|
||||
outname = concat (obj->tempbase, outname, NULL);
|
||||
outname_allocated = 1;
|
||||
}
|
||||
obj->next_input = -1;
|
||||
}
|
||||
else if ((obj->flags & PEX_USE_PIPES) == 0)
|
||||
{
|
||||
if (outname == NULL)
|
||||
{
|
||||
if (obj->tempbase == NULL)
|
||||
{
|
||||
outname = make_temp_file (NULL);
|
||||
outname_allocated = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = strlen (obj->tempbase);
|
||||
|
||||
if (len >= 6
|
||||
&& strcmp (obj->tempbase + len - 6, "XXXXXX") == 0)
|
||||
outname = xstrdup (obj->tempbase);
|
||||
else
|
||||
outname = concat (obj->tempbase, "XXXXXX", NULL);
|
||||
|
||||
outname_allocated = 1;
|
||||
|
||||
out = mkstemps (outname, 0);
|
||||
if (out < 0)
|
||||
{
|
||||
*err = 0;
|
||||
errmsg = "could not create temporary output file";
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
/* This isn't obj->funcs->close because we got the
|
||||
descriptor from mkstemps, not from a function in
|
||||
obj->funcs. Calling close here is just like what
|
||||
make_temp_file does. */
|
||||
close (out);
|
||||
out = -1;
|
||||
}
|
||||
}
|
||||
else if ((flags & PEX_SUFFIX) != 0)
|
||||
{
|
||||
if (obj->tempbase == NULL)
|
||||
outname = make_temp_file (outname);
|
||||
else
|
||||
outname = concat (obj->tempbase, outname, NULL);
|
||||
outname_allocated = 1;
|
||||
}
|
||||
|
||||
if ((obj->flags & PEX_SAVE_TEMPS) == 0)
|
||||
{
|
||||
pex_add_remove (obj, outname, outname_allocated);
|
||||
outname_allocated = 0;
|
||||
}
|
||||
|
||||
if (!outname_allocated)
|
||||
{
|
||||
obj->next_input_name = outname;
|
||||
obj->next_input_name_allocated = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj->next_input_name = outname;
|
||||
outname_allocated = 0;
|
||||
obj->next_input_name_allocated = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (obj->funcs->pipe (obj, p, (flags & PEX_BINARY_OUTPUT) != 0) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
errmsg = "pipe";
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
out = p[WRITE_PORT];
|
||||
obj->next_input = p[READ_PORT];
|
||||
}
|
||||
|
||||
if (out < 0)
|
||||
{
|
||||
out = obj->funcs->open_write (obj, outname,
|
||||
(flags & PEX_BINARY_OUTPUT) != 0);
|
||||
if (out < 0)
|
||||
{
|
||||
*err = errno;
|
||||
errmsg = "open temporary output file";
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (outname_allocated)
|
||||
{
|
||||
free (outname);
|
||||
outname_allocated = 0;
|
||||
}
|
||||
|
||||
/* Set ERRDES. */
|
||||
|
||||
if (errname == NULL)
|
||||
errdes = STDERR_FILE_NO;
|
||||
else
|
||||
{
|
||||
/* We assume that stderr is in text mode--it certainly shouldn't
|
||||
be controlled by PEX_BINARY_OUTPUT. If necessary, we can add
|
||||
a PEX_BINARY_STDERR flag. */
|
||||
errdes = obj->funcs->open_write (obj, errname, 0);
|
||||
if (errdes < 0)
|
||||
{
|
||||
*err = errno;
|
||||
errmsg = "open error file";
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Run the program. */
|
||||
|
||||
pid = obj->funcs->exec_child (obj, flags, executable, argv, in, out, errdes,
|
||||
&errmsg, err);
|
||||
if (pid < 0)
|
||||
goto error_exit;
|
||||
|
||||
++obj->count;
|
||||
obj->children = xrealloc (obj->children, obj->count * sizeof (long));
|
||||
obj->children[obj->count - 1] = pid;
|
||||
|
||||
return NULL;
|
||||
|
||||
error_exit:
|
||||
if (in >= 0 && in != STDIN_FILE_NO)
|
||||
obj->funcs->close (obj, in);
|
||||
if (out >= 0 && out != STDOUT_FILE_NO)
|
||||
obj->funcs->close (obj, out);
|
||||
if (errdes >= 0 && errdes != STDERR_FILE_NO)
|
||||
obj->funcs->close (obj, errdes);
|
||||
if (outname_allocated)
|
||||
free (outname);
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
/* Return a FILE pointer for the output of the last program
|
||||
executed. */
|
||||
|
||||
FILE *
|
||||
pex_read_output (struct pex_obj *obj, int binary)
|
||||
{
|
||||
if (obj->next_input_name != NULL)
|
||||
{
|
||||
const char *errmsg;
|
||||
int err;
|
||||
|
||||
/* We have to make sure that the process has completed before we
|
||||
try to read the file. */
|
||||
if (!pex_get_status_and_time (obj, 0, &errmsg, &err))
|
||||
{
|
||||
errno = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj->read_output = fopen (obj->next_input_name, binary ? "rb" : "r");
|
||||
|
||||
if (obj->next_input_name_allocated)
|
||||
{
|
||||
free (obj->next_input_name);
|
||||
obj->next_input_name_allocated = 0;
|
||||
}
|
||||
obj->next_input_name = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
int o;
|
||||
|
||||
o = obj->next_input;
|
||||
if (o < 0 || o == STDIN_FILE_NO)
|
||||
return NULL;
|
||||
obj->read_output = obj->funcs->fdopenr (obj, o, binary);
|
||||
obj->next_input = -1;
|
||||
}
|
||||
|
||||
return obj->read_output;
|
||||
}
|
||||
|
||||
/* Get the exit status and, if requested, the resource time for all
|
||||
the child processes. Return 0 on failure, 1 on success. */
|
||||
|
||||
static int
|
||||
pex_get_status_and_time (struct pex_obj *obj, int done, const char **errmsg,
|
||||
int *err)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (obj->number_waited == obj->count)
|
||||
return 1;
|
||||
|
||||
obj->status = xrealloc (obj->status, obj->count * sizeof (int));
|
||||
if ((obj->flags & PEX_RECORD_TIMES) != 0)
|
||||
obj->time = xrealloc (obj->time, obj->count * sizeof (struct pex_time));
|
||||
|
||||
ret = 1;
|
||||
for (i = obj->number_waited; i < obj->count; ++i)
|
||||
{
|
||||
if (obj->funcs->wait (obj, obj->children[i], &obj->status[i],
|
||||
obj->time == NULL ? NULL : &obj->time[i],
|
||||
done, errmsg, err) < 0)
|
||||
ret = 0;
|
||||
}
|
||||
obj->number_waited = i;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get exit status of executed programs. */
|
||||
|
||||
int
|
||||
pex_get_status (struct pex_obj *obj, int count, int *vector)
|
||||
{
|
||||
if (obj->status == NULL)
|
||||
{
|
||||
const char *errmsg;
|
||||
int err;
|
||||
|
||||
if (!pex_get_status_and_time (obj, 0, &errmsg, &err))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count > obj->count)
|
||||
{
|
||||
memset (vector + obj->count, 0, (count - obj->count) * sizeof (int));
|
||||
count = obj->count;
|
||||
}
|
||||
|
||||
memcpy (vector, obj->status, count * sizeof (int));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get process times of executed programs. */
|
||||
|
||||
int
|
||||
pex_get_times (struct pex_obj *obj, int count, struct pex_time *vector)
|
||||
{
|
||||
if (obj->status == NULL)
|
||||
{
|
||||
const char *errmsg;
|
||||
int err;
|
||||
|
||||
if (!pex_get_status_and_time (obj, 0, &errmsg, &err))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (obj->time == NULL)
|
||||
return 0;
|
||||
|
||||
if (count > obj->count)
|
||||
{
|
||||
memset (vector + obj->count, 0,
|
||||
(count - obj->count) * sizeof (struct pex_time));
|
||||
count = obj->count;
|
||||
}
|
||||
|
||||
memcpy (vector, obj->time, count * sizeof (struct pex_time));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Free a pex_obj structure. */
|
||||
|
||||
void
|
||||
pex_free (struct pex_obj *obj)
|
||||
{
|
||||
if (obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO)
|
||||
obj->funcs->close (obj, obj->next_input);
|
||||
|
||||
/* If the caller forgot to wait for the children, we do it here, to
|
||||
avoid zombies. */
|
||||
if (obj->status == NULL)
|
||||
{
|
||||
const char *errmsg;
|
||||
int err;
|
||||
|
||||
obj->flags &= ~ PEX_RECORD_TIMES;
|
||||
pex_get_status_and_time (obj, 1, &errmsg, &err);
|
||||
}
|
||||
|
||||
if (obj->next_input_name_allocated)
|
||||
free (obj->next_input_name);
|
||||
if (obj->children != NULL)
|
||||
free (obj->children);
|
||||
if (obj->status != NULL)
|
||||
free (obj->status);
|
||||
if (obj->time != NULL)
|
||||
free (obj->time);
|
||||
if (obj->read_output != NULL)
|
||||
fclose (obj->read_output);
|
||||
|
||||
if (obj->remove_count > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < obj->remove_count; ++i)
|
||||
{
|
||||
remove (obj->remove[i]);
|
||||
free (obj->remove[i]);
|
||||
}
|
||||
free (obj->remove);
|
||||
}
|
||||
|
||||
if (obj->funcs->cleanup != NULL)
|
||||
obj->funcs->cleanup (obj);
|
||||
|
||||
free (obj);
|
||||
}
|
@ -24,6 +24,7 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "config.h"
|
||||
#include "libiberty.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define install_error_msg "installation problem, cannot exec `%s'"
|
||||
|
||||
@ -42,4 +43,87 @@ Boston, MA 02111-1307, USA. */
|
||||
/* value of `pipe': port index for writing. */
|
||||
#define WRITE_PORT 1
|
||||
|
||||
/* The structure used by pex_init and friends. */
|
||||
|
||||
struct pex_obj
|
||||
{
|
||||
/* Flags. */
|
||||
int flags;
|
||||
/* Name of calling program, for error messages. */
|
||||
const char *pname;
|
||||
/* Base name to use for temporary files. */
|
||||
const char *tempbase;
|
||||
/* Pipe to use as stdin for next process. */
|
||||
int next_input;
|
||||
/* File name to use as stdin for next process. */
|
||||
char *next_input_name;
|
||||
/* Whether next_input_name was allocated using malloc. */
|
||||
int next_input_name_allocated;
|
||||
/* Number of child processes. */
|
||||
int count;
|
||||
/* PIDs of child processes; array allocated using maloc. */
|
||||
long *children;
|
||||
/* Exit statuses of child processes; array allocated using malloc. */
|
||||
int *status;
|
||||
/* Time used by child processes; array allocated using malloc. */
|
||||
struct pex_time *time;
|
||||
/* Number of children we have already waited for. */
|
||||
int number_waited;
|
||||
/* FILE created by pex_read_output. */
|
||||
FILE *read_output;
|
||||
/* Number of temporary files to remove. */
|
||||
int remove_count;
|
||||
/* List of temporary files to remove; array allocated using malloc
|
||||
of strings allocated using malloc. */
|
||||
char **remove;
|
||||
/* Pointers to system dependent functions. */
|
||||
const struct pex_funcs *funcs;
|
||||
/* For use by system dependent code. */
|
||||
void *sysdep;
|
||||
};
|
||||
|
||||
/* Functions passed to pex_run_common. */
|
||||
|
||||
struct pex_funcs
|
||||
{
|
||||
/* Open file NAME for reading. If BINARY is non-zero, open in
|
||||
binary mode. Return >= 0 on success, -1 on error. */
|
||||
int (*open_read) (struct pex_obj *, const char *name, int binary);
|
||||
/* Open file NAME for writing. If BINARY is non-zero, open in
|
||||
binary mode. Return >= 0 on success, -1 on error. */
|
||||
int (*open_write) (struct pex_obj *, const char *name, int binary);
|
||||
/* Execute a child process. FLAGS, EXECUTABLE, ARGV, ERR are from
|
||||
pex_run. IN, OUT, ERRDES are each a descriptor, from open_read,
|
||||
open_write, or pipe, or they are one of STDIN_FILE_NO,
|
||||
STDOUT_FILE_NO or STDERR_FILE_NO; if not STD*_FILE_NO, they
|
||||
should be closed. The function should handle the
|
||||
PEX_STDERR_TO_STDOUT flag. Return >= 0 on success, or -1 on
|
||||
error and set *ERRMSG and *ERR. */
|
||||
long (*exec_child) (struct pex_obj *, int flags, const char *executable,
|
||||
char * const * argv, int in, int out, int errdes,
|
||||
const char **errmsg, int *err);
|
||||
/* Close a descriptor. Return 0 on success, -1 on error. */
|
||||
int (*close) (struct pex_obj *, int);
|
||||
/* Wait for a child to complete, returning exit status in *STATUS
|
||||
and time in *TIME (if it is not null). CHILD is from fork. DONE
|
||||
is 1 if this is called via pex_free. ERRMSG and ERR are as in
|
||||
fork. Return 0 on success, -1 on error. */
|
||||
int (*wait) (struct pex_obj *, long, int *status, struct pex_time *time,
|
||||
int done, const char **errmsg, int *err);
|
||||
/* Create a pipe (only called if PEX_USE_PIPES is set) storing two
|
||||
descriptin in *P. If BINARY is non-zero, open in binary mode.
|
||||
Return 0 on success, -1 on error. */
|
||||
int (*pipe) (struct pex_obj *, int *p, int binary);
|
||||
/* Get a FILE pointer to read from a file descriptor (only called if
|
||||
PEX_USE_PIPES is set). If BINARY is non-zero, open in binary
|
||||
mode. Return pointer on success, NULL on error. */
|
||||
FILE * (*fdopenr) (struct pex_obj *, int fd, int binary);
|
||||
/* Free any system dependent data associated with OBJ. May be
|
||||
NULL if there is nothing to do. */
|
||||
void (*cleanup) (struct pex_obj *);
|
||||
};
|
||||
|
||||
extern struct pex_obj *pex_init_common (int, const char *, const char *,
|
||||
const struct pex_funcs *);
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* Utilities to execute a program in a subprocess (possibly linked by pipes
|
||||
with other subprocesses), and wait for it. DJGPP specialization.
|
||||
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003
|
||||
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2005
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the libiberty library.
|
||||
@ -38,59 +38,246 @@ extern int errno;
|
||||
#define PWAIT_ERROR EINVAL
|
||||
#endif
|
||||
|
||||
/* MSDOS doesn't multitask, but for the sake of a consistent interface
|
||||
the code behaves like it does. pexecute runs the program, tucks the
|
||||
exit code away, and returns a "pid". pwait must be called to fetch the
|
||||
exit code. */
|
||||
static int pex_djgpp_open_read (struct pex_obj *, const char *, int);
|
||||
static int pex_djgpp_open_write (struct pex_obj *, const char *, int);
|
||||
static long pex_djgpp_exec_child (struct pex_obj *, int, const char *,
|
||||
char * const *, int, int, int,
|
||||
const char **, int *);
|
||||
static int pex_djgpp_close (struct pex_obj *, int);
|
||||
static int pex_djgpp_wait (struct pex_obj *, long, int *, struct pex_time *,
|
||||
int, const char **, int *);
|
||||
|
||||
/* For communicating information from pexecute to pwait. */
|
||||
static int last_pid = 0;
|
||||
static int last_status = 0;
|
||||
static int last_reaped = 0;
|
||||
/* The list of functions we pass to the common routines. */
|
||||
|
||||
int
|
||||
pexecute (const char *program, char * const *argv, const char *this_pname,
|
||||
const char *temp_base, char **errmsg_fmt,
|
||||
char **errmsg_arg, int flags)
|
||||
const struct pex_funcs funcs =
|
||||
{
|
||||
int rc;
|
||||
pex_djgpp_open_read,
|
||||
pex_djgpp_open_write,
|
||||
pex_djgpp_exec_child,
|
||||
pex_djgpp_close,
|
||||
pex_djgpp_wait,
|
||||
NULL, /* pipe */
|
||||
NULL, /* fdopenr */
|
||||
NULL /* cleanup */
|
||||
};
|
||||
|
||||
last_pid++;
|
||||
if (last_pid < 0)
|
||||
last_pid = 1;
|
||||
/* Return a newly initialized pex_obj structure. */
|
||||
|
||||
if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
|
||||
abort ();
|
||||
|
||||
/* ??? What are the possible return values from spawnv? */
|
||||
rc = (flags & PEXECUTE_SEARCH ? spawnvp : spawnv) (P_WAIT, program, argv);
|
||||
|
||||
if (rc == -1)
|
||||
{
|
||||
*errmsg_fmt = install_error_msg;
|
||||
*errmsg_arg = (char *)program;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Tuck the status away for pwait, and return a "pid". */
|
||||
last_status = rc << 8;
|
||||
return last_pid;
|
||||
struct pex_obj *
|
||||
pex_init (int flags, const char *pname, const char *tempbase)
|
||||
{
|
||||
/* DJGPP does not support pipes. */
|
||||
flags &= ~ PEX_USE_PIPES;
|
||||
return pex_init_common (flags, pname, tempbase, funcs);
|
||||
}
|
||||
|
||||
int
|
||||
pwait (int pid, int *status, int flags)
|
||||
/* Open a file for reading. */
|
||||
|
||||
static int
|
||||
pex_djgpp_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED,
|
||||
const char *name, int binary)
|
||||
{
|
||||
/* On MSDOS each pexecute must be followed by its associated pwait. */
|
||||
if (pid != last_pid
|
||||
/* Called twice for the same child? */
|
||||
|| pid == last_reaped)
|
||||
{
|
||||
errno = PWAIT_ERROR;
|
||||
return -1;
|
||||
}
|
||||
/* ??? Here's an opportunity to canonicalize the values in STATUS.
|
||||
Needed? */
|
||||
*status = (last_status >> 8);
|
||||
last_reaped = last_pid;
|
||||
return last_pid;
|
||||
return open (name, O_RDONLY | (binary ? O_BINARY : O_TEXT));
|
||||
}
|
||||
|
||||
/* Open a file for writing. */
|
||||
|
||||
static int
|
||||
pex_djgpp_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED,
|
||||
const char *name, int binary)
|
||||
{
|
||||
/* Note that we can't use O_EXCL here because gcc may have already
|
||||
created the temporary file via make_temp_file. */
|
||||
return open (name,
|
||||
(O_WRONLY | O_CREAT | O_TRUNC
|
||||
| (binary ? O_BINARY : O_TEXT)),
|
||||
S_IRUSR | S_IWUSR);
|
||||
}
|
||||
|
||||
/* Close a file. */
|
||||
|
||||
static int
|
||||
pex_djgpp_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
|
||||
{
|
||||
return close (fd);
|
||||
}
|
||||
|
||||
/* Execute a child. */
|
||||
|
||||
static long
|
||||
pex_djgpp_exec_child (struct pex_obj *obj, int flags, const char *executable,
|
||||
char * const * argv, int in, int out, int errdes,
|
||||
const char **errmsg, int *err)
|
||||
{
|
||||
int org_in, org_out, org_errdes;
|
||||
int status;
|
||||
int *statuses;
|
||||
|
||||
org_in = -1;
|
||||
org_out = -1;
|
||||
org_errdes = -1;
|
||||
|
||||
if (in != STDIN_FILE_NO)
|
||||
{
|
||||
org_in = _dup (STDIN_FILE_NO);
|
||||
if (org_in < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup";
|
||||
return -1;
|
||||
}
|
||||
if (_dup2 (in, STDIN_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (in) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (out != STDOUT_FILE_NO)
|
||||
{
|
||||
org_out = _dup (STDOUT_FILE_NO);
|
||||
if (org_out < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup";
|
||||
return -1;
|
||||
}
|
||||
if (_dup2 (out, STDOUT_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (out) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errdes != STDERR_FILE_NO
|
||||
|| (flags & PEX_STDERR_TO_STDOUT) != 0)
|
||||
{
|
||||
int e;
|
||||
|
||||
org_errdes = _dup (STDERR_FILE_NO);
|
||||
if (org_errdes < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup";
|
||||
return -1;
|
||||
}
|
||||
if (_dup2 ((flags & PEX_STDERR_TO_STDOUT) != 0 ? STDOUT_FILE_NO : errdes,
|
||||
STDERR_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (errdes != STDERR_FILE_NO)
|
||||
{
|
||||
if (_close (errdes) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status = (((flags & PEX_SEARCH) != 0 ? _spawnvp : _spawnv)
|
||||
(P_WAIT, program, (const char **) argv));
|
||||
|
||||
if (status == -1)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = ((flags & PEX_SEARCH) != 0) ? "_spawnvp" : "_spawnv";
|
||||
}
|
||||
|
||||
if (in != STDIN_FILE_NO)
|
||||
{
|
||||
if (_dup2 (org_in, STDIN_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (org_in) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (out != STDOUT_FILE_NO)
|
||||
{
|
||||
if (_dup2 (org_out, STDOUT_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (org_out) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errdes != STDERR_FILE_NO
|
||||
|| (flags & PEX_STDERR_TO_STDOUT) != 0)
|
||||
{
|
||||
if (_dup2 (org_errdes, STDERR_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (org_errdes) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the exit status for later. When we are called, obj->count
|
||||
is the number of children which have executed before this
|
||||
one. */
|
||||
statuses = (int *) obj->sysdep;
|
||||
statuses = xrealloc (statuses, (obj->count + 1) * sizeof (int));
|
||||
statuses[obj->count] = status;
|
||||
obj->sysdep = (void *) statuses;
|
||||
|
||||
return obj->count;
|
||||
}
|
||||
|
||||
/* Wait for a child process to complete. Actually the child process
|
||||
has already completed, and we just need to return the exit
|
||||
status. */
|
||||
|
||||
static int
|
||||
pex_djgpp_wait (struct pex_obj *obj, long pid, int *status,
|
||||
struct pex_time *time, int done, const char **errmsg,
|
||||
int *err)
|
||||
{
|
||||
int *statuses;
|
||||
|
||||
if (time != NULL)
|
||||
memset (time, 0, sizeof *time);
|
||||
|
||||
statuses = (int *) obj->sysdep;
|
||||
*status = statuses[pid];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* Utilities to execute a program in a subprocess (possibly linked by pipes
|
||||
with other subprocesses), and wait for it. Generic MSDOS specialization.
|
||||
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003
|
||||
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2005
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the libiberty library.
|
||||
@ -36,105 +36,281 @@ extern int errno;
|
||||
#include "safe-ctype.h"
|
||||
#include <process.h>
|
||||
|
||||
/* MSDOS doesn't multitask, but for the sake of a consistent interface
|
||||
the code behaves like it does. pexecute runs the program, tucks the
|
||||
exit code away, and returns a "pid". pwait must be called to fetch the
|
||||
exit code. */
|
||||
/* The structure we keep in obj->sysdep. */
|
||||
|
||||
/* For communicating information from pexecute to pwait. */
|
||||
static int last_pid = 0;
|
||||
static int last_status = 0;
|
||||
static int last_reaped = 0;
|
||||
#define PEX_MSDOS_FILE_COUNT 3
|
||||
|
||||
int
|
||||
pexecute (const char *program, char * const *argv, const char *this_pname,
|
||||
const char *temp_base, char **errmsg_fmt, char **errmsg_arg,
|
||||
int flags)
|
||||
#define PEX_MSDOS_FD_OFFSET 10
|
||||
|
||||
struct pex_msdos
|
||||
{
|
||||
int rc;
|
||||
char *scmd, *rf;
|
||||
FILE *argfile;
|
||||
int i, el = flags & PEXECUTE_SEARCH ? 4 : 0;
|
||||
/* An array of file names. We refer to these using file descriptors
|
||||
of 10 + array index. */
|
||||
const char *files[PEX_MSDOS_FILE_COUNT];
|
||||
/* Exit statuses of programs which have been run. */
|
||||
int *statuses;
|
||||
};
|
||||
|
||||
last_pid++;
|
||||
if (last_pid < 0)
|
||||
last_pid = 1;
|
||||
static int pex_msdos_open (struct pex_obj *, const char *, int);
|
||||
static int pex_msdos_open (struct pex_obj *, const char *, int);
|
||||
static int pex_msdos_fdindex (struct pex_msdos *, int);
|
||||
static long pex_msdos_exec_child (struct pex_obj *, int, const char *,
|
||||
char * const *, int, int, int,
|
||||
const char **, int *);
|
||||
static int pex_msdos_close (struct pex_obj *, int);
|
||||
static int pex_msdos_wait (struct pex_obj *, long, int *, struct pex_time *,
|
||||
int, const char **, int *);
|
||||
static void pex_msdos_cleanup (struct pex_obj *);
|
||||
|
||||
if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
|
||||
abort ();
|
||||
/* The list of functions we pass to the common routines. */
|
||||
|
||||
if (temp_base == 0)
|
||||
temp_base = choose_temp_base ();
|
||||
scmd = (char *) xmalloc (strlen (program) + strlen (temp_base) + 6 + el);
|
||||
rf = scmd + strlen(program) + 2 + el;
|
||||
sprintf (scmd, "%s%s @%s.gp", program,
|
||||
(flags & PEXECUTE_SEARCH ? ".exe" : ""), temp_base);
|
||||
argfile = fopen (rf, "w");
|
||||
if (argfile == 0)
|
||||
const struct pex_funcs funcs =
|
||||
{
|
||||
pex_msdos_open,
|
||||
pex_msdos_open,
|
||||
pex_msdos_exec_child,
|
||||
pex_msdos_close,
|
||||
pex_msdos_wait,
|
||||
NULL, /* pipe */
|
||||
NULL, /* fdopenr */
|
||||
pex_msdos_cleanup
|
||||
};
|
||||
|
||||
/* Return a newly initialized pex_obj structure. */
|
||||
|
||||
struct pex_obj *
|
||||
pex_init (int flags, const char *pname, const char *tempbase)
|
||||
{
|
||||
struct pex_obj *ret;
|
||||
int i;
|
||||
|
||||
/* MSDOS does not support pipes. */
|
||||
flags &= ~ PEX_USE_PIPES;
|
||||
|
||||
ret = pex_init_common (flags, pname, tempbase, funcs);
|
||||
|
||||
ret->sysdep = xmalloc (sizeof (struct pex_msdos));
|
||||
for (i = 0; i < PEX_MSDOS_FILE_COUNT; ++i)
|
||||
ret->files[i] = NULL;
|
||||
ret->statuses = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Open a file. FIXME: We ignore the binary argument, since we have
|
||||
no way to handle it. */
|
||||
|
||||
static int
|
||||
pex_msdos_open (struct pex_obj *obj, const char *name,
|
||||
int binary ATTRIBUTE_UNUSED)
|
||||
{
|
||||
struct pex_msdos *ms;
|
||||
int i;
|
||||
|
||||
ms = (struct pex_msdos *) obj->sysdep;
|
||||
|
||||
for (i = 0; i < PEX_MSDOS_FILE_COUNT; ++i)
|
||||
{
|
||||
int errno_save = errno;
|
||||
if (ms->files[i] == NULL)
|
||||
{
|
||||
ms->files[i] = xstrdup (name);
|
||||
return i + PEX_MSDOS_FD_OFFSET;
|
||||
}
|
||||
}
|
||||
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Get the index into msdos->files associated with an open file
|
||||
descriptor. */
|
||||
|
||||
static int
|
||||
pex_msdos_fdindex (struct pex_msdos *ms, int fd)
|
||||
{
|
||||
fd -= PEX_MSDOS_FD_OFFSET;
|
||||
if (fd < 0 || fd >= PEX_MSDOS_FILE_COUNT || ms->files[fd] == NULL)
|
||||
abort ();
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
/* Close a file. */
|
||||
|
||||
static int
|
||||
pex_msdos_close (struct pex_obj *obj, int fd)
|
||||
{
|
||||
struct pex_msdos *ms;
|
||||
int fdinex;
|
||||
|
||||
ms = (struct pex_msdos *) obj->sysdep;
|
||||
fdindex = pe_msdos_fdindex (ms, fd);
|
||||
free (ms->files[fdindex]);
|
||||
ms->files[fdindex] = NULL;
|
||||
}
|
||||
|
||||
/* Execute a child. */
|
||||
|
||||
static long
|
||||
pex_msdos_exec_child (struct pex_obj *obj, int flags, const char *executable,
|
||||
char * const * argv, int in, int out,
|
||||
int errdes ATTRIBUTE_UNUSED, const char **errmsg,
|
||||
int *err)
|
||||
{
|
||||
struct pex_msdos *ms;
|
||||
char *temp_base;
|
||||
int temp_base_allocated;
|
||||
char *rf;
|
||||
int inindex;
|
||||
char *infile;
|
||||
int outindex;
|
||||
char *outfile;
|
||||
char *scmd;
|
||||
FILE *argfile;
|
||||
int i;
|
||||
int status;
|
||||
|
||||
ms = (struct pex_msdos *) obj->sysdep;
|
||||
|
||||
/* FIXME: I don't know how to redirect stderr, so we ignore ERRDES
|
||||
and PEX_STDERR_TO_STDOUT. */
|
||||
|
||||
temp_base = obj->temp_base;
|
||||
if (temp_base != NULL)
|
||||
temp_base_allocated = 0;
|
||||
else
|
||||
{
|
||||
temp_base = choose_temp_base ();
|
||||
temp_base_allocated = 1;
|
||||
}
|
||||
|
||||
rf = concat (temp_base, ".gp", NULL);
|
||||
|
||||
if (temp_base_allocated)
|
||||
free (temp_base);
|
||||
|
||||
if (in == STDIN_FILE_NO)
|
||||
{
|
||||
inindex = -1;
|
||||
infile = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
inindex = pex_msdos_fdindex (ms, in);
|
||||
infile = ms->files[inindex];
|
||||
}
|
||||
|
||||
if (out == STDOUT_FILE_NO)
|
||||
{
|
||||
outindex = -1;
|
||||
outfile = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
outindex = pex_msdos_fdindex (ms, out);
|
||||
outfile = ms->files[outindex];
|
||||
}
|
||||
|
||||
scmd = xmalloc (strlen (program)
|
||||
+ ((flags & PEXECUTE_SEARCH) != 0 ? 4 : 0)
|
||||
+ strlen (rf)
|
||||
+ strlen (infile)
|
||||
+ strlen (outfile)
|
||||
+ 10);
|
||||
sprintf (scmd, "%s%s @%s%s%s%s%s",
|
||||
program,
|
||||
(flags & PEXECUTE_SEARCH) != 0 ? ".exe" : "",
|
||||
rf,
|
||||
inindex != -1 ? " <" : "",
|
||||
infile,
|
||||
outindex != -1 ? " >" : "",
|
||||
outfile);
|
||||
|
||||
argfile = fopen (rf, "w");
|
||||
if (argfile == NULL)
|
||||
{
|
||||
*err = errno;
|
||||
free (scmd);
|
||||
errno = errno_save;
|
||||
*errmsg_fmt = "cannot open `%s.gp'";
|
||||
*errmsg_arg = temp_base;
|
||||
free (rf);
|
||||
*errmsg = "cannot open temporary command file";
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=1; argv[i]; i++)
|
||||
for (i = 1; argv[i] != NULL; ++i)
|
||||
{
|
||||
char *cp;
|
||||
for (cp = argv[i]; *cp; cp++)
|
||||
char *p;
|
||||
|
||||
for (p = argv[i]; *p != '\0'; ++p)
|
||||
{
|
||||
if (*cp == '"' || *cp == '\'' || *cp == '\\' || ISSPACE (*cp))
|
||||
fputc ('\\', argfile);
|
||||
fputc (*cp, argfile);
|
||||
if (*p == '"' || *p == '\'' || *p == '\\' || ISSPACE (*p))
|
||||
putc ('\\', argfile);
|
||||
putc (*p, argfile);
|
||||
}
|
||||
fputc ('\n', argfile);
|
||||
putc ('\n', argfile);
|
||||
}
|
||||
|
||||
fclose (argfile);
|
||||
|
||||
rc = system (scmd);
|
||||
status = system (scmd);
|
||||
|
||||
{
|
||||
int errno_save = errno;
|
||||
remove (rf);
|
||||
free (scmd);
|
||||
errno = errno_save;
|
||||
}
|
||||
|
||||
if (rc == -1)
|
||||
if (status == -1)
|
||||
{
|
||||
*errmsg_fmt = install_error_msg;
|
||||
*errmsg_arg = (char *)program;
|
||||
*err = errno;
|
||||
remove (rf);
|
||||
free (scmd);
|
||||
free (rf);
|
||||
*errmsg = "system";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Tuck the status away for pwait, and return a "pid". */
|
||||
last_status = rc << 8;
|
||||
return last_pid;
|
||||
remove (rf);
|
||||
free (scmd);
|
||||
free (rf);
|
||||
|
||||
/* Save the exit status for later. When we are called, obj->count
|
||||
is the number of children which have executed before this
|
||||
one. */
|
||||
ms->statuses = xrealloc (ms->statuses, (obj->count + 1) * sizeof (int));
|
||||
ms->statuses[obj->count] = status;
|
||||
|
||||
return obj->count;
|
||||
}
|
||||
|
||||
/* Use ECHILD if available, otherwise use EINVAL. */
|
||||
#ifdef ECHILD
|
||||
#define PWAIT_ERROR ECHILD
|
||||
#else
|
||||
#define PWAIT_ERROR EINVAL
|
||||
#endif
|
||||
/* Wait for a child process to complete. Actually the child process
|
||||
has already completed, and we just need to return the exit
|
||||
status. */
|
||||
|
||||
int
|
||||
pwait (int pid, int *status, int flags)
|
||||
static int
|
||||
pex_msdos_wait (struct pex_obj *obj, long pid, int *status,
|
||||
struct pex_time *time, int done ATTRIBUTE_UNUSED,
|
||||
const char **errmsg ATTRIBUTE_UNUSED,
|
||||
int *err ATTRIBUTE_UNUSED)
|
||||
{
|
||||
/* On MSDOS each pexecute must be followed by its associated pwait. */
|
||||
if (pid != last_pid
|
||||
/* Called twice for the same child? */
|
||||
|| pid == last_reaped)
|
||||
{
|
||||
errno = PWAIT_ERROR;
|
||||
return -1;
|
||||
}
|
||||
/* ??? Here's an opportunity to canonicalize the values in STATUS.
|
||||
Needed? */
|
||||
*status = last_status;
|
||||
last_reaped = last_pid;
|
||||
return last_pid;
|
||||
struct pex_msdos *ms;
|
||||
|
||||
ms = (struct pex_msdos *) obj->sysdep;
|
||||
|
||||
if (time != NULL)
|
||||
memset (time, 0, sizeof *time);
|
||||
|
||||
*status = ms->statuses[pid];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clean up the pex_msdos structure. */
|
||||
|
||||
static void
|
||||
pex_msdos_cleanup (struct pex_obj *obj)
|
||||
{
|
||||
struct pex_msdos *ms;
|
||||
int i;
|
||||
|
||||
ms = (struct pex_msdos *) obj->sysdep;
|
||||
for (i = 0; i < PEX_MSDOS_FILE_COUNT; ++i)
|
||||
if (msdos->files[i] != NULL)
|
||||
free (msdos->files[i]);
|
||||
if (msdos->statuses != NULL)
|
||||
free (msdos->statuses);
|
||||
free (msdos);
|
||||
obj->sysdep = NULL;
|
||||
}
|
||||
|
43
libiberty/pex-one.c
Normal file
43
libiberty/pex-one.c
Normal file
@ -0,0 +1,43 @@
|
||||
/* Execute a program and wait for a result.
|
||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the libiberty library.
|
||||
Libiberty is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
Libiberty is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with libiberty; see the file COPYING.LIB. If not,
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "config.h"
|
||||
#include "libiberty.h"
|
||||
|
||||
const char *
|
||||
pex_one (int flags, const char *executable, char * const *argv,
|
||||
const char *pname, const char *outname, const char *errname,
|
||||
int *status, int *err)
|
||||
{
|
||||
struct pex_obj *obj;
|
||||
const char *errmsg;
|
||||
|
||||
obj = pex_init (0, pname, NULL);
|
||||
errmsg = pex_run (obj, flags, executable, argv, outname, errname, err);
|
||||
if (errmsg == NULL)
|
||||
{
|
||||
if (!pex_get_status (obj, 1, status))
|
||||
{
|
||||
*err = 0;
|
||||
errmsg = "pex_get_status failed";
|
||||
}
|
||||
}
|
||||
pex_free (obj);
|
||||
return errmsg;
|
||||
}
|
@ -20,29 +20,42 @@ License along with libiberty; see the file COPYING.LIB. If not,
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "config.h"
|
||||
#include "libiberty.h"
|
||||
#include "pex-common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#ifdef NEED_DECLARATION_ERRNO
|
||||
extern int errno;
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WAITPID
|
||||
#define waitpid(pid, status, flags) wait(status)
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef vfork /* Autoconf may define this to fork for us. */
|
||||
# define VFORK_STRING "fork"
|
||||
@ -57,80 +70,300 @@ extern int errno;
|
||||
lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
|
||||
#endif /* VMS */
|
||||
|
||||
/* Execute a program, possibly setting up pipes to programs executed
|
||||
via other calls to this function.
|
||||
|
||||
This version of the function uses vfork. In general vfork is
|
||||
similar to setjmp/longjmp, in that any variable which is modified by
|
||||
the child process has an indeterminate value in the parent process.
|
||||
We follow a safe approach here by not modifying any variables at
|
||||
all in the child process (with the possible exception of variables
|
||||
modified by xstrerror if exec fails, but this is unlikely to be
|
||||
detectable).
|
||||
/* File mode to use for private and world-readable files. */
|
||||
|
||||
We work a little bit harder to avoid gcc warnings. gcc will warn
|
||||
about any automatic variable which is live at the time of the
|
||||
vfork, which is non-volatile, and which is either set more than
|
||||
once or is an argument to the function. This warning isn't quite
|
||||
right, since what we really care about is whether the variable is
|
||||
live at the time of the vfork and set afterward by the child
|
||||
process, but gcc only checks whether the variable is set more than
|
||||
once. To avoid this warning, we ensure that any variable which is
|
||||
live at the time of the vfork (i.e., used after the vfork) is set
|
||||
exactly once and is not an argument, or is marked volatile. */
|
||||
#if defined (S_IRUSR) && defined (S_IWUSR) && defined (S_IRGRP) && defined (S_IWGRP) && defined (S_IROTH) && defined (S_IWOTH)
|
||||
#define PUBLIC_MODE \
|
||||
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
|
||||
#else
|
||||
#define PUBLIC_MODE 0666
|
||||
#endif
|
||||
|
||||
int
|
||||
pexecute (const char *program, char * const *argv, const char *this_pname,
|
||||
const char *temp_base ATTRIBUTE_UNUSED,
|
||||
char **errmsg_fmt, char **errmsg_arg, int flagsarg)
|
||||
/* Get the exit status of a particular process, and optionally get the
|
||||
time that it took. This is simple if we have wait4, slightly
|
||||
harder if we have waitpid, and is a pain if we only have wait. */
|
||||
|
||||
static pid_t pex_wait (struct pex_obj *, pid_t, int *, struct pex_time *);
|
||||
|
||||
#ifdef HAVE_WAIT4
|
||||
|
||||
static pid_t
|
||||
pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
|
||||
struct pex_time *time)
|
||||
{
|
||||
int pid;
|
||||
int pdes[2];
|
||||
int out;
|
||||
int input_desc, output_desc;
|
||||
int flags;
|
||||
pid_t ret;
|
||||
struct rusage r;
|
||||
|
||||
#ifdef HAVE_WAITPID
|
||||
if (time == NULL)
|
||||
return waitpid (pid, status, 0);
|
||||
#endif
|
||||
|
||||
ret = wait4 (pid, status, 0, &r);
|
||||
|
||||
if (time != NULL)
|
||||
{
|
||||
time->user_seconds = r.ru_utime.tv_sec;
|
||||
time->user_microseconds= r.ru_utime.tv_usec;
|
||||
time->system_seconds = r.ru_stime.tv_sec;
|
||||
time->system_microseconds= r.ru_stime.tv_usec;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* ! defined (HAVE_WAIT4) */
|
||||
|
||||
#ifdef HAVE_WAITPID
|
||||
|
||||
#ifndef HAVE_GETRUSAGE
|
||||
|
||||
static pid_t
|
||||
pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
|
||||
struct pex_time *time)
|
||||
{
|
||||
if (time != NULL)
|
||||
memset (time, 0, sizeof (struct pex_time));
|
||||
return waitpid (pid, status, 0);
|
||||
}
|
||||
|
||||
#else /* defined (HAVE_GETRUSAGE) */
|
||||
|
||||
static pid_t
|
||||
pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
|
||||
struct pex_time *time)
|
||||
{
|
||||
struct rusage r1, r2;
|
||||
pid_t ret;
|
||||
|
||||
if (time == NULL)
|
||||
return waitpid (pid, status, 0);
|
||||
|
||||
getrusage (RUSAGE_CHILDREN, &r1);
|
||||
|
||||
ret = waitpid (pid, status, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
getrusage (RUSAGE_CHILDREN, &r2);
|
||||
|
||||
time->user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
|
||||
time->user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
|
||||
if (r2.ru_utime.tv_usec < r1.ru_utime.tv_usec)
|
||||
{
|
||||
--time->user_seconds;
|
||||
time->user_microseconds += 1000000;
|
||||
}
|
||||
|
||||
time->system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
|
||||
time->system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
|
||||
if (r2.ru_stime.tv_usec < r1.ru_stime.tv_usec)
|
||||
{
|
||||
--time->system_seconds;
|
||||
time->system_microseconds += 1000000;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* defined (HAVE_GETRUSAGE) */
|
||||
|
||||
#else /* ! defined (HAVE_WAITPID) */
|
||||
|
||||
struct status_list
|
||||
{
|
||||
struct status_list *next;
|
||||
pid_t pid;
|
||||
int status;
|
||||
struct pex_time time;
|
||||
};
|
||||
|
||||
static pid_t
|
||||
pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
|
||||
{
|
||||
struct status_list **pp;
|
||||
|
||||
for (pp = (struct status_list **) &obj->sysdep;
|
||||
*pp != NULL;
|
||||
pp = &(*pp)->next)
|
||||
{
|
||||
if ((*pp)->pid == pid)
|
||||
{
|
||||
struct status_list *p;
|
||||
|
||||
p = *pp;
|
||||
*status = p->status;
|
||||
if (time != NULL)
|
||||
*time = p->time;
|
||||
*pp = p->next;
|
||||
free (p);
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
pid_t cpid;
|
||||
struct status_list *psl;
|
||||
struct pex_time pt;
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
struct rusage r1, r2;
|
||||
#endif
|
||||
|
||||
if (time != NULL)
|
||||
{
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
getrusage (RUSAGE_CHILDREN, &r1);
|
||||
#else
|
||||
memset (&pt, 0, sizeof (struct pex_time));
|
||||
#endif
|
||||
}
|
||||
|
||||
cpid = wait (status);
|
||||
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
if (time != NULL && cpid >= 0)
|
||||
{
|
||||
getrusage (RUSAGE_CHILDREN, &r2);
|
||||
|
||||
pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
|
||||
pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
|
||||
if (pt.user_microseconds < 0)
|
||||
{
|
||||
--pt.user_seconds;
|
||||
pt.user_microseconds += 1000000;
|
||||
}
|
||||
|
||||
pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
|
||||
pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
|
||||
if (pt.system_microseconds < 0)
|
||||
{
|
||||
--pt.system_seconds;
|
||||
pt.system_microseconds += 1000000;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cpid < 0 || cpid == pid)
|
||||
{
|
||||
if (time != NULL)
|
||||
*time = pt;
|
||||
return cpid;
|
||||
}
|
||||
|
||||
psl = xmalloc (sizeof (struct status_list));
|
||||
psl->pid = cpid;
|
||||
psl->status = *status;
|
||||
if (time != NULL)
|
||||
psl->time = pt;
|
||||
psl->next = (struct status_list *) obj->sysdep;
|
||||
obj->sysdep = (void *) psl;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ! defined (HAVE_WAITPID) */
|
||||
#endif /* ! defined (HAVE_WAIT4) */
|
||||
|
||||
static void pex_child_error (struct pex_obj *, const char *, const char *, int)
|
||||
ATTRIBUTE_NORETURN;
|
||||
static int pex_unix_open_read (struct pex_obj *, const char *, int);
|
||||
static int pex_unix_open_write (struct pex_obj *, const char *, int);
|
||||
static long pex_unix_exec_child (struct pex_obj *, int, const char *,
|
||||
char * const *, int, int, int,
|
||||
const char **, int *);
|
||||
static int pex_unix_close (struct pex_obj *, int);
|
||||
static int pex_unix_wait (struct pex_obj *, long, int *, struct pex_time *,
|
||||
int, const char **, int *);
|
||||
static int pex_unix_pipe (struct pex_obj *, int *, int);
|
||||
static FILE *pex_unix_fdopenr (struct pex_obj *, int, int);
|
||||
static void pex_unix_cleanup (struct pex_obj *);
|
||||
|
||||
/* The list of functions we pass to the common routines. */
|
||||
|
||||
const struct pex_funcs funcs =
|
||||
{
|
||||
pex_unix_open_read,
|
||||
pex_unix_open_write,
|
||||
pex_unix_exec_child,
|
||||
pex_unix_close,
|
||||
pex_unix_wait,
|
||||
pex_unix_pipe,
|
||||
pex_unix_fdopenr,
|
||||
pex_unix_cleanup
|
||||
};
|
||||
|
||||
/* Return a newly initialized pex_obj structure. */
|
||||
|
||||
struct pex_obj *
|
||||
pex_init (int flags, const char *pname, const char *tempbase)
|
||||
{
|
||||
return pex_init_common (flags, pname, tempbase, &funcs);
|
||||
}
|
||||
|
||||
/* Open a file for reading. */
|
||||
|
||||
static int
|
||||
pex_unix_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
|
||||
int binary ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return open (name, O_RDONLY);
|
||||
}
|
||||
|
||||
/* Open a file for writing. */
|
||||
|
||||
static int
|
||||
pex_unix_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
|
||||
int binary ATTRIBUTE_UNUSED)
|
||||
{
|
||||
/* Note that we can't use O_EXCL here because gcc may have already
|
||||
created the temporary file via make_temp_file. */
|
||||
return open (name, O_WRONLY | O_CREAT | O_TRUNC, PUBLIC_MODE);
|
||||
}
|
||||
|
||||
/* Close a file. */
|
||||
|
||||
static int
|
||||
pex_unix_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
|
||||
{
|
||||
return close (fd);
|
||||
}
|
||||
|
||||
/* Report an error from a child process. We don't use stdio routines,
|
||||
because we might be here due to a vfork call. */
|
||||
|
||||
static void
|
||||
pex_child_error (struct pex_obj *obj, const char *executable,
|
||||
const char *errmsg, int err)
|
||||
{
|
||||
#define writeerr(s) write (STDERR_FILE_NO, s, strlen (s))
|
||||
writeerr (obj->pname);
|
||||
writeerr (": error trying to exec '");
|
||||
writeerr (executable);
|
||||
writeerr ("': ");
|
||||
writeerr (errmsg);
|
||||
writeerr (": ");
|
||||
writeerr (xstrerror (err));
|
||||
writeerr ("\n");
|
||||
_exit (-1);
|
||||
}
|
||||
|
||||
/* Execute a child. */
|
||||
|
||||
static long
|
||||
pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable,
|
||||
char * const * argv, int in, int out, int errdes,
|
||||
const char **errmsg, int *err)
|
||||
{
|
||||
pid_t pid;
|
||||
/* We declare these to be volatile to avoid warnings from gcc about
|
||||
them being clobbered by vfork. */
|
||||
volatile int retries, sleep_interval;
|
||||
/* Pipe waiting from last process, to be used as input for the next one.
|
||||
Value is STDIN_FILE_NO if no pipe is waiting
|
||||
(i.e. the next command is the first of a group). */
|
||||
static int last_pipe_input;
|
||||
volatile int sleep_interval;
|
||||
volatile int retries;
|
||||
|
||||
flags = flagsarg;
|
||||
|
||||
/* If this is the first process, initialize. */
|
||||
if (flags & PEXECUTE_FIRST)
|
||||
last_pipe_input = STDIN_FILE_NO;
|
||||
|
||||
input_desc = last_pipe_input;
|
||||
|
||||
/* If this isn't the last process, make a pipe for its output,
|
||||
and record it as waiting to be the input to the next process. */
|
||||
if (! (flags & PEXECUTE_LAST))
|
||||
{
|
||||
if (pipe (pdes) < 0)
|
||||
{
|
||||
*errmsg_fmt = "pipe";
|
||||
*errmsg_arg = NULL;
|
||||
return -1;
|
||||
}
|
||||
out = pdes[WRITE_PORT];
|
||||
last_pipe_input = pdes[READ_PORT];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Last process. */
|
||||
out = STDOUT_FILE_NO;
|
||||
last_pipe_input = STDIN_FILE_NO;
|
||||
}
|
||||
|
||||
output_desc = out;
|
||||
|
||||
/* Fork a subprocess; wait and retry if it fails. */
|
||||
sleep_interval = 1;
|
||||
pid = -1;
|
||||
for (retries = 0; retries < 4; retries++)
|
||||
for (retries = 0; retries < 4; ++retries)
|
||||
{
|
||||
pid = vfork ();
|
||||
if (pid >= 0)
|
||||
@ -142,66 +375,139 @@ pexecute (const char *program, char * const *argv, const char *this_pname,
|
||||
switch (pid)
|
||||
{
|
||||
case -1:
|
||||
*errmsg_fmt = "fork";
|
||||
*errmsg_arg = NULL;
|
||||
*err = errno;
|
||||
*errmsg = VFORK_STRING;
|
||||
return -1;
|
||||
|
||||
case 0: /* child */
|
||||
/* Move the input and output pipes into place, if necessary. */
|
||||
if (input_desc != STDIN_FILE_NO)
|
||||
case 0:
|
||||
/* Child process. */
|
||||
if (in != STDIN_FILE_NO)
|
||||
{
|
||||
close (STDIN_FILE_NO);
|
||||
dup (input_desc);
|
||||
close (input_desc);
|
||||
if (dup2 (in, STDIN_FILE_NO) < 0)
|
||||
pex_child_error (obj, executable, "dup2", errno);
|
||||
if (close (in) < 0)
|
||||
pex_child_error (obj, executable, "close", errno);
|
||||
}
|
||||
if (output_desc != STDOUT_FILE_NO)
|
||||
if (out != STDOUT_FILE_NO)
|
||||
{
|
||||
close (STDOUT_FILE_NO);
|
||||
dup (output_desc);
|
||||
close (output_desc);
|
||||
if (dup2 (out, STDOUT_FILE_NO) < 0)
|
||||
pex_child_error (obj, executable, "dup2", errno);
|
||||
if (close (out) < 0)
|
||||
pex_child_error (obj, executable, "close", errno);
|
||||
}
|
||||
if (errdes != STDERR_FILE_NO)
|
||||
{
|
||||
if (dup2 (errdes, STDERR_FILE_NO) < 0)
|
||||
pex_child_error (obj, executable, "dup2", errno);
|
||||
if (close (errdes) < 0)
|
||||
pex_child_error (obj, executable, "close", errno);
|
||||
}
|
||||
if ((flags & PEX_STDERR_TO_STDOUT) != 0)
|
||||
{
|
||||
if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0)
|
||||
pex_child_error (obj, executable, "dup2", errno);
|
||||
}
|
||||
if ((flags & PEX_SEARCH) != 0)
|
||||
{
|
||||
execvp (executable, argv);
|
||||
pex_child_error (obj, executable, "execvp", errno);
|
||||
}
|
||||
|
||||
/* Close the parent's descs that aren't wanted here. */
|
||||
if (last_pipe_input != STDIN_FILE_NO)
|
||||
close (last_pipe_input);
|
||||
|
||||
/* Exec the program. */
|
||||
if (flags & PEXECUTE_SEARCH)
|
||||
execvp (program, argv);
|
||||
else
|
||||
execv (program, argv);
|
||||
{
|
||||
execv (executable, argv);
|
||||
pex_child_error (obj, executable, "execv", errno);
|
||||
}
|
||||
|
||||
/* We don't want to call fprintf after vfork. */
|
||||
#define writeerr(s) write (STDERR_FILE_NO, s, strlen (s))
|
||||
writeerr (this_pname);
|
||||
writeerr (": ");
|
||||
writeerr ("installation problem, cannot exec '");
|
||||
writeerr (program);
|
||||
writeerr ("': ");
|
||||
writeerr (xstrerror (errno));
|
||||
writeerr ("\n");
|
||||
_exit (-1);
|
||||
/* NOTREACHED */
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
default:
|
||||
/* In the parent, after forking.
|
||||
Close the descriptors that we made for this child. */
|
||||
if (input_desc != STDIN_FILE_NO)
|
||||
close (input_desc);
|
||||
if (output_desc != STDOUT_FILE_NO)
|
||||
close (output_desc);
|
||||
/* Parent process. */
|
||||
if (in != STDIN_FILE_NO)
|
||||
{
|
||||
if (close (in) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (out != STDOUT_FILE_NO)
|
||||
{
|
||||
if (close (out) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (errdes != STDERR_FILE_NO)
|
||||
{
|
||||
if (close (errdes) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return child's process number. */
|
||||
return pid;
|
||||
return (long) pid;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pwait (int pid, int *status, int flags ATTRIBUTE_UNUSED)
|
||||
/* Wait for a child process to complete. */
|
||||
|
||||
static int
|
||||
pex_unix_wait (struct pex_obj *obj, long pid, int *status,
|
||||
struct pex_time *time, int done, const char **errmsg,
|
||||
int *err)
|
||||
{
|
||||
/* ??? Here's an opportunity to canonicalize the values in STATUS.
|
||||
Needed? */
|
||||
pid = waitpid (pid, status, 0);
|
||||
return pid;
|
||||
/* If we are cleaning up when the caller didn't retrieve process
|
||||
status for some reason, encourage the process to go away. */
|
||||
if (done)
|
||||
kill (pid, SIGTERM);
|
||||
|
||||
if (pex_wait (obj, pid, status, time) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "wait";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a pipe. */
|
||||
|
||||
static int
|
||||
pex_unix_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
|
||||
int binary ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return pipe (p);
|
||||
}
|
||||
|
||||
/* Get a FILE pointer to read from a file descriptor. */
|
||||
|
||||
static FILE *
|
||||
pex_unix_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
|
||||
int binary ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return fdopen (fd, "r");
|
||||
}
|
||||
|
||||
static void
|
||||
pex_unix_cleanup (struct pex_obj *obj ATTRIBUTE_UNUSED)
|
||||
{
|
||||
#if !defined (HAVE_WAIT4) && !defined (HAVE_WAITPID)
|
||||
while (obj->sysdep != NULL)
|
||||
{
|
||||
struct status_list *this;
|
||||
struct status_list *next;
|
||||
|
||||
this = (struct status_list *) obj->sysdep;
|
||||
next = this->next;
|
||||
free (this);
|
||||
obj->sysdep = (void *) next;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "pex-common.h"
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
@ -35,6 +38,7 @@ Boston, MA 02111-1307, USA. */
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* mingw32 headers may not define the following. */
|
||||
|
||||
@ -53,27 +57,49 @@ Boston, MA 02111-1307, USA. */
|
||||
to remove the outermost set of double quotes from all arguments. */
|
||||
|
||||
static const char * const *
|
||||
fix_argv (char **argvec)
|
||||
fix_argv (char * const *argvec)
|
||||
{
|
||||
char **argv;
|
||||
int i;
|
||||
char * command0 = argvec[0];
|
||||
char *command0;
|
||||
|
||||
/* See whether we need to change anything. */
|
||||
for (command0 = argvec[0]; *command0 != '\0'; command0++)
|
||||
if (*command0 == '/')
|
||||
break;
|
||||
if (*command0 == '\0')
|
||||
{
|
||||
for (i = 1; argvec[i] != NULL; i++)
|
||||
if (strpbrk (argvec[i], "\" \t") != NULL)
|
||||
break;
|
||||
|
||||
if (argvec[i] == NULL)
|
||||
return (const char * const *) argvec;
|
||||
}
|
||||
|
||||
for (i = 0; argvec[i] != NULL; i++)
|
||||
;
|
||||
argv = xmalloc ((i + 1) * sizeof (char *));
|
||||
for (i = 0; argvec[i] != NULL; i++)
|
||||
argv[i] = xstrdup (argvec[i]);
|
||||
argv[i] = NULL;
|
||||
|
||||
/* Ensure that the executable pathname uses Win32 backslashes. This
|
||||
is not necessary on NT, but on W9x, forward slashes causes failure
|
||||
of spawn* and exec* functions (and probably any function that
|
||||
calls CreateProcess) *iff* the executable pathname (argvec[0]) is
|
||||
a quoted string. And quoting is necessary in case a pathname
|
||||
contains embedded white space. You can't win. */
|
||||
for (; *command0 != '\0'; command0++)
|
||||
is not necessary on NT, but on W9x, forward slashes causes
|
||||
failure of spawn* and exec* functions (and probably any function
|
||||
that calls CreateProcess) *iff* the executable pathname (argv[0])
|
||||
is a quoted string. And quoting is necessary in case a pathname
|
||||
contains embedded white space. You can't win. */
|
||||
for (command0 = argv[0]; *command0 != '\0'; command0++)
|
||||
if (*command0 == '/')
|
||||
*command0 = '\\';
|
||||
|
||||
for (i = 1; argvec[i] != 0; i++)
|
||||
|
||||
for (i = 1; argv[i] != 0; i++)
|
||||
{
|
||||
int len, j;
|
||||
char *temp, *newtemp;
|
||||
|
||||
temp = argvec[i];
|
||||
temp = argv[i];
|
||||
len = strlen (temp);
|
||||
for (j = 0; j < len; j++)
|
||||
{
|
||||
@ -90,17 +116,21 @@ fix_argv (char **argvec)
|
||||
}
|
||||
}
|
||||
|
||||
argvec[i] = temp;
|
||||
}
|
||||
if (argv[i] != temp)
|
||||
{
|
||||
free (argv[i]);
|
||||
argv[i] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; argvec[i] != 0; i++)
|
||||
for (i = 0; argv[i] != 0; i++)
|
||||
{
|
||||
if (strpbrk (argvec[i], " \t"))
|
||||
if (strpbrk (argv[i], " \t"))
|
||||
{
|
||||
int len, trailing_backslash;
|
||||
char *temp;
|
||||
|
||||
len = strlen (argvec[i]);
|
||||
len = strlen (argv[i]);
|
||||
trailing_backslash = 0;
|
||||
|
||||
/* There is an added complication when an arg with embedded white
|
||||
@ -111,8 +141,8 @@ fix_argv (char **argvec)
|
||||
We handle this case by escaping the trailing backslash, provided
|
||||
it was not escaped in the first place. */
|
||||
if (len > 1
|
||||
&& argvec[i][len-1] == '\\'
|
||||
&& argvec[i][len-2] != '\\')
|
||||
&& argv[i][len-1] == '\\'
|
||||
&& argv[i][len-2] != '\\')
|
||||
{
|
||||
trailing_backslash = 1;
|
||||
++len; /* to escape the final backslash. */
|
||||
@ -122,127 +152,289 @@ fix_argv (char **argvec)
|
||||
|
||||
temp = xmalloc (len + 1);
|
||||
temp[0] = '"';
|
||||
strcpy (temp + 1, argvec[i]);
|
||||
strcpy (temp + 1, argv[i]);
|
||||
if (trailing_backslash)
|
||||
temp[len-2] = '\\';
|
||||
temp[len-1] = '"';
|
||||
temp[len - 2] = '\\';
|
||||
temp[len - 1] = '"';
|
||||
temp[len] = '\0';
|
||||
|
||||
argvec[i] = temp;
|
||||
free (argv[i]);
|
||||
argv[i] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
return (const char * const *) argvec;
|
||||
return (const char * const *) argv;
|
||||
}
|
||||
|
||||
/* Win32 supports pipes */
|
||||
int
|
||||
pexecute (const char *program, char * const *argv,
|
||||
const char *this_pname ATTRIBUTE_UNUSED,
|
||||
const char *temp_base ATTRIBUTE_UNUSED,
|
||||
char **errmsg_fmt, char **errmsg_arg, int flags)
|
||||
static int pex_win32_open_read (struct pex_obj *, const char *, int);
|
||||
static int pex_win32_open_write (struct pex_obj *, const char *, int);
|
||||
static long pex_win32_exec_child (struct pex_obj *, int, const char *,
|
||||
char * const *, int, int, int,
|
||||
const char **, int *);
|
||||
static int pex_win32_close (struct pex_obj *, int);
|
||||
static int pex_win32_wait (struct pex_obj *, long, int *,
|
||||
struct pex_time *, int, const char **, int *);
|
||||
static int pex_win32_pipe (struct pex_obj *, int *, int);
|
||||
static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
|
||||
|
||||
/* The list of functions we pass to the common routines. */
|
||||
|
||||
const struct pex_funcs funcs =
|
||||
{
|
||||
int pid;
|
||||
int pdes[2];
|
||||
int org_stdin = -1;
|
||||
int org_stdout = -1;
|
||||
int input_desc, output_desc;
|
||||
pex_win32_open_read,
|
||||
pex_win32_open_write,
|
||||
pex_win32_exec_child,
|
||||
pex_win32_close,
|
||||
pex_win32_wait,
|
||||
pex_win32_pipe,
|
||||
pex_win32_fdopenr,
|
||||
NULL /* cleanup */
|
||||
};
|
||||
|
||||
/* Pipe waiting from last process, to be used as input for the next one.
|
||||
Value is STDIN_FILE_NO if no pipe is waiting
|
||||
(i.e. the next command is the first of a group). */
|
||||
static int last_pipe_input;
|
||||
/* Return a newly initialized pex_obj structure. */
|
||||
|
||||
/* If this is the first process, initialize. */
|
||||
if (flags & PEXECUTE_FIRST)
|
||||
last_pipe_input = STDIN_FILE_NO;
|
||||
struct pex_obj *
|
||||
pex_init (int flags, const char *pname, const char *tempbase)
|
||||
{
|
||||
return pex_init_common (flags, pname, tempbase, &funcs);
|
||||
}
|
||||
|
||||
input_desc = last_pipe_input;
|
||||
/* Open a file for reading. */
|
||||
|
||||
/* If this isn't the last process, make a pipe for its output,
|
||||
and record it as waiting to be the input to the next process. */
|
||||
if (! (flags & PEXECUTE_LAST))
|
||||
static int
|
||||
pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
|
||||
int binary)
|
||||
{
|
||||
return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
|
||||
}
|
||||
|
||||
/* Open a file for writing. */
|
||||
|
||||
static int
|
||||
pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
|
||||
int binary)
|
||||
{
|
||||
/* Note that we can't use O_EXCL here because gcc may have already
|
||||
created the temporary file via make_temp_file. */
|
||||
return _open (name,
|
||||
(_O_WRONLY | _O_CREAT | _O_TRUNC
|
||||
| (binary ? _O_BINARY : _O_TEXT)),
|
||||
_S_IREAD | _S_IWRITE);
|
||||
}
|
||||
|
||||
/* Close a file. */
|
||||
|
||||
static int
|
||||
pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
|
||||
{
|
||||
return _close (fd);
|
||||
}
|
||||
|
||||
/* Execute a child. */
|
||||
|
||||
static long
|
||||
pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
|
||||
const char *executable, char * const * argv,
|
||||
int in, int out, int errdes, const char **errmsg,
|
||||
int *err)
|
||||
{
|
||||
int org_in, org_out, org_errdes;
|
||||
long pid;
|
||||
|
||||
org_in = -1;
|
||||
org_out = -1;
|
||||
org_errdes = -1;
|
||||
|
||||
if (in != STDIN_FILE_NO)
|
||||
{
|
||||
if (_pipe (pdes, 256, O_BINARY) < 0)
|
||||
org_in = _dup (STDIN_FILE_NO);
|
||||
if (org_in < 0)
|
||||
{
|
||||
*errmsg_fmt = "pipe";
|
||||
*errmsg_arg = NULL;
|
||||
*err = errno;
|
||||
*errmsg = "_dup";
|
||||
return -1;
|
||||
}
|
||||
if (_dup2 (in, STDIN_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (in) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
output_desc = pdes[WRITE_PORT];
|
||||
last_pipe_input = pdes[READ_PORT];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Last process. */
|
||||
output_desc = STDOUT_FILE_NO;
|
||||
last_pipe_input = STDIN_FILE_NO;
|
||||
}
|
||||
|
||||
if (input_desc != STDIN_FILE_NO)
|
||||
if (out != STDOUT_FILE_NO)
|
||||
{
|
||||
org_stdin = dup (STDIN_FILE_NO);
|
||||
dup2 (input_desc, STDIN_FILE_NO);
|
||||
close (input_desc);
|
||||
org_out = _dup (STDOUT_FILE_NO);
|
||||
if (org_out < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup";
|
||||
return -1;
|
||||
}
|
||||
if (_dup2 (out, STDOUT_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (out) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (output_desc != STDOUT_FILE_NO)
|
||||
if (errdes != STDERR_FILE_NO
|
||||
|| (flags & PEX_STDERR_TO_STDOUT) != 0)
|
||||
{
|
||||
org_stdout = dup (STDOUT_FILE_NO);
|
||||
dup2 (output_desc, STDOUT_FILE_NO);
|
||||
close (output_desc);
|
||||
org_errdes = _dup (STDERR_FILE_NO);
|
||||
if (org_errdes < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup";
|
||||
return -1;
|
||||
}
|
||||
if (_dup2 ((flags & PEX_STDERR_TO_STDOUT) != 0 ? STDOUT_FILE_NO : errdes,
|
||||
STDERR_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (errdes != STDERR_FILE_NO)
|
||||
{
|
||||
if (_close (errdes) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pid = (flags & PEXECUTE_SEARCH ? _spawnvp : _spawnv)
|
||||
(_P_NOWAIT, program, fix_argv(argv));
|
||||
|
||||
if (input_desc != STDIN_FILE_NO)
|
||||
{
|
||||
dup2 (org_stdin, STDIN_FILE_NO);
|
||||
close (org_stdin);
|
||||
}
|
||||
|
||||
if (output_desc != STDOUT_FILE_NO)
|
||||
{
|
||||
dup2 (org_stdout, STDOUT_FILE_NO);
|
||||
close (org_stdout);
|
||||
}
|
||||
pid = (((flags & PEX_SEARCH) != 0 ? _spawnvp : _spawnv)
|
||||
(_P_NOWAIT, executable, fix_argv (argv)));
|
||||
|
||||
if (pid == -1)
|
||||
{
|
||||
*errmsg_fmt = install_error_msg;
|
||||
*errmsg_arg = (char*) program;
|
||||
return -1;
|
||||
*err = errno;
|
||||
*errmsg = ((flags & PEX_SEARCH) != 0) ? "_spawnvp" : "_spawnv";
|
||||
}
|
||||
|
||||
if (in != STDIN_FILE_NO)
|
||||
{
|
||||
if (_dup2 (org_in, STDIN_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (org_in) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (out != STDOUT_FILE_NO)
|
||||
{
|
||||
if (_dup2 (org_out, STDOUT_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (org_out) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errdes != STDERR_FILE_NO
|
||||
|| (flags & PEX_STDERR_TO_STDOUT) != 0)
|
||||
{
|
||||
if (_dup2 (org_errdes, STDERR_FILE_NO) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_dup2";
|
||||
return -1;
|
||||
}
|
||||
if (_close (org_errdes) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_close";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/* MS CRTDLL doesn't return enough information in status to decide if the
|
||||
child exited due to a signal or not, rather it simply returns an
|
||||
integer with the exit code of the child; eg., if the child exited with
|
||||
an abort() call and didn't have a handler for SIGABRT, it simply returns
|
||||
with status = 3. We fix the status code to conform to the usual WIF*
|
||||
macros. Note that WIFSIGNALED will never be true under CRTDLL. */
|
||||
/* Wait for a child process to complete. MS CRTDLL doesn't return
|
||||
enough information in status to decide if the child exited due to a
|
||||
signal or not, rather it simply returns an integer with the exit
|
||||
code of the child; eg., if the child exited with an abort() call
|
||||
and didn't have a handler for SIGABRT, it simply returns with
|
||||
status == 3. We fix the status code to conform to the usual WIF*
|
||||
macros. Note that WIFSIGNALED will never be true under CRTDLL. */
|
||||
|
||||
int
|
||||
pwait (int pid, int *status, int flags ATTRIBUTE_UNUSED)
|
||||
static int
|
||||
pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, long pid,
|
||||
int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
|
||||
const char **errmsg, int *err)
|
||||
{
|
||||
int termstat;
|
||||
|
||||
pid = _cwait (&termstat, pid, WAIT_CHILD);
|
||||
if (time != NULL)
|
||||
memset (time, 0, sizeof *time);
|
||||
|
||||
/* ??? Here's an opportunity to canonicalize the values in STATUS.
|
||||
Needed? */
|
||||
/* FIXME: If done is non-zero, we should probably try to kill the
|
||||
process. */
|
||||
|
||||
if (_cwait (&termstat, pid, WAIT_CHILD) < 0)
|
||||
{
|
||||
*err = errno;
|
||||
*errmsg = "_cwait";
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* cwait returns the child process exit code in termstat. A value
|
||||
of 3 indicates that the child caught a signal, but not which one.
|
||||
Since only SIGABRT, SIGFPE and SIGINT do anything, we report
|
||||
SIGABRT. */
|
||||
|
||||
/* cwait returns the child process exit code in termstat.
|
||||
A value of 3 indicates that the child caught a signal, but not
|
||||
which one. Since only SIGABRT, SIGFPE and SIGINT do anything, we
|
||||
report SIGABRT. */
|
||||
if (termstat == 3)
|
||||
*status = SIGABRT;
|
||||
else
|
||||
*status = (((termstat) & 0xff) << 8);
|
||||
*status = ((termstat & 0xff) << 8);
|
||||
|
||||
return pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a pipe. */
|
||||
|
||||
static int
|
||||
pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
|
||||
int binary)
|
||||
{
|
||||
return _pipe (p, 256, binary ? _O_BINARY : _O_TEXT);
|
||||
}
|
||||
|
||||
/* Get a FILE pointer to read from a file descriptor. */
|
||||
|
||||
static FILE *
|
||||
pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
|
||||
int binary)
|
||||
{
|
||||
return fdopen (fd, binary ? "rb" : "r");
|
||||
}
|
||||
|
121
libiberty/pexecute.c
Normal file
121
libiberty/pexecute.c
Normal file
@ -0,0 +1,121 @@
|
||||
/* Utilities to execute a program in a subprocess (possibly linked by pipes
|
||||
with other subprocesses), and wait for it.
|
||||
Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the libiberty library.
|
||||
Libiberty is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
Libiberty is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with libiberty; see the file COPYING.LIB. If not,
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* pexecute is an old routine. This implementation uses the newer
|
||||
pex_init/pex_run/pex_get_status/pex_free routines. Don't use
|
||||
pexecute in new code. Use the newer routines instead. */
|
||||
|
||||
#include "config.h"
|
||||
#include "libiberty.h"
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/* We only permit a single pexecute chain to execute at a time. This
|
||||
was always true anyhow, though it wasn't documented. */
|
||||
|
||||
static struct pex_obj *pex;
|
||||
static int idx;
|
||||
|
||||
int
|
||||
pexecute (const char *program, char * const *argv, const char *pname,
|
||||
const char *temp_base, char **errmsg_fmt, char **errmsg_arg,
|
||||
int flags)
|
||||
{
|
||||
const char *errmsg;
|
||||
int err;
|
||||
|
||||
if ((flags & PEXECUTE_FIRST) != 0)
|
||||
{
|
||||
if (pex != NULL)
|
||||
{
|
||||
*errmsg_fmt = "pexecute already in progress";
|
||||
*errmsg_arg = NULL;
|
||||
return -1;
|
||||
}
|
||||
pex = pex_init (PEX_USE_PIPES, pname, temp_base);
|
||||
idx = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pex == NULL)
|
||||
{
|
||||
*errmsg_fmt = "pexecute not in progress";
|
||||
*errmsg_arg = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
errmsg = pex_run (pex,
|
||||
(((flags & PEXECUTE_LAST) != 0 ? PEX_LAST : 0)
|
||||
| ((flags & PEXECUTE_SEARCH) != 0 ? PEX_SEARCH : 0)),
|
||||
program, argv, NULL, NULL, &err);
|
||||
if (errmsg != NULL)
|
||||
{
|
||||
*errmsg_fmt = (char *) errmsg;
|
||||
*errmsg_arg = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Instead of a PID, we just return a one-based index into the
|
||||
status values. We avoid zero just because the old pexecute would
|
||||
never return it. */
|
||||
return ++idx;
|
||||
}
|
||||
|
||||
int
|
||||
pwait (int pid, int *status, int flags ATTRIBUTE_UNUSED)
|
||||
{
|
||||
/* The PID returned by pexecute is one-based. */
|
||||
--pid;
|
||||
|
||||
if (pex == NULL || pid < 0 || pid >= idx)
|
||||
return -1;
|
||||
|
||||
if (pid == 0 && idx == 1)
|
||||
{
|
||||
if (!pex_get_status (pex, 1, status))
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int *vector;
|
||||
|
||||
vector = xmalloc (idx * sizeof (int));
|
||||
if (!pex_get_status (pex, idx, vector))
|
||||
return -1;
|
||||
*status = vector[pid];
|
||||
free (vector);
|
||||
}
|
||||
|
||||
/* Assume that we are done after the caller has retrieved the last
|
||||
exit status. The original implementation did not require that
|
||||
the exit statuses be retrieved in order, but this implementation
|
||||
does. */
|
||||
if (pid + 1 == idx)
|
||||
{
|
||||
pex_free (pex);
|
||||
pex = NULL;
|
||||
idx = 0;
|
||||
}
|
||||
|
||||
return pid + 1;
|
||||
}
|
@ -1,63 +1,185 @@
|
||||
@deftypefn Extension struct pex_obj *pex_init (int @var{flags}, const char *@var{pname}, const char *@var{tempbase})
|
||||
|
||||
Prepare to execute one or more programs, with standard output of each
|
||||
program fed to standard input of the next. This is a system
|
||||
independent interface to execute a pipeline.
|
||||
|
||||
@var{flags} is a bitwise combination of the following:
|
||||
|
||||
@table @code
|
||||
|
||||
@vindex PEX_RECORD_TIMES
|
||||
@item PEX_RECORD_TIMES
|
||||
Record subprocess times if possible.
|
||||
|
||||
@vindex PEX_USE_PIPES
|
||||
@item PEX_USE_PIPES
|
||||
Use pipes for communication between processes, if possible.
|
||||
|
||||
@vindex PEX_SAVE_TEMPS
|
||||
@item PEX_SAVE_TEMPS
|
||||
Don't delete temporary files used for communication between
|
||||
processes.
|
||||
|
||||
@end table
|
||||
|
||||
@var{pname} is the name of program to be executed, used in error
|
||||
messages. @var{tempbase} is a base name to use for any required
|
||||
temporary files; it may be @code{NULL} to use a randomly chosen name.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension const char *pex_run (struct pex_obj *@var{obj}, int @var{flags}, const char *@var{executable}, char * const *@var{argv}, const char *@var{outname}, const char *@var{errname}, int *@var{err})
|
||||
|
||||
Execute one program in a pipeline. On success this returns
|
||||
@code{NULL}. On failure it returns an error message, a statically
|
||||
allocated string.
|
||||
|
||||
@var{obj} is returned by a previous call to @code{pex_init}.
|
||||
|
||||
@var{flags} is a bitwise combination of the following:
|
||||
|
||||
@table @code
|
||||
|
||||
@vindex PEX_LAST
|
||||
@item PEX_LAST
|
||||
This must be set on the last program in the pipeline. In particular,
|
||||
it should be set when executing a single program. The standard output
|
||||
of the program will be sent to @var{outname}, or, if @var{outname} is
|
||||
@code{NULL}, to the standard output of the calling program. This
|
||||
should not be set if you want to call @code{pex_read_output}
|
||||
(described below). After a call to @code{pex_run} with this bit set,
|
||||
@var{pex_run} may no longer be called with the same @var{obj}.
|
||||
|
||||
@vindex PEX_SEARCH
|
||||
@item PEX_SEARCH
|
||||
Search for the program using the user's executable search path.
|
||||
|
||||
@vindex PEX_SUFFIX
|
||||
@item PEX_SUFFIX
|
||||
@var{outname} is a suffix. See the description of @var{outname},
|
||||
below.
|
||||
|
||||
@vindex PEX_STDERR_TO_STDOUT
|
||||
@item PEX_STDERR_TO_STDOUT
|
||||
Send the program's standard error to standard output, if possible.
|
||||
|
||||
@vindex PEX_BINARY_INPUT
|
||||
@vindex PEX_BINARY_OUTPUT
|
||||
@item PEX_BINARY_INPUT
|
||||
@itemx PEX_BINARY_OUTPUT
|
||||
The standard input (output) of the program should be read (written) in
|
||||
binary mode rather than text mode. These flags are ignored on systems
|
||||
which do not distinguish binary mode and text mode, such as Unix. For
|
||||
proper behavior these flags should match appropriately--a call to
|
||||
@code{pex_run} using @code{PEX_BINARY_OUTPUT} should be followed by a
|
||||
call using @code{PEX_BINARY_INPUT}.
|
||||
@end table
|
||||
|
||||
@var{executable} is the program to execute. @var{argv} is the set of
|
||||
arguments to pass to the program; normally @code{@var{argv}[0]} will
|
||||
be a copy of @var{executable}.
|
||||
|
||||
@var{outname} is used to set the name of the file to use for standard
|
||||
output. There are two cases in which no output file will be used: 1)
|
||||
if @code{PEX_LAST} is not set in @var{flags}, and @code{PEX_USE_PIPES}
|
||||
was set in the call to @code{pex_init}, and the system supports pipes;
|
||||
2) if @code{PEX_LAST} is set in @var{flags}, and @var{outname} is
|
||||
@code{NULL}. Otherwise the code will use a file to hold standard
|
||||
output. If @code{PEX_LAST} is not set, this file is considered to be
|
||||
a temporary file, and it will be removed when no longer needed, unless
|
||||
@code{PEX_SAVE_TEMPS} was set in the call to @code{pex_init}.
|
||||
|
||||
There are two cases to consider when setting the name of the file to
|
||||
hold standard output.
|
||||
|
||||
First case: @code{PEX_SUFFIX} is set in @var{flags}. In this case
|
||||
@var{outname} may not be @code{NULL}. If the @var{tempbase} parameter
|
||||
to @code{pex_init} was not @code{NULL}, then the output file name is
|
||||
the concatenation of @var{tempbase} and @var{outname}. If
|
||||
@var{tempbase} was @code{NULL}, then the output file name is a random
|
||||
file name ending in @var{outname}.
|
||||
|
||||
Second case: @code{PEX_SUFFIX} was not set in @var{flags}. In this
|
||||
case, if @var{outname} is not @code{NULL}, it is used as the output
|
||||
file name. If @var{outname} is @code{NULL}, and @var{tempbase} was
|
||||
not NULL, the output file name is randomly chosen using
|
||||
@var{tempbase}. Otherwise the output file name is chosen completely
|
||||
at random.
|
||||
|
||||
@var{errname} is the file name to use for standard error output. If
|
||||
it is @code{NULL}, standard error is the same as the caller.
|
||||
Otherwise, standard error is written to the named file.
|
||||
|
||||
On an error return, the code sets @code{*@var{err}} to an @code{errno}
|
||||
value, or to 0 if there is no relevant @code{errno}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension FILE * pex_read_output (struct pex_obj *@var{obj}, int @var{binary})
|
||||
|
||||
Returns a @code{FILE} pointer which may be used to read the standard
|
||||
output of the last program in the pipeline. When this is used,
|
||||
@code{PEX_LAST} should not be used in a call to @code{pex_run}. After
|
||||
this is called, @code{pex_run} may no longer be called with the same
|
||||
@var{obj}. @var{binary} should be non-zero if the file should be
|
||||
opened in binary mode. Don't call @code{fclose} on the returned file;
|
||||
it will be closed by @code{pex_free}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension int pex_get_status (struct pex_obj *@var{obj}, int @var{count}, int *@var{vector})
|
||||
|
||||
Returns the exit status of all programs run using @var{obj}.
|
||||
@var{count} is the number of results expected. The results will be
|
||||
placed into @var{vector}. The results are in the order of the calls
|
||||
to @code{pex_run}. Returns 0 on error, 1 on success.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension int pex_get_times (struct pex_obj *@var{obj}, int @var{count}, struct pex_time *@var{vector})
|
||||
|
||||
Returns the process execution times of all programs run using
|
||||
@var{obj}. @var{count} is the number of results expected. The
|
||||
results will be placed into @var{vector}. The results are in the
|
||||
order of the calls to @code{pex_run}. Returns 0 on error, 1 on
|
||||
success.
|
||||
|
||||
@code{struct pex_time} has the following fields: @code{user_seconds},
|
||||
@code{user_microseconds}, @code{system_seconds},
|
||||
@code{system_microseconds}. On systems which do not support reporting
|
||||
process times, all the fields will be set to @code{0}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension void pex_free (struct pex_obj @var{obj})
|
||||
|
||||
Clean up and free all data associated with @var{obj}.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension const char *pex_one (int @var{flags}, const char *@var{executable}, char * const *@var{argv}, const char *@var{pname}, const char *@var{outname}, const char *@var{errname}, int *@var{status}, int *@var{err})
|
||||
|
||||
An interface to @code{pex_init} to permit the easy execution of a
|
||||
single program. The return value and most of the parameters are as
|
||||
for a call to @code{pex_run}. @var{flags} is restricted to a
|
||||
combination of @code{PEX_SEARCH}, @code{PEX_STDERR_TO_STDOUT}, and
|
||||
@code{PEX_BINARY_OUTPUT}. @var{outname} is interpreted as if
|
||||
@code{PEX_LAST} were set. On a successful return, *@var{status} will
|
||||
be set to the exit status of the program.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension int pexecute (const char *@var{program}, char * const *@var{argv}, const char *@var{this_pname}, const char *@var{temp_base}, char **@var{errmsg_fmt}, char **@var{errmsg_arg}, int flags)
|
||||
|
||||
Executes a program.
|
||||
|
||||
@var{program} and @var{argv} are the arguments to
|
||||
@code{execv}/@code{execvp}.
|
||||
|
||||
@var{this_pname} is name of the calling program (i.e., @code{argv[0]}).
|
||||
|
||||
@var{temp_base} is the path name, sans suffix, of a temporary file to
|
||||
use if needed. This is currently only needed for MS-DOS ports that
|
||||
don't use @code{go32} (do any still exist?). Ports that don't need it
|
||||
can pass @code{NULL}.
|
||||
|
||||
(@code{@var{flags} & PEXECUTE_SEARCH}) is non-zero if @env{PATH}
|
||||
should be searched (??? It's not clear that GCC passes this flag
|
||||
correctly). (@code{@var{flags} & PEXECUTE_FIRST}) is nonzero for the
|
||||
first process in chain. (@code{@var{flags} & PEXECUTE_FIRST}) is
|
||||
nonzero for the last process in chain. The first/last flags could be
|
||||
simplified to only mark the last of a chain of processes but that
|
||||
requires the caller to always mark the last one (and not give up
|
||||
early if some error occurs). It's more robust to require the caller
|
||||
to mark both ends of the chain.
|
||||
|
||||
The result is the pid on systems like Unix where we
|
||||
@code{fork}/@code{exec} and on systems like WIN32 and OS/2 where we
|
||||
use @code{spawn}. It is up to the caller to wait for the child.
|
||||
|
||||
The result is the @code{WEXITSTATUS} on systems like MS-DOS where we
|
||||
@code{spawn} and wait for the child here.
|
||||
|
||||
Upon failure, @var{errmsg_fmt} and @var{errmsg_arg} are set to the
|
||||
text of the error message with an optional argument (if not needed,
|
||||
@var{errmsg_arg} is set to @code{NULL}), and @minus{}1 is returned.
|
||||
@code{errno} is available to the caller to use.
|
||||
This is the old interface to execute one or more programs. It is
|
||||
still supported for compatibility purposes, but is no longer
|
||||
documented.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags})
|
||||
|
||||
Waits for a program started by @code{pexecute} to finish.
|
||||
|
||||
@var{pid} is the process id of the task to wait for. @var{status} is
|
||||
the `status' argument to wait. @var{flags} is currently unused
|
||||
(allows future enhancement without breaking upward compatibility).
|
||||
Pass 0 for now.
|
||||
|
||||
The result is the pid of the child reaped, or -1 for failure
|
||||
(@code{errno} says why).
|
||||
|
||||
On systems that don't support waiting for a particular child,
|
||||
@var{pid} is ignored. On systems like MS-DOS that don't really
|
||||
multitask @code{pwait} is just a mechanism to provide a consistent
|
||||
interface for the caller.
|
||||
Another part of the old execution interface.
|
||||
|
||||
@end deftypefn
|
||||
|
||||
@undocumented pfinish
|
||||
|
||||
pfinish: finish generation of script
|
||||
|
||||
pfinish is necessary for systems like MPW where a script is generated
|
||||
that runs the requested programs.
|
||||
|
@ -42,17 +42,28 @@ INCDIR=$(srcdir)/../$(MULTISRCTOP)../include
|
||||
|
||||
all:
|
||||
|
||||
# CHECK is set to "really_check" or the empty string by configure.
|
||||
check: @CHECK@
|
||||
|
||||
really-check: check-cplus-dem check-pexecute
|
||||
|
||||
# Run some tests of the demangler.
|
||||
check-cplus-dem: test-demangle $(srcdir)/demangle-expected
|
||||
./test-demangle < $(srcdir)/demangle-expected
|
||||
|
||||
# Check the pexecute code.
|
||||
check-pexecute: test-pexecute
|
||||
./test-pexecute
|
||||
|
||||
TEST_COMPILE = $(CC) @DEFS@ $(LIBCFLAGS) -I.. -I$(INCDIR) $(HDEFINES)
|
||||
test-demangle: $(srcdir)/test-demangle.c ../libiberty.a
|
||||
$(TEST_COMPILE) -o test-demangle \
|
||||
$(srcdir)/test-demangle.c ../libiberty.a
|
||||
|
||||
test-pexecute: $(srcdir)/test-pexecute.c ../libiberty.a
|
||||
$(TEST_COMPILE) -DHAVE_CONFIG_H -I.. -o test-pexecute \
|
||||
$(srcdir)/test-pexecute.c ../libiberty.a
|
||||
|
||||
# Standard (either GNU or Cygnus) rules we don't use.
|
||||
info install-info clean-info dvi install etags tags installcheck:
|
||||
|
||||
|
521
libiberty/testsuite/test-pexecute.c
Normal file
521
libiberty/testsuite/test-pexecute.c
Normal file
@ -0,0 +1,521 @@
|
||||
/* Pexecute test program,
|
||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor <ian@airs.com>.
|
||||
|
||||
This file is part of GNU libiberty.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include "ansidecl.h"
|
||||
#include "libiberty.h"
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#ifndef WIFSIGNALED
|
||||
#define WIFSIGNALED(S) (((S) & 0xff) != 0 && ((S) & 0xff) != 0x7f)
|
||||
#endif
|
||||
#ifndef WTERMSIG
|
||||
#define WTERMSIG(S) ((S) & 0x7f)
|
||||
#endif
|
||||
#ifndef WIFEXITED
|
||||
#define WIFEXITED(S) (((S) & 0xff) == 0)
|
||||
#endif
|
||||
#ifndef WEXITSTATUS
|
||||
#define WEXITSTATUS(S) (((S) & 0xff00) >> 8)
|
||||
#endif
|
||||
#ifndef WSTOPSIG
|
||||
#define WSTOPSIG WEXITSTATUS
|
||||
#endif
|
||||
#ifndef WCOREDUMP
|
||||
#define WCOREDUMP(S) ((S) & WCOREFLG)
|
||||
#endif
|
||||
#ifndef WCOREFLG
|
||||
#define WCOREFLG 0200
|
||||
#endif
|
||||
|
||||
#ifndef EXIT_SUCCESS
|
||||
#define EXIT_SUCCESS 0
|
||||
#endif
|
||||
|
||||
#ifndef EXIT_FAILURE
|
||||
#define EXIT_FAILURE 1
|
||||
#endif
|
||||
|
||||
/* When this program is run with no arguments, it runs some tests of
|
||||
the libiberty pexecute functions. As a test program, it simply
|
||||
invokes itself with various arguments.
|
||||
|
||||
argv[1]:
|
||||
*empty string* Run tests, exit with success status
|
||||
exit Exit success
|
||||
error Exit error
|
||||
abort Abort
|
||||
echo Echo remaining arguments, exit success
|
||||
echoerr Echo next arg to stdout, next to stderr, repeat
|
||||
copy Copy stdin to stdout
|
||||
write Write stdin to file named in next argument
|
||||
*/
|
||||
|
||||
static void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN;
|
||||
static void error (int, const char *);
|
||||
static void check_line (int, FILE *, const char *);
|
||||
static void do_cmd (int, char **) ATTRIBUTE_NORETURN;
|
||||
|
||||
/* The number of errors we have seen. */
|
||||
|
||||
static int error_count;
|
||||
|
||||
/* Print a fatal error and exit. LINE is the line number where we
|
||||
detected the error, ERRMSG is the error message to print, and ERR
|
||||
is 0 or an errno value to print. */
|
||||
|
||||
static void
|
||||
fatal_error (int line, const char *errmsg, int err)
|
||||
{
|
||||
fprintf (stderr, "test-pexecute:%d: %s", line, errmsg);
|
||||
if (errno != 0)
|
||||
fprintf (stderr, ": %s", xstrerror (err));
|
||||
fprintf (stderr, "\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#define FATAL_ERROR(ERRMSG, ERR) fatal_error (__LINE__, ERRMSG, ERR)
|
||||
|
||||
/* Print an error message and bump the error count. LINE is the line
|
||||
number where we detected the error, ERRMSG is the error to
|
||||
print. */
|
||||
|
||||
static void
|
||||
error (int line, const char *errmsg)
|
||||
{
|
||||
fprintf (stderr, "test-pexecute:%d: %s\n", line, errmsg);
|
||||
++error_count;
|
||||
}
|
||||
|
||||
#define ERROR(ERRMSG) error (__LINE__, ERRMSG)
|
||||
|
||||
/* Check a line in a file. */
|
||||
|
||||
static void
|
||||
check_line (int line, FILE *e, const char *str)
|
||||
{
|
||||
const char *p;
|
||||
int c;
|
||||
char buf[1000];
|
||||
|
||||
p = str;
|
||||
while (1)
|
||||
{
|
||||
c = getc (e);
|
||||
|
||||
if (*p == '\0')
|
||||
{
|
||||
if (c != '\n')
|
||||
{
|
||||
snprintf (buf, sizeof buf, "got '%c' when expecting newline", c);
|
||||
fatal_error (line, buf, 0);
|
||||
}
|
||||
c = getc (e);
|
||||
if (c != EOF)
|
||||
{
|
||||
snprintf (buf, sizeof buf, "got '%c' when expecting EOF", c);
|
||||
fatal_error (line, buf, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (c != *p)
|
||||
{
|
||||
snprintf (buf, sizeof buf, "expected '%c', got '%c'", *p, c);
|
||||
fatal_error (line, buf, 0);
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_LINE(E, STR) check_line (__LINE__, E, STR)
|
||||
|
||||
/* Main function for the pexecute tester. Run the tests. */
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int trace;
|
||||
struct pex_obj *test_pex_tmp;
|
||||
int test_pex_status;
|
||||
FILE *test_pex_file;
|
||||
struct pex_obj *pex1;
|
||||
char *subargv[10];
|
||||
int status;
|
||||
FILE *e;
|
||||
int statuses[10];
|
||||
|
||||
trace = 0;
|
||||
if (argc > 1 && strcmp (argv[1], "-t") == 0)
|
||||
{
|
||||
trace = 1;
|
||||
--argc;
|
||||
++argv;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
do_cmd (argc, argv);
|
||||
|
||||
#define TEST_PEX_INIT(FLAGS, TEMPBASE) \
|
||||
(((test_pex_tmp = pex_init (FLAGS, "test-pexecute", TEMPBASE)) \
|
||||
!= NULL) \
|
||||
? test_pex_tmp \
|
||||
: (FATAL_ERROR ("pex_init failed", 0), NULL))
|
||||
|
||||
#define TEST_PEX_RUN(PEXOBJ, FLAGS, EXECUTABLE, ARGV, OUTNAME, ERRNAME) \
|
||||
do \
|
||||
{ \
|
||||
int err; \
|
||||
if (trace) \
|
||||
fprintf (stderr, "Line %d: running %s %s\n", \
|
||||
__LINE__, EXECUTABLE, ARGV[0]); \
|
||||
const char *pex_run_err = pex_run (PEXOBJ, FLAGS, EXECUTABLE, \
|
||||
ARGV, OUTNAME, ERRNAME, &err); \
|
||||
if (pex_run_err != NULL) \
|
||||
FATAL_ERROR (pex_run_err, err); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_PEX_GET_STATUS_1(PEXOBJ) \
|
||||
(pex_get_status (PEXOBJ, 1, &test_pex_status) \
|
||||
? test_pex_status \
|
||||
: (FATAL_ERROR ("pex_get_status failed", errno), 1))
|
||||
|
||||
#define TEST_PEX_GET_STATUS(PEXOBJ, COUNT, VECTOR) \
|
||||
do \
|
||||
{ \
|
||||
if (!pex_get_status (PEXOBJ, COUNT, VECTOR)) \
|
||||
FATAL_ERROR ("pex_get_status failed", errno); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_PEX_READ_OUTPUT(PEXOBJ) \
|
||||
((test_pex_file = pex_read_output (PEXOBJ, 0)) != NULL \
|
||||
? test_pex_file \
|
||||
: (FATAL_ERROR ("pex_read_output failed", errno), NULL))
|
||||
|
||||
remove ("temp.x");
|
||||
remove ("temp.y");
|
||||
|
||||
memset (subargv, 0, sizeof subargv);
|
||||
|
||||
subargv[0] = "./test-pexecute";
|
||||
|
||||
pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
|
||||
subargv[1] = "exit";
|
||||
subargv[2] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, NULL);
|
||||
status = TEST_PEX_GET_STATUS_1 (pex1);
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
|
||||
ERROR ("exit failed");
|
||||
pex_free (pex1);
|
||||
|
||||
pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
|
||||
subargv[1] = "error";
|
||||
subargv[2] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, NULL);
|
||||
status = TEST_PEX_GET_STATUS_1 (pex1);
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_FAILURE)
|
||||
ERROR ("error test failed");
|
||||
pex_free (pex1);
|
||||
|
||||
/* We redirect stderr to a file to avoid an error message which is
|
||||
printed on mingw32 when the child calls abort. */
|
||||
pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
|
||||
subargv[1] = "abort";
|
||||
subargv[2] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, "temp.z");
|
||||
status = TEST_PEX_GET_STATUS_1 (pex1);
|
||||
if (!WIFSIGNALED (status) || WTERMSIG (status) != SIGABRT)
|
||||
ERROR ("abort failed");
|
||||
pex_free (pex1);
|
||||
remove ("temp.z");
|
||||
|
||||
pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
|
||||
subargv[1] = "echo";
|
||||
subargv[2] = "foo";
|
||||
subargv[3] = NULL;
|
||||
TEST_PEX_RUN (pex1, 0, "./test-pexecute", subargv, NULL, NULL);
|
||||
e = TEST_PEX_READ_OUTPUT (pex1);
|
||||
CHECK_LINE (e, "foo");
|
||||
if (TEST_PEX_GET_STATUS_1 (pex1) != 0)
|
||||
ERROR ("echo exit status failed");
|
||||
pex_free (pex1);
|
||||
|
||||
pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
|
||||
subargv[1] = "echo";
|
||||
subargv[2] = "bar";
|
||||
subargv[3] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
|
||||
subargv[1] = "copy";
|
||||
subargv[2] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
|
||||
e = TEST_PEX_READ_OUTPUT (pex1);
|
||||
CHECK_LINE (e, "bar");
|
||||
TEST_PEX_GET_STATUS (pex1, 2, statuses);
|
||||
if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
|
||||
|| !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
|
||||
ERROR ("copy exit status failed");
|
||||
pex_free (pex1);
|
||||
if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
|
||||
ERROR ("temporary files exist");
|
||||
|
||||
pex1 = TEST_PEX_INIT (0, "temp");
|
||||
subargv[1] = "echo";
|
||||
subargv[2] = "bar";
|
||||
subargv[3] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
|
||||
subargv[1] = "copy";
|
||||
subargv[2] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
|
||||
e = TEST_PEX_READ_OUTPUT (pex1);
|
||||
CHECK_LINE (e, "bar");
|
||||
TEST_PEX_GET_STATUS (pex1, 2, statuses);
|
||||
if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
|
||||
|| !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
|
||||
ERROR ("copy exit status failed");
|
||||
pex_free (pex1);
|
||||
if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
|
||||
ERROR ("temporary files exist");
|
||||
|
||||
pex1 = TEST_PEX_INIT (PEX_SAVE_TEMPS, "temp");
|
||||
subargv[1] = "echo";
|
||||
subargv[2] = "quux";
|
||||
subargv[3] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
|
||||
subargv[1] = "copy";
|
||||
subargv[2] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
|
||||
e = TEST_PEX_READ_OUTPUT (pex1);
|
||||
CHECK_LINE (e, "quux");
|
||||
TEST_PEX_GET_STATUS (pex1, 2, statuses);
|
||||
if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
|
||||
|| !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
|
||||
ERROR ("copy temp exit status failed");
|
||||
e = fopen ("temp.x", "r");
|
||||
if (e == NULL)
|
||||
FATAL_ERROR ("fopen temp.x failed in copy temp", errno);
|
||||
CHECK_LINE (e, "quux");
|
||||
fclose (e);
|
||||
e = fopen ("temp.y", "r");
|
||||
if (e == NULL)
|
||||
FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
|
||||
CHECK_LINE (e, "quux");
|
||||
fclose (e);
|
||||
pex_free (pex1);
|
||||
remove ("temp.x");
|
||||
remove ("temp.y");
|
||||
|
||||
pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
|
||||
subargv[1] = "echoerr";
|
||||
subargv[2] = "one";
|
||||
subargv[3] = "two";
|
||||
subargv[4] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", "temp2.x");
|
||||
subargv[1] = "write";
|
||||
subargv[2] = "temp2.y";
|
||||
subargv[3] = NULL;
|
||||
TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
|
||||
TEST_PEX_GET_STATUS (pex1, 2, statuses);
|
||||
if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
|
||||
|| !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
|
||||
ERROR ("echoerr exit status failed");
|
||||
pex_free (pex1);
|
||||
if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
|
||||
ERROR ("temporary files exist");
|
||||
e = fopen ("temp2.x", "r");
|
||||
if (e == NULL)
|
||||
FATAL_ERROR ("fopen temp2.x failed in echoerr", errno);
|
||||
CHECK_LINE (e, "two");
|
||||
fclose (e);
|
||||
e = fopen ("temp2.y", "r");
|
||||
if (e == NULL)
|
||||
FATAL_ERROR ("fopen temp2.y failed in echoerr", errno);
|
||||
CHECK_LINE (e, "one");
|
||||
fclose (e);
|
||||
remove ("temp2.x");
|
||||
remove ("temp2.y");
|
||||
|
||||
/* Test the old pexecute interface. */
|
||||
{
|
||||
int pid1, pid2;
|
||||
char *errmsg_fmt;
|
||||
char *errmsg_arg;
|
||||
char errbuf1[1000];
|
||||
char errbuf2[1000];
|
||||
|
||||
subargv[1] = "echo";
|
||||
subargv[2] = "oldpexecute";
|
||||
subargv[3] = NULL;
|
||||
pid1 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
|
||||
&errmsg_fmt, &errmsg_arg, PEXECUTE_FIRST);
|
||||
if (pid1 < 0)
|
||||
{
|
||||
snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
|
||||
snprintf (errbuf2, sizeof errbuf2, "pexecute 1 failed: %s", errbuf1);
|
||||
FATAL_ERROR (errbuf2, 0);
|
||||
}
|
||||
|
||||
subargv[1] = "write";
|
||||
subargv[2] = "temp.y";
|
||||
subargv[3] = NULL;
|
||||
pid2 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
|
||||
&errmsg_fmt, &errmsg_arg, PEXECUTE_LAST);
|
||||
if (pid2 < 0)
|
||||
{
|
||||
snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
|
||||
snprintf (errbuf2, sizeof errbuf2, "pexecute 2 failed: %s", errbuf1);
|
||||
FATAL_ERROR (errbuf2, 0);
|
||||
}
|
||||
|
||||
if (pwait (pid1, &status, 0) < 0)
|
||||
FATAL_ERROR ("write pwait 1 failed", errno);
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
|
||||
ERROR ("write exit status 1 failed");
|
||||
|
||||
if (pwait (pid2, &status, 0) < 0)
|
||||
FATAL_ERROR ("write pwait 1 failed", errno);
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
|
||||
ERROR ("write exit status 2 failed");
|
||||
|
||||
e = fopen ("temp.y", "r");
|
||||
if (e == NULL)
|
||||
FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
|
||||
CHECK_LINE (e, "oldpexecute");
|
||||
fclose (e);
|
||||
|
||||
remove ("temp.y");
|
||||
}
|
||||
|
||||
if (trace)
|
||||
fprintf (stderr, "Exiting with status %d\n", error_count);
|
||||
|
||||
return error_count;
|
||||
}
|
||||
|
||||
/* Execute one of the special testing commands. */
|
||||
|
||||
static void
|
||||
do_cmd (int argc, char **argv)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
/* Try to prevent generating a core dump. */
|
||||
#ifdef RLIMIT_CORE
|
||||
{
|
||||
struct rlimit r;
|
||||
|
||||
r.rlim_cur = 0;
|
||||
r.rlim_max = 0;
|
||||
setrlimit (RLIMIT_CORE, &r);
|
||||
}
|
||||
#endif
|
||||
|
||||
s = argv[1];
|
||||
if (strcmp (s, "exit") == 0)
|
||||
exit (EXIT_SUCCESS);
|
||||
else if (strcmp (s, "echo") == 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 2; i < argc; ++i)
|
||||
{
|
||||
if (i > 2)
|
||||
putchar (' ');
|
||||
fputs (argv[i], stdout);
|
||||
}
|
||||
putchar ('\n');
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else if (strcmp (s, "echoerr") == 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 2; i < argc; ++i)
|
||||
{
|
||||
if (i > 3)
|
||||
putc (' ', (i & 1) == 0 ? stdout : stderr);
|
||||
fputs (argv[i], (i & 1) == 0 ? stdout : stderr);
|
||||
}
|
||||
putc ('\n', stdout);
|
||||
putc ('\n', stderr);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else if (strcmp (s, "error") == 0)
|
||||
exit (EXIT_FAILURE);
|
||||
else if (strcmp (s, "abort") == 0)
|
||||
abort ();
|
||||
else if (strcmp (s, "copy") == 0)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = getchar ()) != EOF)
|
||||
putchar (c);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else if (strcmp (s, "write") == 0)
|
||||
{
|
||||
FILE *e;
|
||||
int c;
|
||||
|
||||
e = fopen (argv[2], "w");
|
||||
if (e == NULL)
|
||||
FATAL_ERROR ("fopen for write failed", errno);
|
||||
while ((c = getchar ()) != EOF)
|
||||
putc (c, e);
|
||||
if (fclose (e) != 0)
|
||||
FATAL_ERROR ("fclose for write failed", errno);
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[1000];
|
||||
|
||||
snprintf (buf, sizeof buf, "unrecognized command %s", argv[1]);
|
||||
FATAL_ERROR (buf, 0);
|
||||
}
|
||||
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
Loading…
Reference in New Issue
Block a user