aboutsummaryrefslogtreecommitdiff
path: root/libiberty
diff options
context:
space:
mode:
Diffstat (limited to 'libiberty')
-rw-r--r--libiberty/fopen_unlocked.c126
-rw-r--r--libiberty/gettimeofday.c30
-rw-r--r--libiberty/pex-common.c472
-rw-r--r--libiberty/pex-one.c43
-rw-r--r--libiberty/pexecute.c121
-rw-r--r--libiberty/strndup.c55
-rw-r--r--libiberty/strverscmp.c157
-rw-r--r--libiberty/testsuite/test-pexecute.c522
-rw-r--r--libiberty/unlink-if-ordinary.c72
-rw-r--r--libiberty/xstrndup.c60
10 files changed, 1658 insertions, 0 deletions
diff --git a/libiberty/fopen_unlocked.c b/libiberty/fopen_unlocked.c
new file mode 100644
index 00000000000..3c3cefed76b
--- /dev/null
+++ b/libiberty/fopen_unlocked.c
@@ -0,0 +1,126 @@
+/* Implement fopen_unlocked and related functions.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Written by Kaveh R. Ghazi <ghazi@caip.rutgers.edu>.
+
+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., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+/*
+
+@deftypefn Extension void unlock_stream (FILE * @var{stream})
+
+If the OS supports it, ensure that the supplied stream is setup to
+avoid any multi-threaded locking. Otherwise leave the @code{FILE}
+pointer unchanged. If the @var{stream} is @code{NULL} do nothing.
+
+@end deftypefn
+
+@deftypefn Extension void unlock_std_streams (void)
+
+If the OS supports it, ensure that the standard I/O streams,
+@code{stdin}, @code{stdout} and @code{stderr} are setup to avoid any
+multi-threaded locking. Otherwise do nothing.
+
+@end deftypefn
+
+@deftypefn Extension {FILE *} fopen_unlocked (const char *@var{path}, const char * @var{mode})
+
+Opens and returns a @code{FILE} pointer via @code{fopen}. If the
+operating system supports it, ensure that the stream is setup to avoid
+any multi-threaded locking. Otherwise return the @code{FILE} pointer
+unchanged.
+
+@end deftypefn
+
+@deftypefn Extension {FILE *} fdopen_unlocked (int @var{fildes}, const char * @var{mode})
+
+Opens and returns a @code{FILE} pointer via @code{fdopen}. If the
+operating system supports it, ensure that the stream is setup to avoid
+any multi-threaded locking. Otherwise return the @code{FILE} pointer
+unchanged.
+
+@end deftypefn
+
+@deftypefn Extension {FILE *} freopen_unlocked (const char * @var{path}, const char * @var{mode}, FILE * @var{stream})
+
+Opens and returns a @code{FILE} pointer via @code{freopen}. If the
+operating system supports it, ensure that the stream is setup to avoid
+any multi-threaded locking. Otherwise return the @code{FILE} pointer
+unchanged.
+
+@end deftypefn
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDIO_EXT_H
+#include <stdio_ext.h>
+#endif
+
+#include "libiberty.h"
+
+/* This is an inline helper function to consolidate attempts to unlock
+ a stream. */
+
+static inline void
+unlock_1 (FILE *const fp ATTRIBUTE_UNUSED)
+{
+#if defined(HAVE___FSETLOCKING) && defined(FSETLOCKING_BYCALLER)
+ if (fp)
+ __fsetlocking (fp, FSETLOCKING_BYCALLER);
+#endif
+}
+
+void
+unlock_stream (FILE *fp)
+{
+ unlock_1 (fp);
+}
+
+void
+unlock_std_streams (void)
+{
+ unlock_1 (stdin);
+ unlock_1 (stdout);
+ unlock_1 (stderr);
+}
+
+FILE *
+fopen_unlocked (const char *path, const char *mode)
+{
+ FILE *const fp = fopen (path, mode);
+ unlock_1 (fp);
+ return fp;
+}
+
+FILE *
+fdopen_unlocked (int fildes, const char *mode)
+{
+ FILE *const fp = fdopen (fildes, mode);
+ unlock_1 (fp);
+ return fp;
+}
+
+FILE *
+freopen_unlocked (const char *path, const char *mode, FILE *stream)
+{
+ FILE *const fp = freopen (path, mode, stream);
+ unlock_1 (fp);
+ return fp;
+}
diff --git a/libiberty/gettimeofday.c b/libiberty/gettimeofday.c
new file mode 100644
index 00000000000..fca16794028
--- /dev/null
+++ b/libiberty/gettimeofday.c
@@ -0,0 +1,30 @@
+#include "config.h"
+#include "libiberty.h"
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+/*
+
+@deftypefn Supplemental int gettimeofday (struct timeval *@var{tp}, void *@var{tz})
+
+Writes the current time to @var{tp}. This implementation requires
+that @var{tz} be NULL. Returns 0 on success, -1 on failure.
+
+@end deftypefn
+
+*/
+
+int
+gettimeofday (struct timeval *tp, void *tz)
+{
+ if (tz)
+ abort ();
+ tp->tv_usec = 0;
+ if (time (&tp->tv_sec) == (time_t) -1)
+ return -1;
+ return 0;
+}
diff --git a/libiberty/pex-common.c b/libiberty/pex-common.c
new file mode 100644
index 00000000000..b2ca6e08ce2
--- /dev/null
+++ b/libiberty/pex-common.c
@@ -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., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, 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 = XNEW (struct pex_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 = XRESIZEVEC (char *, obj->remove, obj->remove_count);
+ 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 = XRESIZEVEC (long, obj->children, obj->count);
+ 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 = XRESIZEVEC (int, obj->status, obj->count);
+ if ((obj->flags & PEX_RECORD_TIMES) != 0)
+ obj->time = XRESIZEVEC (struct pex_time, obj->time, obj->count);
+
+ 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);
+}
diff --git a/libiberty/pex-one.c b/libiberty/pex-one.c
new file mode 100644
index 00000000000..696b8bcc62a
--- /dev/null
+++ b/libiberty/pex-one.c
@@ -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., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, 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;
+}
diff --git a/libiberty/pexecute.c b/libiberty/pexecute.c
new file mode 100644
index 00000000000..cce6e300c5d
--- /dev/null
+++ b/libiberty/pexecute.c
@@ -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., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, 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 = (char *) "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 = (char *) "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 = XNEWVEC (int, idx);
+ 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;
+}
diff --git a/libiberty/strndup.c b/libiberty/strndup.c
new file mode 100644
index 00000000000..9e9b4e2991f
--- /dev/null
+++ b/libiberty/strndup.c
@@ -0,0 +1,55 @@
+/* Implement the strndup function.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Written by Kaveh R. Ghazi <ghazi@caip.rutgers.edu>.
+
+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., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+/*
+
+@deftypefn Extension char* strndup (const char *@var{s}, size_t @var{n})
+
+Returns a pointer to a copy of @var{s} with at most @var{n} characters
+in memory obtained from @code{malloc}, or @code{NULL} if insufficient
+memory was available. The result is always NUL terminated.
+
+@end deftypefn
+
+*/
+
+#include "ansidecl.h"
+#include <stddef.h>
+
+extern size_t strlen (const char*);
+extern PTR malloc (size_t);
+extern PTR memcpy (PTR, const PTR, size_t);
+
+char *
+strndup (const char *s, size_t n)
+{
+ char *result;
+ size_t len = strlen (s);
+
+ if (n < len)
+ len = n;
+
+ result = (char *) malloc (len + 1);
+ if (!result)
+ return 0;
+
+ result[len] = '\0';
+ return (char *) memcpy (result, s, len);
+}
diff --git a/libiberty/strverscmp.c b/libiberty/strverscmp.c
new file mode 100644
index 00000000000..2c6fe8a5910
--- /dev/null
+++ b/libiberty/strverscmp.c
@@ -0,0 +1,157 @@
+/* Compare strings while treating digits characters numerically.
+ Copyright (C) 1997, 2002, 2005 Free Software Foundation, Inc.
+ This file is part of the libiberty library.
+ Contributed by Jean-François Bignolles <bignolle@ecoledoc.ibp.fr>, 1997.
+
+ Libiberty is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include "libiberty.h"
+#include "safe-ctype.h"
+
+/*
+@deftypefun int strverscmp (const char *@var{s1}, const char *@var{s2})
+The @code{strverscmp} function compares the string @var{s1} against
+@var{s2}, considering them as holding indices/version numbers. Return
+value follows the same conventions as found in the @code{strverscmp}
+function. In fact, if @var{s1} and @var{s2} contain no digits,
+@code{strverscmp} behaves like @code{strcmp}.
+
+Basically, we compare strings normally (character by character), until
+we find a digit in each string - then we enter a special comparison
+mode, where each sequence of digits is taken as a whole. If we reach the
+end of these two parts without noticing a difference, we return to the
+standard comparison mode. There are two types of numeric parts:
+"integral" and "fractional" (those begin with a '0'). The types
+of the numeric parts affect the way we sort them:
+
+@itemize @bullet
+@item
+integral/integral: we compare values as you would expect.
+
+@item
+fractional/integral: the fractional part is less than the integral one.
+Again, no surprise.
+
+@item
+fractional/fractional: the things become a bit more complex.
+If the common prefix contains only leading zeroes, the longest part is less
+than the other one; else the comparison behaves normally.
+@end itemize
+
+@smallexample
+strverscmp ("no digit", "no digit")
+ @result{} 0 // @r{same behavior as strcmp.}
+strverscmp ("item#99", "item#100")
+ @result{} <0 // @r{same prefix, but 99 < 100.}
+strverscmp ("alpha1", "alpha001")
+ @result{} >0 // @r{fractional part inferior to integral one.}
+strverscmp ("part1_f012", "part1_f01")
+ @result{} >0 // @r{two fractional parts.}
+strverscmp ("foo.009", "foo.0")
+ @result{} <0 // @r{idem, but with leading zeroes only.}
+@end smallexample
+
+This function is especially useful when dealing with filename sorting,
+because filenames frequently hold indices/version numbers.
+@end deftypefun
+
+*/
+
+/* states: S_N: normal, S_I: comparing integral part, S_F: comparing
+ fractional parts, S_Z: idem but with leading Zeroes only */
+#define S_N 0x0
+#define S_I 0x4
+#define S_F 0x8
+#define S_Z 0xC
+
+/* result_type: CMP: return diff; LEN: compare using len_diff/diff */
+#define CMP 2
+#define LEN 3
+
+
+/* Compare S1 and S2 as strings holding indices/version numbers,
+ returning less than, equal to or greater than zero if S1 is less than,
+ equal to or greater than S2 (for more info, see the Glibc texinfo doc). */
+
+int
+strverscmp (const char *s1, const char *s2)
+{
+ const unsigned char *p1 = (const unsigned char *) s1;
+ const unsigned char *p2 = (const unsigned char *) s2;
+ unsigned char c1, c2;
+ int state;
+ int diff;
+
+ /* Symbol(s) 0 [1-9] others (padding)
+ Transition (10) 0 (01) d (00) x (11) - */
+ static const unsigned int next_state[] =
+ {
+ /* state x d 0 - */
+ /* S_N */ S_N, S_I, S_Z, S_N,
+ /* S_I */ S_N, S_I, S_I, S_I,
+ /* S_F */ S_N, S_F, S_F, S_F,
+ /* S_Z */ S_N, S_F, S_Z, S_Z
+ };
+
+ static const int result_type[] =
+ {
+ /* state x/x x/d x/0 x/- d/x d/d d/0 d/-
+ 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */
+
+ /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
+ CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
+ /* S_I */ CMP, -1, -1, CMP, +1, LEN, LEN, CMP,
+ +1, LEN, LEN, CMP, CMP, CMP, CMP, CMP,
+ /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
+ CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
+ /* S_Z */ CMP, +1, +1, CMP, -1, CMP, CMP, CMP,
+ -1, CMP, CMP, CMP
+ };
+
+ if (p1 == p2)
+ return 0;
+
+ c1 = *p1++;
+ c2 = *p2++;
+ /* Hint: '0' is a digit too. */
+ state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0));
+
+ while ((diff = c1 - c2) == 0 && c1 != '\0')
+ {
+ state = next_state[state];
+ c1 = *p1++;
+ c2 = *p2++;
+ state |= (c1 == '0') + (ISDIGIT (c1) != 0);
+ }
+
+ state = result_type[state << 2 | (((c2 == '0') + (ISDIGIT (c2) != 0)))];
+
+ switch (state)
+ {
+ case CMP:
+ return diff;
+
+ case LEN:
+ while (ISDIGIT (*p1++))
+ if (!ISDIGIT (*p2++))
+ return 1;
+
+ return ISDIGIT (*p2) ? -1 : diff;
+
+ default:
+ return state;
+ }
+}
diff --git a/libiberty/testsuite/test-pexecute.c b/libiberty/testsuite/test-pexecute.c
new file mode 100644
index 00000000000..dfb5413453e
--- /dev/null
+++ b/libiberty/testsuite/test-pexecute.c
@@ -0,0 +1,522 @@
+/* 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; \
+ const char *pex_run_err; \
+ if (trace) \
+ fprintf (stderr, "Line %d: running %s %s\n", \
+ __LINE__, EXECUTABLE, ARGV[0]); \
+ 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);
+}
diff --git a/libiberty/unlink-if-ordinary.c b/libiberty/unlink-if-ordinary.c
new file mode 100644
index 00000000000..c03b4dd7c70
--- /dev/null
+++ b/libiberty/unlink-if-ordinary.c
@@ -0,0 +1,72 @@
+/* unlink-if-ordinary.c - remove link to a file unless it is special
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of the libiberty library. This library 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, or (at your option)
+any later version.
+
+This library 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 GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+As a special exception, if you link this library with files
+compiled with a GNU compiler to produce an executable, this does not cause
+the resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why
+the executable file might be covered by the GNU General Public License. */
+
+/*
+
+@deftypefn Supplemental int unlink_if_ordinary (const char*)
+
+Unlinks the named file, unless it is special (e.g. a device file).
+Returns 0 when the file was unlinked, a negative value (and errno set) when
+there was an error deleting the file, and a positive value if no attempt
+was made to unlink the file because it is special.
+
+@end deftypefn
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "libiberty.h"
+
+#ifndef S_ISLNK
+#ifdef S_IFLNK
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#else
+#define S_ISLNK(m) 0
+#define lstat stat
+#endif
+#endif
+
+int
+unlink_if_ordinary (const char *name)
+{
+ struct stat st;
+
+ if (lstat (name, &st) == 0
+ && (S_ISREG (st.st_mode) || S_ISLNK (st.st_mode)))
+ return unlink (name);
+
+ return 1;
+}
diff --git a/libiberty/xstrndup.c b/libiberty/xstrndup.c
new file mode 100644
index 00000000000..0a41f608ec0
--- /dev/null
+++ b/libiberty/xstrndup.c
@@ -0,0 +1,60 @@
+/* Implement the xstrndup function.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Written by Kaveh R. Ghazi <ghazi@caip.rutgers.edu>.
+
+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., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+/*
+
+@deftypefn Replacement char* xstrndup (const char *@var{s}, size_t @var{n})
+
+Returns a pointer to a copy of @var{s} with at most @var{n} characters
+without fail, using @code{xmalloc} to obtain memory. The result is
+always NUL terminated.
+
+@end deftypefn
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <sys/types.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+# include <strings.h>
+# endif
+#endif
+#include "ansidecl.h"
+#include "libiberty.h"
+
+char *
+xstrndup (const char *s, size_t n)
+{
+ char *result;
+ size_t len = strlen (s);
+
+ if (n < len)
+ len = n;
+
+ result = XNEWVEC (char, len + 1);
+
+ result[len] = '\0';
+ return (char *) memcpy (result, s, len);
+}