diff options
Diffstat (limited to 'libiberty')
-rw-r--r-- | libiberty/fopen_unlocked.c | 126 | ||||
-rw-r--r-- | libiberty/gettimeofday.c | 30 | ||||
-rw-r--r-- | libiberty/pex-common.c | 472 | ||||
-rw-r--r-- | libiberty/pex-one.c | 43 | ||||
-rw-r--r-- | libiberty/pexecute.c | 121 | ||||
-rw-r--r-- | libiberty/strndup.c | 55 | ||||
-rw-r--r-- | libiberty/strverscmp.c | 157 | ||||
-rw-r--r-- | libiberty/testsuite/test-pexecute.c | 522 | ||||
-rw-r--r-- | libiberty/unlink-if-ordinary.c | 72 | ||||
-rw-r--r-- | libiberty/xstrndup.c | 60 |
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); +} |