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.
This commit is contained in:
parent
3d0dfe269d
commit
b109e79adc
|
@ -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>
|
2005-03-28 Mark Mitchell <mark@codesourcery.com>
|
||||||
|
|
||||||
* libiberty.h (ffs): Declare, if necessary.
|
* libiberty.h (ffs): Declare, if necessary.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* Function declarations for libiberty.
|
/* 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
|
Note - certain prototypes declared in this header file are for
|
||||||
functions whoes implementation copyright does not belong to the
|
functions whoes implementation copyright does not belong to the
|
||||||
|
@ -46,6 +46,8 @@ extern "C" {
|
||||||
/* Get a definition for va_list. */
|
/* Get a definition for va_list. */
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
/* Build an argument vector from a string. Allocates memory using
|
/* Build an argument vector from a string. Allocates memory using
|
||||||
malloc. Use freeargv to free the vector. */
|
malloc. Use freeargv to free the vector. */
|
||||||
|
|
||||||
|
@ -314,6 +316,166 @@ extern void hex_init (void);
|
||||||
the argument being performed exactly once. */
|
the argument being performed exactly once. */
|
||||||
#define hex_value(c) ((unsigned int) _hex_value[(unsigned char) (c)])
|
#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. */
|
/* Definitions used by the pexecute routine. */
|
||||||
|
|
||||||
#define PEXECUTE_FIRST 1
|
#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>
|
2005-03-28 Mark Kettenis <kettenis@gnu.org>
|
||||||
|
|
||||||
* unlink-if-ordinary.c: Include <sys/types.h>.
|
* 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 \
|
make-temp-file.c md5.c memchr.c memcmp.c memcpy.c memmove.c \
|
||||||
mempcpy.c memset.c mkstemps.c \
|
mempcpy.c memset.c mkstemps.c \
|
||||||
objalloc.c obstack.c \
|
objalloc.c obstack.c \
|
||||||
partition.c \
|
partition.c pexecute.c \
|
||||||
pex-djgpp.c pex-msdos.c \
|
pex-common.c pex-djgpp.c pex-msdos.c pex-one.c \
|
||||||
pex-unix.c pex-win32.c \
|
pex-unix.c pex-win32.c \
|
||||||
physmem.c putenv.c \
|
physmem.c putenv.c \
|
||||||
random.c regex.c rename.c rindex.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 \
|
./lbasename.o ./lrealpath.o \
|
||||||
./make-relative-prefix.o ./make-temp-file.o \
|
./make-relative-prefix.o ./make-temp-file.o \
|
||||||
./objalloc.o ./obstack.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 \
|
./safe-ctype.o ./sort.o ./spaces.o ./splay-tree.o ./strerror.o \
|
||||||
./strsignal.o \
|
./strsignal.o \
|
||||||
./ternary.o \
|
./ternary.o \
|
||||||
|
@ -756,6 +757,13 @@ $(CONFIGURED_OFILES): stamp-picdir
|
||||||
else true; fi
|
else true; fi
|
||||||
$(COMPILE.c) $(srcdir)/partition.c $(OUTPUT_OPTION)
|
$(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 \
|
./pex-djgpp.o: $(srcdir)/pex-djgpp.c config.h $(INCDIR)/ansidecl.h \
|
||||||
$(INCDIR)/libiberty.h $(srcdir)/pex-common.h
|
$(INCDIR)/libiberty.h $(srcdir)/pex-common.h
|
||||||
if [ x"$(PICFLAG)" != x ]; then \
|
if [ x"$(PICFLAG)" != x ]; then \
|
||||||
|
@ -771,6 +779,13 @@ $(CONFIGURED_OFILES): stamp-picdir
|
||||||
else true; fi
|
else true; fi
|
||||||
$(COMPILE.c) $(srcdir)/pex-msdos.c $(OUTPUT_OPTION)
|
$(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 \
|
./pex-unix.o: $(srcdir)/pex-unix.c config.h $(INCDIR)/ansidecl.h \
|
||||||
$(INCDIR)/libiberty.h $(srcdir)/pex-common.h
|
$(INCDIR)/libiberty.h $(srcdir)/pex-common.h
|
||||||
if [ x"$(PICFLAG)" != x ]; then \
|
if [ x"$(PICFLAG)" != x ]; then \
|
||||||
|
@ -785,6 +800,13 @@ $(CONFIGURED_OFILES): stamp-picdir
|
||||||
else true; fi
|
else true; fi
|
||||||
$(COMPILE.c) $(srcdir)/pex-win32.c $(OUTPUT_OPTION)
|
$(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 \
|
./physmem.o: $(srcdir)/physmem.c config.h $(INCDIR)/ansidecl.h \
|
||||||
$(INCDIR)/libiberty.h
|
$(INCDIR)/libiberty.h
|
||||||
if [ x"$(PICFLAG)" != x ]; then \
|
if [ x"$(PICFLAG)" != x ]; then \
|
||||||
|
|
|
@ -4818,7 +4818,7 @@ vars="sys_errlist sys_nerr sys_siglist"
|
||||||
|
|
||||||
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
|
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
|
||||||
checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp"
|
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
|
# These are neither executed nor required, but they help keep
|
||||||
# autoheader happy without adding a bunch of text to acconfig.h.
|
# 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 \
|
strtod strtol strtoul tmpnam vasprintf vfprintf vprintf \
|
||||||
vsprintf waitpid getrusage on_exit psignal strerror strsignal \
|
vsprintf waitpid getrusage on_exit psignal strerror strsignal \
|
||||||
sysconf times sbrk gettimeofday ffs snprintf vsnprintf \
|
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
|
realpath canonicalize_file_name
|
||||||
do
|
do
|
||||||
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||||
|
@ -5133,7 +5133,7 @@ fi;
|
||||||
else
|
else
|
||||||
|
|
||||||
# Not a target library, so we set things up to run the test suite.
|
# Not a target library, so we set things up to run the test suite.
|
||||||
CHECK=check-cplus-dem
|
CHECK=really-check
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -268,7 +268,7 @@ vars="sys_errlist sys_nerr sys_siglist"
|
||||||
|
|
||||||
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
|
checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
|
||||||
checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp"
|
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
|
# These are neither executed nor required, but they help keep
|
||||||
# autoheader happy without adding a bunch of text to acconfig.h.
|
# 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 \
|
strtod strtol strtoul tmpnam vasprintf vfprintf vprintf \
|
||||||
vsprintf waitpid getrusage on_exit psignal strerror strsignal \
|
vsprintf waitpid getrusage on_exit psignal strerror strsignal \
|
||||||
sysconf times sbrk gettimeofday ffs snprintf vsnprintf \
|
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)
|
realpath canonicalize_file_name)
|
||||||
AC_DEFINE(HAVE_SYS_ERRLIST, 1, [Define if you have the sys_errlist variable.])
|
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.])
|
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
|
else
|
||||||
|
|
||||||
# Not a target library, so we set things up to run the test suite.
|
# Not a target library, so we set things up to run the test suite.
|
||||||
CHECK=check-cplus-dem
|
CHECK=really-check
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
@c Edit the *.c files, configure with --enable-maintainer-mode,
|
@c Edit the *.c files, configure with --enable-maintainer-mode,
|
||||||
@c and let gather-docs build you a new copy.
|
@c and let gather-docs build you a new copy.
|
||||||
|
|
||||||
@c safe-ctype.c:24
|
@c safe-ctype.c:25
|
||||||
@defvr Extension HOST_CHARSET
|
@defvr Extension HOST_CHARSET
|
||||||
This macro indicates the basic character set and encoding used by the
|
This macro indicates the basic character set and encoding used by the
|
||||||
host: more precisely, the encoding used for character constants in
|
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 ftable
|
||||||
@end defvr
|
@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
|
@c alloca.c:26
|
||||||
@deftypefn Replacement void* alloca (size_t @var{size})
|
@deftypefn Replacement void* alloca (size_t @var{size})
|
||||||
|
|
||||||
|
@ -43,7 +176,7 @@ the possibility of a GCC built-in function.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c asprintf.c:33
|
@c asprintf.c:29
|
||||||
@deftypefn Extension int asprintf (char **@var{resptr}, const char *@var{format}, ...)
|
@deftypefn Extension int asprintf (char **@var{resptr}, const char *@var{format}, ...)
|
||||||
|
|
||||||
Like @code{sprintf}, but instead of passing a pointer to a buffer, you
|
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
|
@end deftypefn
|
||||||
|
|
||||||
@c argv.c:139
|
@c argv.c:121
|
||||||
@deftypefn Extension char** buildargv (char *@var{sp})
|
@deftypefn Extension char** buildargv (char *@var{sp})
|
||||||
|
|
||||||
Given a pointer to a string, parse the string extracting fields
|
Given a pointer to a string, parse the string extracting fields
|
||||||
|
@ -158,7 +291,7 @@ not recommended.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c make-temp-file.c:88
|
@c make-temp-file.c:87
|
||||||
@deftypefn Replacement char* choose_tmpdir ()
|
@deftypefn Replacement char* choose_tmpdir ()
|
||||||
|
|
||||||
Returns a pointer to a directory path suitable for creating temporary
|
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
|
@end deftypefn
|
||||||
|
|
||||||
@c argv.c:65
|
@c argv.c:49
|
||||||
@deftypefn Extension char** dupargv (char **@var{vector})
|
@deftypefn Extension char** dupargv (char **@var{vector})
|
||||||
|
|
||||||
Duplicate an argument vector. Simply scans through @var{vector},
|
Duplicate an argument vector. Simply scans through @var{vector},
|
||||||
|
@ -288,7 +421,7 @@ Ignores case when performing the comparison.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c argv.c:111
|
@c argv.c:94
|
||||||
@deftypefn Extension void freeargv (char **@var{vector})
|
@deftypefn Extension void freeargv (char **@var{vector})
|
||||||
|
|
||||||
Free an argument vector that was built using @code{buildargv}. Simply
|
Free an argument vector that was built using @code{buildargv}. Simply
|
||||||
|
@ -412,7 +545,7 @@ struct qelem @{
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c safe-ctype.c:45
|
@c safe-ctype.c:46
|
||||||
@deffn Extension ISALPHA (@var{c})
|
@deffn Extension ISALPHA (@var{c})
|
||||||
@deffnx Extension ISALNUM (@var{c})
|
@deffnx Extension ISALNUM (@var{c})
|
||||||
@deffnx Extension ISBLANK (@var{c})
|
@deffnx Extension ISBLANK (@var{c})
|
||||||
|
@ -462,7 +595,7 @@ false for characters with numeric values from 128 to 255.
|
||||||
@end itemize
|
@end itemize
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@c safe-ctype.c:94
|
@c safe-ctype.c:95
|
||||||
@deffn Extension ISIDNUM (@var{c})
|
@deffn Extension ISIDNUM (@var{c})
|
||||||
@deffnx Extension ISIDST (@var{c})
|
@deffnx Extension ISIDST (@var{c})
|
||||||
@deffnx Extension IS_VSPACE (@var{c})
|
@deffnx Extension IS_VSPACE (@var{c})
|
||||||
|
@ -535,7 +668,7 @@ relative prefix can be found, return @code{NULL}.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c make-temp-file.c:138
|
@c make-temp-file.c:137
|
||||||
@deftypefn Replacement char* make_temp_file (const char *@var{suffix})
|
@deftypefn Replacement char* make_temp_file (const char *@var{suffix})
|
||||||
|
|
||||||
Return a temporary file name (as a string) or @code{NULL} if unable to
|
Return a temporary file name (as a string) or @code{NULL} if unable to
|
||||||
|
@ -618,46 +751,62 @@ reading and writing.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c pexecute.txh:1
|
@c pexecute.txh:155
|
||||||
@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)
|
@deftypefn Extension void pex_free (struct pex_obj @var{obj})
|
||||||
|
|
||||||
Executes a program.
|
Clean up and free all data associated with @var{obj}.
|
||||||
|
|
||||||
@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.
|
|
||||||
|
|
||||||
@end deftypefn
|
@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})
|
@deftypefn Supplemental void psignal (unsigned @var{signo}, char *@var{message})
|
||||||
|
|
||||||
Print @var{message} to the standard error, followed by a colon,
|
Print @var{message} to the standard error, followed by a colon,
|
||||||
|
@ -676,23 +825,10 @@ name is unset/removed.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c pexecute.txh:39
|
@c pexecute.txh:181
|
||||||
@deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags})
|
@deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags})
|
||||||
|
|
||||||
Waits for a program started by @code{pexecute} to finish.
|
Another part of the old execution interface.
|
||||||
|
|
||||||
@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.
|
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
|
@ -711,7 +847,7 @@ control over the state of the random number generator.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c concat.c:177
|
@c concat.c:167
|
||||||
@deftypefn Extension char* reconcat (char *@var{optr}, const char *@var{s1}, @dots{}, @code{NULL})
|
@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
|
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
|
@end deftypefn
|
||||||
|
|
||||||
@c strsignal.c:352
|
@c strsignal.c:348
|
||||||
@deftypefn Extension int signo_max (void)
|
@deftypefn Extension int signo_max (void)
|
||||||
|
|
||||||
Returns the maximum signal value for which a corresponding symbolic
|
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
|
@end deftypefn
|
||||||
|
|
||||||
@c strerror.c:671
|
@c strerror.c:670
|
||||||
@deftypefn Replacement {const char*} strerrno (int @var{errnum})
|
@deftypefn Replacement {const char*} strerrno (int @var{errnum})
|
||||||
|
|
||||||
Given an error number returned from a system call (typically returned
|
Given an error number returned from a system call (typically returned
|
||||||
|
@ -919,7 +1055,7 @@ null character, the results are undefined.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c strsignal.c:387
|
@c strsignal.c:383
|
||||||
@deftypefn Supplemental {const char *} strsignal (int @var{signo})
|
@deftypefn Supplemental {const char *} strsignal (int @var{signo})
|
||||||
|
|
||||||
Maps an signal number to an signal message string, the contents of
|
Maps an signal number to an signal message string, the contents of
|
||||||
|
@ -940,7 +1076,7 @@ call to @code{strsignal}.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c strsignal.c:451
|
@c strsignal.c:446
|
||||||
@deftypefn Extension {const char*} strsigno (int @var{signo})
|
@deftypefn Extension {const char*} strsigno (int @var{signo})
|
||||||
|
|
||||||
Given an signal number, returns a pointer to a string containing the
|
Given an signal number, returns a pointer to a string containing the
|
||||||
|
@ -982,7 +1118,7 @@ the location referenced by @var{endptr}.
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@c strerror.c:731
|
@c strerror.c:729
|
||||||
@deftypefn Extension int strtoerrno (const char *@var{name})
|
@deftypefn Extension int strtoerrno (const char *@var{name})
|
||||||
|
|
||||||
Given the symbolic name of a error number (e.g., @code{EACCES}), map it
|
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
|
@end deftypefn
|
||||||
|
|
||||||
@c strsignal.c:506
|
@c strsignal.c:500
|
||||||
@deftypefn Extension int strtosigno (const char *@var{name})
|
@deftypefn Extension int strtosigno (const char *@var{name})
|
||||||
|
|
||||||
Given the symbolic name of a signal, map it to a signal number. If no
|
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
|
@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})
|
@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,
|
Like @code{vsprintf}, but instead of passing a pointer to a buffer,
|
||||||
|
|
|
@ -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 "config.h"
|
||||||
#include "libiberty.h"
|
#include "libiberty.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define install_error_msg "installation problem, cannot exec `%s'"
|
#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. */
|
/* value of `pipe': port index for writing. */
|
||||||
#define WRITE_PORT 1
|
#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
|
#endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* Utilities to execute a program in a subprocess (possibly linked by pipes
|
/* Utilities to execute a program in a subprocess (possibly linked by pipes
|
||||||
with other subprocesses), and wait for it. DJGPP specialization.
|
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.
|
Free Software Foundation, Inc.
|
||||||
|
|
||||||
This file is part of the libiberty library.
|
This file is part of the libiberty library.
|
||||||
|
@ -38,59 +38,246 @@ extern int errno;
|
||||||
#define PWAIT_ERROR EINVAL
|
#define PWAIT_ERROR EINVAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* MSDOS doesn't multitask, but for the sake of a consistent interface
|
static int pex_djgpp_open_read (struct pex_obj *, const char *, int);
|
||||||
the code behaves like it does. pexecute runs the program, tucks the
|
static int pex_djgpp_open_write (struct pex_obj *, const char *, int);
|
||||||
exit code away, and returns a "pid". pwait must be called to fetch the
|
static long pex_djgpp_exec_child (struct pex_obj *, int, const char *,
|
||||||
exit code. */
|
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. */
|
/* The list of functions we pass to the common routines. */
|
||||||
static int last_pid = 0;
|
|
||||||
static int last_status = 0;
|
|
||||||
static int last_reaped = 0;
|
|
||||||
|
|
||||||
int
|
const struct pex_funcs funcs =
|
||||||
pexecute (const char *program, char * const *argv, const char *this_pname,
|
|
||||||
const char *temp_base, char **errmsg_fmt,
|
|
||||||
char **errmsg_arg, int flags)
|
|
||||||
{
|
{
|
||||||
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++;
|
/* Return a newly initialized pex_obj structure. */
|
||||||
if (last_pid < 0)
|
|
||||||
last_pid = 1;
|
|
||||||
|
|
||||||
if ((flags & PEXECUTE_ONE) != PEXECUTE_ONE)
|
struct pex_obj *
|
||||||
abort ();
|
pex_init (int flags, const char *pname, const char *tempbase)
|
||||||
|
{
|
||||||
/* ??? What are the possible return values from spawnv? */
|
/* DJGPP does not support pipes. */
|
||||||
rc = (flags & PEXECUTE_SEARCH ? spawnvp : spawnv) (P_WAIT, program, argv);
|
flags &= ~ PEX_USE_PIPES;
|
||||||
|
return pex_init_common (flags, pname, tempbase, funcs);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
/* Open a file for reading. */
|
||||||
pwait (int pid, int *status, int flags)
|
|
||||||
|
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. */
|
return open (name, O_RDONLY | (binary ? O_BINARY : O_TEXT));
|
||||||
if (pid != last_pid
|
}
|
||||||
/* Called twice for the same child? */
|
|
||||||
|| pid == last_reaped)
|
/* Open a file for writing. */
|
||||||
{
|
|
||||||
errno = PWAIT_ERROR;
|
static int
|
||||||
return -1;
|
pex_djgpp_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED,
|
||||||
}
|
const char *name, int binary)
|
||||||
/* ??? Here's an opportunity to canonicalize the values in STATUS.
|
{
|
||||||
Needed? */
|
/* Note that we can't use O_EXCL here because gcc may have already
|
||||||
*status = (last_status >> 8);
|
created the temporary file via make_temp_file. */
|
||||||
last_reaped = last_pid;
|
return open (name,
|
||||||
return last_pid;
|
(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
|
/* Utilities to execute a program in a subprocess (possibly linked by pipes
|
||||||
with other subprocesses), and wait for it. Generic MSDOS specialization.
|
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.
|
Free Software Foundation, Inc.
|
||||||
|
|
||||||
This file is part of the libiberty library.
|
This file is part of the libiberty library.
|
||||||
|
@ -36,105 +36,281 @@ extern int errno;
|
||||||
#include "safe-ctype.h"
|
#include "safe-ctype.h"
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
|
|
||||||
/* MSDOS doesn't multitask, but for the sake of a consistent interface
|
/* The structure we keep in obj->sysdep. */
|
||||||
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. */
|
|
||||||
|
|
||||||
/* For communicating information from pexecute to pwait. */
|
#define PEX_MSDOS_FILE_COUNT 3
|
||||||
static int last_pid = 0;
|
|
||||||
static int last_status = 0;
|
|
||||||
static int last_reaped = 0;
|
|
||||||
|
|
||||||
int
|
#define PEX_MSDOS_FD_OFFSET 10
|
||||||
pexecute (const char *program, char * const *argv, const char *this_pname,
|
|
||||||
const char *temp_base, char **errmsg_fmt, char **errmsg_arg,
|
struct pex_msdos
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
int rc;
|
/* An array of file names. We refer to these using file descriptors
|
||||||
char *scmd, *rf;
|
of 10 + array index. */
|
||||||
FILE *argfile;
|
const char *files[PEX_MSDOS_FILE_COUNT];
|
||||||
int i, el = flags & PEXECUTE_SEARCH ? 4 : 0;
|
/* Exit statuses of programs which have been run. */
|
||||||
|
int *statuses;
|
||||||
|
};
|
||||||
|
|
||||||
last_pid++;
|
static int pex_msdos_open (struct pex_obj *, const char *, int);
|
||||||
if (last_pid < 0)
|
static int pex_msdos_open (struct pex_obj *, const char *, int);
|
||||||
last_pid = 1;
|
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)
|
/* The list of functions we pass to the common routines. */
|
||||||
abort ();
|
|
||||||
|
|
||||||
if (temp_base == 0)
|
const struct pex_funcs funcs =
|
||||||
temp_base = choose_temp_base ();
|
{
|
||||||
scmd = (char *) xmalloc (strlen (program) + strlen (temp_base) + 6 + el);
|
pex_msdos_open,
|
||||||
rf = scmd + strlen(program) + 2 + el;
|
pex_msdos_open,
|
||||||
sprintf (scmd, "%s%s @%s.gp", program,
|
pex_msdos_exec_child,
|
||||||
(flags & PEXECUTE_SEARCH ? ".exe" : ""), temp_base);
|
pex_msdos_close,
|
||||||
argfile = fopen (rf, "w");
|
pex_msdos_wait,
|
||||||
if (argfile == 0)
|
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);
|
free (scmd);
|
||||||
errno = errno_save;
|
free (rf);
|
||||||
*errmsg_fmt = "cannot open `%s.gp'";
|
*errmsg = "cannot open temporary command file";
|
||||||
*errmsg_arg = temp_base;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=1; argv[i]; i++)
|
for (i = 1; argv[i] != NULL; ++i)
|
||||||
{
|
{
|
||||||
char *cp;
|
char *p;
|
||||||
for (cp = argv[i]; *cp; cp++)
|
|
||||||
|
for (p = argv[i]; *p != '\0'; ++p)
|
||||||
{
|
{
|
||||||
if (*cp == '"' || *cp == '\'' || *cp == '\\' || ISSPACE (*cp))
|
if (*p == '"' || *p == '\'' || *p == '\\' || ISSPACE (*p))
|
||||||
fputc ('\\', argfile);
|
putc ('\\', argfile);
|
||||||
fputc (*cp, argfile);
|
putc (*p, argfile);
|
||||||
}
|
}
|
||||||
fputc ('\n', argfile);
|
putc ('\n', argfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose (argfile);
|
fclose (argfile);
|
||||||
|
|
||||||
rc = system (scmd);
|
status = system (scmd);
|
||||||
|
|
||||||
{
|
if (status == -1)
|
||||||
int errno_save = errno;
|
|
||||||
remove (rf);
|
|
||||||
free (scmd);
|
|
||||||
errno = errno_save;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc == -1)
|
|
||||||
{
|
{
|
||||||
*errmsg_fmt = install_error_msg;
|
*err = errno;
|
||||||
*errmsg_arg = (char *)program;
|
remove (rf);
|
||||||
|
free (scmd);
|
||||||
|
free (rf);
|
||||||
|
*errmsg = "system";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tuck the status away for pwait, and return a "pid". */
|
remove (rf);
|
||||||
last_status = rc << 8;
|
free (scmd);
|
||||||
return last_pid;
|
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. */
|
/* Wait for a child process to complete. Actually the child process
|
||||||
#ifdef ECHILD
|
has already completed, and we just need to return the exit
|
||||||
#define PWAIT_ERROR ECHILD
|
status. */
|
||||||
#else
|
|
||||||
#define PWAIT_ERROR EINVAL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
static int
|
||||||
pwait (int pid, int *status, int flags)
|
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. */
|
struct pex_msdos *ms;
|
||||||
if (pid != last_pid
|
|
||||||
/* Called twice for the same child? */
|
ms = (struct pex_msdos *) obj->sysdep;
|
||||||
|| pid == last_reaped)
|
|
||||||
{
|
if (time != NULL)
|
||||||
errno = PWAIT_ERROR;
|
memset (time, 0, sizeof *time);
|
||||||
return -1;
|
|
||||||
}
|
*status = ms->statuses[pid];
|
||||||
/* ??? Here's an opportunity to canonicalize the values in STATUS.
|
|
||||||
Needed? */
|
return 0;
|
||||||
*status = last_status;
|
}
|
||||||
last_reaped = last_pid;
|
|
||||||
return last_pid;
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
Boston, MA 02111-1307, USA. */
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "libiberty.h"
|
||||||
#include "pex-common.h"
|
#include "pex-common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#ifdef NEED_DECLARATION_ERRNO
|
#ifdef NEED_DECLARATION_ERRNO
|
||||||
extern int errno;
|
extern int errno;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
#ifdef HAVE_STRING_H
|
#ifdef HAVE_STRING_H
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_STDLIB_H
|
|
||||||
#include <stdlib.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SYS_WAIT_H
|
#ifdef HAVE_SYS_WAIT_H
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_GETRUSAGE
|
||||||
#ifndef HAVE_WAITPID
|
#include <sys/time.h>
|
||||||
#define waitpid(pid, status, flags) wait(status)
|
#include <sys/resource.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef vfork /* Autoconf may define this to fork for us. */
|
#ifdef vfork /* Autoconf may define this to fork for us. */
|
||||||
# define VFORK_STRING "fork"
|
# define VFORK_STRING "fork"
|
||||||
|
@ -57,80 +70,300 @@ extern int errno;
|
||||||
lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
|
lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
|
||||||
#endif /* VMS */
|
#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
|
/* File mode to use for private and world-readable files. */
|
||||||
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).
|
|
||||||
|
|
||||||
We work a little bit harder to avoid gcc warnings. gcc will warn
|
#if defined (S_IRUSR) && defined (S_IWUSR) && defined (S_IRGRP) && defined (S_IWGRP) && defined (S_IROTH) && defined (S_IWOTH)
|
||||||
about any automatic variable which is live at the time of the
|
#define PUBLIC_MODE \
|
||||||
vfork, which is non-volatile, and which is either set more than
|
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
|
||||||
once or is an argument to the function. This warning isn't quite
|
#else
|
||||||
right, since what we really care about is whether the variable is
|
#define PUBLIC_MODE 0666
|
||||||
live at the time of the vfork and set afterward by the child
|
#endif
|
||||||
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. */
|
|
||||||
|
|
||||||
int
|
/* Get the exit status of a particular process, and optionally get the
|
||||||
pexecute (const char *program, char * const *argv, const char *this_pname,
|
time that it took. This is simple if we have wait4, slightly
|
||||||
const char *temp_base ATTRIBUTE_UNUSED,
|
harder if we have waitpid, and is a pain if we only have wait. */
|
||||||
char **errmsg_fmt, char **errmsg_arg, int flagsarg)
|
|
||||||
|
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;
|
pid_t ret;
|
||||||
int pdes[2];
|
struct rusage r;
|
||||||
int out;
|
|
||||||
int input_desc, output_desc;
|
#ifdef HAVE_WAITPID
|
||||||
int flags;
|
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
|
/* We declare these to be volatile to avoid warnings from gcc about
|
||||||
them being clobbered by vfork. */
|
them being clobbered by vfork. */
|
||||||
volatile int retries, sleep_interval;
|
volatile int sleep_interval;
|
||||||
/* Pipe waiting from last process, to be used as input for the next one.
|
volatile int retries;
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
sleep_interval = 1;
|
||||||
pid = -1;
|
pid = -1;
|
||||||
for (retries = 0; retries < 4; retries++)
|
for (retries = 0; retries < 4; ++retries)
|
||||||
{
|
{
|
||||||
pid = vfork ();
|
pid = vfork ();
|
||||||
if (pid >= 0)
|
if (pid >= 0)
|
||||||
|
@ -142,66 +375,139 @@ pexecute (const char *program, char * const *argv, const char *this_pname,
|
||||||
switch (pid)
|
switch (pid)
|
||||||
{
|
{
|
||||||
case -1:
|
case -1:
|
||||||
*errmsg_fmt = "fork";
|
*err = errno;
|
||||||
*errmsg_arg = NULL;
|
*errmsg = VFORK_STRING;
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
case 0: /* child */
|
case 0:
|
||||||
/* Move the input and output pipes into place, if necessary. */
|
/* Child process. */
|
||||||
if (input_desc != STDIN_FILE_NO)
|
if (in != STDIN_FILE_NO)
|
||||||
{
|
{
|
||||||
close (STDIN_FILE_NO);
|
if (dup2 (in, STDIN_FILE_NO) < 0)
|
||||||
dup (input_desc);
|
pex_child_error (obj, executable, "dup2", errno);
|
||||||
close (input_desc);
|
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);
|
if (dup2 (out, STDOUT_FILE_NO) < 0)
|
||||||
dup (output_desc);
|
pex_child_error (obj, executable, "dup2", errno);
|
||||||
close (output_desc);
|
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
|
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 */
|
/* NOTREACHED */
|
||||||
return 0;
|
return -1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* In the parent, after forking.
|
/* Parent process. */
|
||||||
Close the descriptors that we made for this child. */
|
if (in != STDIN_FILE_NO)
|
||||||
if (input_desc != STDIN_FILE_NO)
|
{
|
||||||
close (input_desc);
|
if (close (in) < 0)
|
||||||
if (output_desc != STDOUT_FILE_NO)
|
{
|
||||||
close (output_desc);
|
*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 (long) pid;
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
/* Wait for a child process to complete. */
|
||||||
pwait (int pid, int *status, int flags ATTRIBUTE_UNUSED)
|
|
||||||
|
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.
|
/* If we are cleaning up when the caller didn't retrieve process
|
||||||
Needed? */
|
status for some reason, encourage the process to go away. */
|
||||||
pid = waitpid (pid, status, 0);
|
if (done)
|
||||||
return pid;
|
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"
|
#include "pex-common.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_STDLIB_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
#ifdef HAVE_STRING_H
|
#ifdef HAVE_STRING_H
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -35,6 +38,7 @@ Boston, MA 02111-1307, USA. */
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
/* mingw32 headers may not define the following. */
|
/* 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. */
|
to remove the outermost set of double quotes from all arguments. */
|
||||||
|
|
||||||
static const char * const *
|
static const char * const *
|
||||||
fix_argv (char **argvec)
|
fix_argv (char * const *argvec)
|
||||||
{
|
{
|
||||||
|
char **argv;
|
||||||
int i;
|
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
|
/* Ensure that the executable pathname uses Win32 backslashes. This
|
||||||
is not necessary on NT, but on W9x, forward slashes causes failure
|
is not necessary on NT, but on W9x, forward slashes causes
|
||||||
of spawn* and exec* functions (and probably any function that
|
failure of spawn* and exec* functions (and probably any function
|
||||||
calls CreateProcess) *iff* the executable pathname (argvec[0]) is
|
that calls CreateProcess) *iff* the executable pathname (argv[0])
|
||||||
a quoted string. And quoting is necessary in case a pathname
|
is a quoted string. And quoting is necessary in case a pathname
|
||||||
contains embedded white space. You can't win. */
|
contains embedded white space. You can't win. */
|
||||||
for (; *command0 != '\0'; command0++)
|
for (command0 = argv[0]; *command0 != '\0'; command0++)
|
||||||
if (*command0 == '/')
|
if (*command0 == '/')
|
||||||
*command0 = '\\';
|
*command0 = '\\';
|
||||||
|
|
||||||
for (i = 1; argvec[i] != 0; i++)
|
for (i = 1; argv[i] != 0; i++)
|
||||||
{
|
{
|
||||||
int len, j;
|
int len, j;
|
||||||
char *temp, *newtemp;
|
char *temp, *newtemp;
|
||||||
|
|
||||||
temp = argvec[i];
|
temp = argv[i];
|
||||||
len = strlen (temp);
|
len = strlen (temp);
|
||||||
for (j = 0; j < len; j++)
|
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;
|
int len, trailing_backslash;
|
||||||
char *temp;
|
char *temp;
|
||||||
|
|
||||||
len = strlen (argvec[i]);
|
len = strlen (argv[i]);
|
||||||
trailing_backslash = 0;
|
trailing_backslash = 0;
|
||||||
|
|
||||||
/* There is an added complication when an arg with embedded white
|
/* 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
|
We handle this case by escaping the trailing backslash, provided
|
||||||
it was not escaped in the first place. */
|
it was not escaped in the first place. */
|
||||||
if (len > 1
|
if (len > 1
|
||||||
&& argvec[i][len-1] == '\\'
|
&& argv[i][len-1] == '\\'
|
||||||
&& argvec[i][len-2] != '\\')
|
&& argv[i][len-2] != '\\')
|
||||||
{
|
{
|
||||||
trailing_backslash = 1;
|
trailing_backslash = 1;
|
||||||
++len; /* to escape the final backslash. */
|
++len; /* to escape the final backslash. */
|
||||||
|
@ -122,127 +152,289 @@ fix_argv (char **argvec)
|
||||||
|
|
||||||
temp = xmalloc (len + 1);
|
temp = xmalloc (len + 1);
|
||||||
temp[0] = '"';
|
temp[0] = '"';
|
||||||
strcpy (temp + 1, argvec[i]);
|
strcpy (temp + 1, argv[i]);
|
||||||
if (trailing_backslash)
|
if (trailing_backslash)
|
||||||
temp[len-2] = '\\';
|
temp[len - 2] = '\\';
|
||||||
temp[len-1] = '"';
|
temp[len - 1] = '"';
|
||||||
temp[len] = '\0';
|
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 */
|
static int pex_win32_open_read (struct pex_obj *, const char *, int);
|
||||||
int
|
static int pex_win32_open_write (struct pex_obj *, const char *, int);
|
||||||
pexecute (const char *program, char * const *argv,
|
static long pex_win32_exec_child (struct pex_obj *, int, const char *,
|
||||||
const char *this_pname ATTRIBUTE_UNUSED,
|
char * const *, int, int, int,
|
||||||
const char *temp_base ATTRIBUTE_UNUSED,
|
const char **, int *);
|
||||||
char **errmsg_fmt, char **errmsg_arg, int flags)
|
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;
|
pex_win32_open_read,
|
||||||
int pdes[2];
|
pex_win32_open_write,
|
||||||
int org_stdin = -1;
|
pex_win32_exec_child,
|
||||||
int org_stdout = -1;
|
pex_win32_close,
|
||||||
int input_desc, output_desc;
|
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.
|
/* Return a newly initialized pex_obj structure. */
|
||||||
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;
|
|
||||||
|
|
||||||
/* If this is the first process, initialize. */
|
struct pex_obj *
|
||||||
if (flags & PEXECUTE_FIRST)
|
pex_init (int flags, const char *pname, const char *tempbase)
|
||||||
last_pipe_input = STDIN_FILE_NO;
|
{
|
||||||
|
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,
|
static int
|
||||||
and record it as waiting to be the input to the next process. */
|
pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
|
||||||
if (! (flags & PEXECUTE_LAST))
|
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";
|
*err = errno;
|
||||||
*errmsg_arg = NULL;
|
*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;
|
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);
|
org_out = _dup (STDOUT_FILE_NO);
|
||||||
dup2 (input_desc, STDIN_FILE_NO);
|
if (org_out < 0)
|
||||||
close (input_desc);
|
{
|
||||||
|
*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);
|
org_errdes = _dup (STDERR_FILE_NO);
|
||||||
dup2 (output_desc, STDOUT_FILE_NO);
|
if (org_errdes < 0)
|
||||||
close (output_desc);
|
{
|
||||||
|
*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)
|
pid = (((flags & PEX_SEARCH) != 0 ? _spawnvp : _spawnv)
|
||||||
(_P_NOWAIT, program, fix_argv(argv));
|
(_P_NOWAIT, executable, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid == -1)
|
if (pid == -1)
|
||||||
{
|
{
|
||||||
*errmsg_fmt = install_error_msg;
|
*err = errno;
|
||||||
*errmsg_arg = (char*) program;
|
*errmsg = ((flags & PEX_SEARCH) != 0) ? "_spawnvp" : "_spawnv";
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
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;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MS CRTDLL doesn't return enough information in status to decide if the
|
/* Wait for a child process to complete. MS CRTDLL doesn't return
|
||||||
child exited due to a signal or not, rather it simply returns an
|
enough information in status to decide if the child exited due to a
|
||||||
integer with the exit code of the child; eg., if the child exited with
|
signal or not, rather it simply returns an integer with the exit
|
||||||
an abort() call and didn't have a handler for SIGABRT, it simply returns
|
code of the child; eg., if the child exited with an abort() call
|
||||||
with status = 3. We fix the status code to conform to the usual WIF*
|
and didn't have a handler for SIGABRT, it simply returns with
|
||||||
macros. Note that WIFSIGNALED will never be true under CRTDLL. */
|
status == 3. We fix the status code to conform to the usual WIF*
|
||||||
|
macros. Note that WIFSIGNALED will never be true under CRTDLL. */
|
||||||
|
|
||||||
int
|
static int
|
||||||
pwait (int pid, int *status, int flags ATTRIBUTE_UNUSED)
|
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;
|
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.
|
/* FIXME: If done is non-zero, we should probably try to kill the
|
||||||
Needed? */
|
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)
|
if (termstat == 3)
|
||||||
*status = SIGABRT;
|
*status = SIGABRT;
|
||||||
else
|
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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
@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.
|
This is the old interface to execute one or more programs. It is
|
||||||
|
still supported for compatibility purposes, but is no longer
|
||||||
@var{program} and @var{argv} are the arguments to
|
documented.
|
||||||
@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.
|
|
||||||
|
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags})
|
@deftypefn Extension int pwait (int @var{pid}, int *@var{status}, int @var{flags})
|
||||||
|
|
||||||
Waits for a program started by @code{pexecute} to finish.
|
Another part of the old execution interface.
|
||||||
|
|
||||||
@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.
|
|
||||||
|
|
||||||
@end deftypefn
|
@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:
|
all:
|
||||||
|
|
||||||
|
# CHECK is set to "really_check" or the empty string by configure.
|
||||||
check: @CHECK@
|
check: @CHECK@
|
||||||
|
|
||||||
|
really-check: check-cplus-dem check-pexecute
|
||||||
|
|
||||||
# Run some tests of the demangler.
|
# Run some tests of the demangler.
|
||||||
check-cplus-dem: test-demangle $(srcdir)/demangle-expected
|
check-cplus-dem: test-demangle $(srcdir)/demangle-expected
|
||||||
./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_COMPILE = $(CC) @DEFS@ $(LIBCFLAGS) -I.. -I$(INCDIR) $(HDEFINES)
|
||||||
test-demangle: $(srcdir)/test-demangle.c ../libiberty.a
|
test-demangle: $(srcdir)/test-demangle.c ../libiberty.a
|
||||||
$(TEST_COMPILE) -o test-demangle \
|
$(TEST_COMPILE) -o test-demangle \
|
||||||
$(srcdir)/test-demangle.c ../libiberty.a
|
$(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.
|
# Standard (either GNU or Cygnus) rules we don't use.
|
||||||
info install-info clean-info dvi install etags tags installcheck:
|
info install-info clean-info dvi install etags tags installcheck:
|
||||||
|
|
||||||
|
|
|
@ -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