aboutsummaryrefslogtreecommitdiffstats
path: root/general.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>1996-08-26 18:22:31 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:49 +0000
commit726f63884db0132f01745f1fb4465e6621088ccf (patch)
tree6c2f7765a890a97e0e513cb539df43283a8f7c4d /general.c
downloadandroid_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.gz
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.bz2
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.zip
Imported from ../bash-1.14.7.tar.gz.
Diffstat (limited to 'general.c')
-rw-r--r--general.c1273
1 files changed, 1273 insertions, 0 deletions
diff --git a/general.c b/general.c
new file mode 100644
index 0000000..9ccfce6
--- /dev/null
+++ b/general.c
@@ -0,0 +1,1273 @@
+/* general.c -- Stuff that is used by all files. */
+
+/* Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992
+ Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash 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.
+
+ Bash 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 Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "config.h" /* includes unistd.h for us */
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include "bashtypes.h"
+#include <sys/param.h>
+#if defined (_POSIX_VERSION)
+# if defined (amiga) && defined (USGr4)
+# define _POSIX_SOURCE
+# endif
+# include <signal.h>
+# if defined (amiga) && defined (USGr4)
+# undef _POSIX_SOURCE
+# endif
+#endif /* _POSIX_VERSION */
+#include "filecntl.h"
+#include "bashansi.h"
+#include "shell.h"
+#include <tilde/tilde.h>
+
+#if !defined (USG) || defined (HAVE_RESOURCE)
+# include <sys/time.h>
+#endif
+
+#include <sys/times.h>
+#include "maxpath.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+/* Make the functions strchr and strrchr if they do not exist. */
+#if !defined (HAVE_STRCHR)
+char *
+strchr (string, c)
+ char *string;
+ int c;
+{
+ register int i;
+
+ for (i = 0; string && string[i]; i++)
+ if (string[i] == c)
+ return ((char *) (string + i));
+
+ return ((char *) NULL);
+}
+
+char *
+strrchr (string, c)
+ char *string;
+ int c;
+{
+ register int i;
+
+ if (string)
+ i = strlen (string) - 1;
+ else
+ i = -1;
+
+ for (; string && i > -1; i--)
+ if (string[i] == c)
+ return ((char *) (string + i));
+
+ return ((char *) NULL);
+}
+#endif /* !HAVE_STRCHR */
+
+/* **************************************************************** */
+/* */
+/* Memory Allocation and Deallocation. */
+/* */
+/* **************************************************************** */
+
+char *
+xmalloc (size)
+ int size;
+{
+ register char *temp = (char *)malloc (size);
+
+ if (!temp)
+ fatal_error ("Out of virtual memory!");
+
+ return (temp);
+}
+
+char *
+xrealloc (pointer, size)
+ GENPTR pointer;
+ int size;
+{
+ char *temp;
+
+ if (!pointer)
+ temp = xmalloc (size);
+ else
+ temp = (char *)realloc (pointer, size);
+
+ if (!temp)
+ fatal_error ("Out of virtual memory!");
+
+ return (temp);
+}
+
+/* Use this as the function to call when adding unwind protects so we
+ don't need to know what free() returns. */
+void
+xfree (string)
+ char *string;
+{
+ free (string);
+}
+
+/* **************************************************************** */
+/* */
+/* Integer to String Conversion */
+/* */
+/* **************************************************************** */
+
+/* Number of characters that can appear in a string representation
+ of an integer. 32 is larger than the string rep of 2^^31 - 1. */
+#define MAX_INT_LEN 32
+
+/* Integer to string conversion. This conses the string; the
+ caller should free it. */
+char *
+itos (i)
+ int i;
+{
+ char *buf, *p, *ret;
+ int negative = 0;
+ unsigned int ui;
+
+ buf = xmalloc (MAX_INT_LEN);
+
+ if (i < 0)
+ {
+ negative++;
+ i = -i;
+ }
+
+ ui = (unsigned int) i;
+
+ buf[MAX_INT_LEN - 1] = '\0';
+ p = &buf[MAX_INT_LEN - 2];
+
+ do
+ *p-- = (ui % 10) + '0';
+ while (ui /= 10);
+
+ if (negative)
+ *p-- = '-';
+
+ ret = savestring (p + 1);
+ free (buf);
+ return (ret);
+}
+
+/* Return non-zero if all of the characters in STRING are digits. */
+int
+all_digits (string)
+ char *string;
+{
+ while (*string)
+ {
+ if (!digit (*string))
+ return (0);
+ else
+ string++;
+ }
+ return (1);
+}
+
+/* atol(3) is not universal */
+long
+string_to_long (s)
+ char *s;
+{
+ long ret = 0L;
+ int neg = 0;
+
+ while (s && *s && whitespace (*s))
+ s++;
+ if (*s == '-' || *s == '+')
+ {
+ neg = *s == '-';
+ s++;
+ }
+ for ( ; s && *s && digit (*s); s++)
+ ret = (ret * 10) + digit_value (*s);
+ return (neg ? -ret : ret);
+}
+
+#if defined (RLIMTYPE)
+RLIMTYPE
+string_to_rlimtype (s)
+ char *s;
+{
+ RLIMTYPE ret = 0;
+ int neg = 0;
+
+ while (s && *s && whitespace (*s))
+ s++;
+ if (*s == '-' || *s == '+')
+ {
+ neg = *s == '-';
+ s++;
+ }
+ for ( ; s && *s && digit (*s); s++)
+ ret = (ret * 10) + digit_value (*s);
+ return (neg ? -ret : ret);
+}
+
+void
+print_rlimtype (n, addnl)
+ RLIMTYPE n;
+ int addnl;
+{
+ char s[sizeof (RLIMTYPE) * 3 + 1];
+ int len = sizeof (RLIMTYPE) * 3 + 1;
+
+ if (n == 0)
+ {
+ printf ("0%s", addnl ? "\n" : "");
+ return;
+ }
+
+ if (n < 0)
+ {
+ putchar ('-');
+ n = -n;
+ }
+
+ s[--len] = '\0';
+ for ( ; n != 0; n /= 10)
+ s[--len] = n % 10 + '0';
+ printf ("%s%s", s + len, addnl ? "\n" : "");
+}
+#endif /* RLIMTYPE */
+
+/* Return 1 if this token is a legal shell `identifier'; that is, it consists
+ solely of letters, digits, and underscores, and does not begin with a
+ digit. */
+int
+legal_identifier (name)
+ char *name;
+{
+ register char *s;
+
+ if (!name || !*name || digit (*name))
+ return (0);
+
+ for (s = name; s && *s; s++)
+ {
+ if (!isletter (*s) && !digit (*s) && (*s != '_'))
+ return (0);
+ }
+ return (1);
+}
+
+/* Make sure that WORD is a valid shell identifier, i.e.
+ does not contain a dollar sign, nor is quoted in any way. Nor
+ does it consist of all digits. If CHECK_WORD is non-zero,
+ the word is checked to ensure that it consists of only letters,
+ digits, and underscores. */
+check_identifier (word, check_word)
+ WORD_DESC *word;
+ int check_word;
+{
+ if (word->dollar_present || word->quoted || all_digits (word->word))
+ {
+ report_error ("`%s' is not a valid identifier", word->word);
+ return (0);
+ }
+ else if (check_word && legal_identifier (word->word) == 0)
+ {
+ report_error ("`%s' is not a valid identifier", word->word);
+ return (0);
+ }
+ else
+ return (1);
+}
+
+/* A function to unset no-delay mode on a file descriptor. Used in shell.c
+ to unset it on the fd passed as stdin. Should be called on stdin if
+ readline gets an EAGAIN or EWOULDBLOCK when trying to read input. */
+
+#if !defined (O_NDELAY)
+# if defined (FNDELAY)
+# define O_NDELAY FNDELAY
+# endif
+#endif /* O_NDELAY */
+
+/* Make sure no-delay mode is not set on file descriptor FD. */
+void
+unset_nodelay_mode (fd)
+ int fd;
+{
+ int flags, set = 0;
+
+ if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
+ return;
+
+#if defined (O_NONBLOCK)
+ if (flags & O_NONBLOCK)
+ {
+ flags &= ~O_NONBLOCK;
+ set++;
+ }
+#endif /* O_NONBLOCK */
+
+#if defined (O_NDELAY)
+ if (flags & O_NDELAY)
+ {
+ flags &= ~O_NDELAY;
+ set++;
+ }
+#endif /* O_NDELAY */
+
+ if (set)
+ fcntl (fd, F_SETFL, flags);
+}
+
+
+/* **************************************************************** */
+/* */
+/* Generic List Functions */
+/* */
+/* **************************************************************** */
+
+/* Call FUNCTION on every member of LIST, a generic list. */
+void
+map_over_list (list, function)
+ GENERIC_LIST *list;
+ Function *function;
+{
+ while (list)
+ {
+ (*function) (list);
+ list = list->next;
+ }
+}
+
+/* Call FUNCTION on every string in WORDS. */
+void
+map_over_words (words, function)
+ WORD_LIST *words;
+ Function *function;
+{
+ while (words)
+ {
+ (*function)(words->word->word);
+ words = words->next;
+ }
+}
+
+/* Reverse the chain of structures in LIST. Output the new head
+ of the chain. You should always assign the output value of this
+ function to something, or you will lose the chain. */
+GENERIC_LIST *
+reverse_list (list)
+ GENERIC_LIST *list;
+{
+ register GENERIC_LIST *next, *prev = (GENERIC_LIST *)NULL;
+
+ while (list)
+ {
+ next = list->next;
+ list->next = prev;
+ prev = list;
+ list = next;
+ }
+ return (prev);
+}
+
+/* Return the number of elements in LIST, a generic list. */
+int
+list_length (list)
+ GENERIC_LIST *list;
+{
+ register int i;
+
+ for (i = 0; list; list = list->next, i++);
+ return (i);
+}
+
+/* A global variable which acts as a sentinel for an `error' list return. */
+GENERIC_LIST global_error_list;
+
+/* Delete the element of LIST which satisfies the predicate function COMPARER.
+ Returns the element that was deleted, so you can dispose of it, or -1 if
+ the element wasn't found. COMPARER is called with the list element and
+ then ARG. Note that LIST contains the address of a variable which points
+ to the list. You might call this function like this:
+
+ SHELL_VAR *elt = delete_element (&variable_list, check_var_has_name, "foo");
+ dispose_variable (elt);
+*/
+GENERIC_LIST *
+delete_element (list, comparer, arg)
+ GENERIC_LIST **list;
+ Function *comparer;
+ char *arg;
+{
+ register GENERIC_LIST *prev = (GENERIC_LIST *)NULL;
+ register GENERIC_LIST *temp = *list;
+
+ while (temp)
+ {
+ if ((*comparer) (temp, arg))
+ {
+ if (prev)
+ prev->next = temp->next;
+ else
+ *list = temp->next;
+ return (temp);
+ }
+ prev = temp;
+ temp = temp->next;
+ }
+ return ((GENERIC_LIST *)&global_error_list);
+}
+
+/* Find NAME in ARRAY. Return the index of NAME, or -1 if not present.
+ ARRAY should be NULL terminated. */
+int
+find_name_in_list (name, array)
+ char *name, **array;
+{
+ int i;
+
+ for (i = 0; array[i]; i++)
+ if (strcmp (name, array[i]) == 0)
+ return (i);
+
+ return (-1);
+}
+
+/* Return the length of ARRAY, a NULL terminated array of char *. */
+int
+array_len (array)
+ char **array;
+{
+ register int i;
+ for (i = 0; array[i]; i++);
+ return (i);
+}
+
+/* Free the contents of ARRAY, a NULL terminated array of char *. */
+void
+free_array (array)
+ char **array;
+{
+ register int i = 0;
+
+ if (!array) return;
+
+ while (array[i])
+ free (array[i++]);
+ free (array);
+}
+
+/* Allocate and return a new copy of ARRAY and its contents. */
+char **
+copy_array (array)
+ char **array;
+{
+ register int i;
+ int len;
+ char **new_array;
+
+ len = array_len (array);
+
+ new_array = (char **)xmalloc ((len + 1) * sizeof (char *));
+ for (i = 0; array[i]; i++)
+ new_array[i] = savestring (array[i]);
+ new_array[i] = (char *)NULL;
+
+ return (new_array);
+}
+
+/* Comparison routine for use with qsort() on arrays of strings. */
+int
+qsort_string_compare (s1, s2)
+ register char **s1, **s2;
+{
+ int result;
+
+ if ((result = **s1 - **s2) == 0)
+ result = strcmp (*s1, *s2);
+
+ return (result);
+}
+
+/* Append LIST2 to LIST1. Return the header of the list. */
+GENERIC_LIST *
+list_append (head, tail)
+ GENERIC_LIST *head, *tail;
+{
+ register GENERIC_LIST *t_head = head;
+
+ if (!t_head)
+ return (tail);
+
+ while (t_head->next)
+ t_head = t_head->next;
+ t_head->next = tail;
+ return (head);
+}
+
+/* Some random string stuff. */
+
+/* Remove all leading whitespace from STRING. This includes
+ newlines. STRING should be terminated with a zero. */
+void
+strip_leading (string)
+ char *string;
+{
+ char *start = string;
+
+ while (*string && (whitespace (*string) || *string == '\n'))
+ string++;
+
+ if (string != start)
+ {
+ int len = strlen (string);
+ FASTCOPY (string, start, len);
+ start[len] = '\0';
+ }
+}
+
+/* Remove all trailing whitespace from STRING. This includes
+ newlines. If NEWLINES_ONLY is non-zero, only trailing newlines
+ are removed. STRING should be terminated with a zero. */
+void
+strip_trailing (string, newlines_only)
+ char *string;
+ int newlines_only;
+{
+ int len = strlen (string) - 1;
+
+ while (len >= 0)
+ {
+ if ((newlines_only && string[len] == '\n') ||
+ (!newlines_only && whitespace (string[len])))
+ len--;
+ else
+ break;
+ }
+ string[len + 1] = '\0';
+}
+
+/* Canonicalize PATH, and return a new path. The new path differs from PATH
+ in that:
+ Multple `/'s are collapsed to a single `/'.
+ Leading `./'s and trailing `/.'s are removed.
+ Trailing `/'s are removed.
+ Non-leading `../'s and trailing `..'s are handled by removing
+ portions of the path. */
+char *
+canonicalize_pathname (path)
+ char *path;
+{
+ register int i, start;
+ char stub_char;
+ char *result;
+
+ /* The result cannot be larger than the input PATH. */
+ result = savestring (path);
+
+ stub_char = (*path == '/') ? '/' : '.';
+
+ /* Walk along RESULT looking for things to compact. */
+ i = 0;
+ while (1)
+ {
+ if (!result[i])
+ break;
+
+ while (result[i] && result[i] != '/')
+ i++;
+
+ start = i++;
+
+ /* If we didn't find any slashes, then there is nothing left to do. */
+ if (!result[start])
+ break;
+
+ /* Handle multiple `/'s in a row. */
+ while (result[i] == '/')
+ i++;
+
+#if !defined (apollo)
+ if ((start + 1) != i)
+#else
+ if ((start + 1) != i && (start != 0 || i != 2))
+#endif /* apollo */
+ {
+ strcpy (result + start + 1, result + i);
+ i = start + 1;
+ }
+
+#if 0
+ /* Handle backslash-quoted `/'. */
+ if (start > 0 && result[start - 1] == '\\')
+ continue;
+#endif
+
+ /* Check for trailing `/'. */
+ if (start && !result[i])
+ {
+ zero_last:
+ result[--i] = '\0';
+ break;
+ }
+
+ /* Check for `../', `./' or trailing `.' by itself. */
+ if (result[i] == '.')
+ {
+ /* Handle trailing `.' by itself. */
+ if (!result[i + 1])
+ goto zero_last;
+
+ /* Handle `./'. */
+ if (result[i + 1] == '/')
+ {
+ strcpy (result + i, result + i + 1);
+ i = (start < 0) ? 0 : start;
+ continue;
+ }
+
+ /* Handle `../' or trailing `..' by itself. */
+ if (result[i + 1] == '.' &&
+ (result[i + 2] == '/' || !result[i + 2]))
+ {
+ while (--start > -1 && result[start] != '/');
+ strcpy (result + start + 1, result + i + 2);
+ i = (start < 0) ? 0 : start;
+ continue;
+ }
+ }
+ }
+
+ if (!*result)
+ {
+ *result = stub_char;
+ result[1] = '\0';
+ }
+ return (result);
+}
+
+/* Turn STRING (a pathname) into an absolute pathname, assuming that
+ DOT_PATH contains the symbolic location of `.'. This always
+ returns a new string, even if STRING was an absolute pathname to
+ begin with. */
+char *
+make_absolute (string, dot_path)
+ char *string, *dot_path;
+{
+ char *result;
+ int result_len;
+
+ if (!dot_path || *string == '/')
+ result = savestring (string);
+ else
+ {
+ if (dot_path && dot_path[0])
+ {
+ result = xmalloc (2 + strlen (dot_path) + strlen (string));
+ strcpy (result, dot_path);
+ result_len = strlen (result);
+ if (result[result_len - 1] != '/')
+ {
+ result[result_len++] = '/';
+ result[result_len] = '\0';
+ }
+ }
+ else
+ {
+ result = xmalloc (3 + strlen (string));
+ result[0] = '.'; result[1] = '/'; result[2] = '\0';
+ result_len = 2;
+ }
+
+ strcpy (result + result_len, string);
+ }
+
+ return (result);
+}
+
+/* Return 1 if STRING contains an absolute pathname, else 0. */
+int
+absolute_pathname (string)
+ char *string;
+{
+ if (!string || !*string)
+ return (0);
+
+ if (*string == '/')
+ return (1);
+
+ if (*string++ == '.')
+ {
+ if (!*string || *string == '/')
+ return (1);
+
+ if (*string == '.' && (string[1] == '\0' || string[1] == '/'))
+ return (1);
+ }
+ return (0);
+}
+
+/* Return 1 if STRING is an absolute program name; it is absolute if it
+ contains any slashes. This is used to decide whether or not to look
+ up through $PATH. */
+int
+absolute_program (string)
+ char *string;
+{
+ return ((char *)strchr (string, '/') != (char *)NULL);
+}
+
+/* Return the `basename' of the pathname in STRING (the stuff after the
+ last '/'). If STRING is not a full pathname, simply return it. */
+char *
+base_pathname (string)
+ char *string;
+{
+ char *p;
+
+ if (!absolute_pathname (string))
+ return (string);
+
+ p = (char *)strrchr (string, '/');
+ if (p)
+ return (++p);
+ else
+ return (string);
+}
+
+/* Return the full pathname of FILE. Easy. Filenames that begin
+ with a '/' are returned as themselves. Other filenames have
+ the current working directory prepended. A new string is
+ returned in either case. */
+char *
+full_pathname (file)
+ char *file;
+{
+ char *disposer;
+
+ if (*file == '~')
+ file = tilde_expand (file);
+ else
+ file = savestring (file);
+
+ if ((*file == '/') && absolute_pathname (file))
+ return (file);
+
+ disposer = file;
+
+ {
+ char *current_dir = xmalloc (2 + MAXPATHLEN + strlen (file));
+ int dlen;
+ if (getwd (current_dir) == 0)
+ {
+ report_error (current_dir);
+ free (current_dir);
+ return ((char *)NULL);
+ }
+ dlen = strlen (current_dir);
+ current_dir[dlen++] = '/';
+
+ /* Turn /foo/./bar into /foo/bar. */
+ if (file[0] == '.' && file[1] == '/')
+ file += 2;
+
+ strcpy (current_dir + dlen, file);
+ free (disposer);
+ return (current_dir);
+ }
+}
+
+#if !defined (HAVE_STRCASECMP)
+
+#if !defined (to_upper)
+# define to_upper(c) (islower(c) ? toupper(c) : (c))
+#endif /* to_upper */
+
+/* Compare at most COUNT characters from string1 to string2. Case
+ doesn't matter. */
+int
+strnicmp (string1, string2, count)
+ char *string1, *string2;
+ int count;
+{
+ register char ch1, ch2;
+
+ while (count)
+ {
+ ch1 = *string1++;
+ ch2 = *string2++;
+ if (to_upper(ch1) == to_upper(ch2))
+ count--;
+ else
+ break;
+ }
+ return (count);
+}
+
+/* strcmp (), but caseless. */
+int
+stricmp (string1, string2)
+ char *string1, *string2;
+{
+ register char ch1, ch2;
+
+ while (*string1 && *string2)
+ {
+ ch1 = *string1++;
+ ch2 = *string2++;
+ if (to_upper(ch1) != to_upper(ch2))
+ return (1);
+ }
+ return (*string1 - *string2);
+}
+#endif /* !HAVE_STRCASECMP */
+
+/* Determine if s2 occurs in s1. If so, return a pointer to the
+ match in s1. The compare is case insensitive. */
+char *
+strindex (s1, s2)
+ char *s1, *s2;
+{
+ register int i, l = strlen (s2);
+ register int len = strlen (s1);
+
+ for (i = 0; (len - i) >= l; i++)
+ if (strnicmp (s1 + i, s2, l) == 0)
+ return (s1 + i);
+ return ((char *)NULL);
+}
+
+/* Set the environment variables $LINES and $COLUMNS in response to
+ a window size change. */
+void
+set_lines_and_columns (lines, cols)
+ int lines, cols;
+{
+ char *val;
+
+ val = itos (lines);
+ bind_variable ("LINES", val);
+ free (val);
+
+ val = itos (cols);
+ bind_variable ("COLUMNS", val);
+ free (val);
+}
+
+/* A wrapper for bcopy that can be prototyped in general.h */
+void
+xbcopy (s, d, n)
+ char *s, *d;
+ int n;
+{
+ FASTCOPY (s, d, n);
+}
+
+/* Return a string corresponding to the error number E. From
+ the ANSI C spec. */
+#if defined (strerror)
+# undef strerror
+#endif
+
+#if !defined (HAVE_STRERROR)
+char *
+strerror (e)
+ int e;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ static char emsg[40];
+
+ if (e > 0 && e < sys_nerr)
+ return (sys_errlist[e]);
+ else
+ {
+ sprintf (emsg, "Unknown error %d", e);
+ return (&emsg[0]);
+ }
+}
+#endif /* HAVE_STRERROR */
+
+#if (defined (USG) && !defined (HAVE_TIMEVAL)) || defined (Minix)
+# define TIMEVAL_MISSING
+#endif
+
+#if !defined (TIMEVAL_MISSING) || defined (HAVE_RESOURCE)
+/* Print the contents of a struct timeval * in a standard way. */
+void
+print_timeval (tvp)
+ struct timeval *tvp;
+{
+ int minutes, seconds_fraction;
+ long seconds;
+
+ seconds = tvp->tv_sec;
+
+ seconds_fraction = tvp->tv_usec % 1000000;
+ seconds_fraction = (seconds_fraction * 100) / 1000000;
+
+ minutes = seconds / 60;
+ seconds %= 60;
+
+ printf ("%0dm%0ld.%02ds", minutes, seconds, seconds_fraction);
+}
+#endif /* !TIMEVAL_MISSING || HAVE_RESOURCE */
+
+/* Print the time defined by a time_t (returned by the `times' and `time'
+ system calls) in a standard way. This is scaled in terms of HZ, which
+ is what is returned by the `times' call. */
+
+#if !defined (BrainDeath)
+# if !defined (HZ)
+# if defined (USG)
+# define HZ 100 /* From my Sys V.3.2 manual for times(2) */
+# else
+# define HZ 60 /* HZ is always 60 on BSD systems */
+# endif /* USG */
+# endif /* HZ */
+
+void
+print_time_in_hz (t)
+ time_t t;
+{
+ int minutes, seconds_fraction;
+ long seconds;
+
+ seconds_fraction = t % HZ;
+ seconds_fraction = (seconds_fraction * 100) / HZ;
+
+ seconds = t / HZ;
+
+ minutes = seconds / 60;
+ seconds %= 60;
+
+ printf ("%0dm%0ld.%02ds", minutes, seconds, seconds_fraction);
+}
+#endif /* BrainDeath */
+
+#if !defined (HAVE_DUP2)
+/* Replacement for dup2 (), for those systems which either don't have it,
+ or supply one with broken behaviour. */
+int
+dup2 (fd1, fd2)
+ int fd1, fd2;
+{
+ extern int getdtablesize ();
+ int saved_errno, r;
+
+ /* If FD1 is not a valid file descriptor, then return immediately with
+ an error. */
+ if (fcntl (fd1, F_GETFL, 0) == -1)
+ return (-1);
+
+ if (fd2 < 0 || fd2 >= getdtablesize ())
+ {
+ errno = EBADF;
+ return (-1);
+ }
+
+ if (fd1 == fd2)
+ return (0);
+
+ saved_errno = errno;
+
+ (void) close (fd2);
+ r = fcntl (fd1, F_DUPFD, fd2);
+
+ if (r >= 0)
+ errno = saved_errno;
+ else
+ if (errno == EINVAL)
+ errno = EBADF;
+
+ /* Force the new file descriptor to remain open across exec () calls. */
+ SET_OPEN_ON_EXEC (fd2);
+ return (r);
+}
+#endif /* !HAVE_DUP2 */
+
+/*
+ * Return the total number of available file descriptors.
+ *
+ * On some systems, like 4.2BSD and its descendents, there is a system call
+ * that returns the size of the descriptor table: getdtablesize(). There are
+ * lots of ways to emulate this on non-BSD systems.
+ *
+ * On System V.3, this can be obtained via a call to ulimit:
+ * return (ulimit(4, 0L));
+ *
+ * On other System V systems, NOFILE is defined in /usr/include/sys/param.h
+ * (this is what we assume below), so we can simply use it:
+ * return (NOFILE);
+ *
+ * On POSIX systems, there are specific functions for retrieving various
+ * configuration parameters:
+ * return (sysconf(_SC_OPEN_MAX));
+ *
+ */
+
+#if !defined (USG) && !defined (HPUX) && !defined (HAVE_GETDTABLESIZE)
+# define HAVE_GETDTABLESIZE
+#endif /* !USG && !HPUX && !HAVE_GETDTABLESIZE */
+
+#if defined (hppa) && (defined (hpux_8) || defined (hpux_9))
+# undef HAVE_GETDTABLESIZE
+#endif /* hppa && hpux_8 */
+
+#if !defined (HAVE_GETDTABLESIZE)
+int
+getdtablesize ()
+{
+# if defined (_POSIX_VERSION) && defined (_SC_OPEN_MAX)
+ return (sysconf(_SC_OPEN_MAX)); /* Posix systems use sysconf */
+# else /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
+# if defined (USGr3)
+ return (ulimit (4, 0L)); /* System V.3 systems use ulimit(4, 0L) */
+# else /* !USGr3 */
+# if defined (NOFILE) /* Other systems use NOFILE */
+ return (NOFILE);
+# else /* !NOFILE */
+ return (20); /* XXX - traditional value is 20 */
+# endif /* !NOFILE */
+# endif /* !USGr3 */
+# endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
+}
+#endif /* !HAVE_GETDTABLESIZE */
+
+#if defined (USG)
+
+#if !defined (HAVE_BCOPY)
+bcopy (s,d,n) char *d,*s; { FASTCOPY (s, d, n); }
+bzero (s,n) char *s; int n; { memset(s, '\0', n); }
+#endif /* !HAVE_BCOPY */
+
+#if !defined (HAVE_GETHOSTNAME)
+#include <sys/utsname.h>
+int
+gethostname (name, namelen)
+ char *name;
+ int namelen;
+{
+ int i;
+ struct utsname ut;
+
+ --namelen;
+
+ uname (&ut);
+ i = strlen (ut.nodename) + 1;
+ strncpy (name, ut.nodename, i < namelen ? i : namelen);
+ name[namelen] = '\0';
+ return (0);
+}
+#endif /* !HAVE_GETHOSTNAME */
+#endif /* USG */
+
+#if !defined (HAVE_GETWD)
+char *
+getwd (string)
+ char *string;
+{
+ extern char *getcwd ();
+ char *result;
+
+ result = getcwd (string, MAXPATHLEN);
+ if (result == NULL)
+ strcpy (string, "getwd: cannot access parent directories");
+ return (result);
+}
+#endif /* !HAVE_GETWD */
+
+/* A slightly related function. Get the prettiest name of this
+ directory possible. */
+static char tdir[MAXPATHLEN];
+
+/* Return a pretty pathname. If the first part of the pathname is
+ the same as $HOME, then replace that with `~'. */
+char *
+polite_directory_format (name)
+ char *name;
+{
+ char *home = get_string_value ("HOME");
+ int l = home ? strlen (home) : 0;
+
+ if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
+ {
+ strcpy (tdir + 1, name + l);
+ tdir[0] = '~';
+ return (tdir);
+ }
+ else
+ return (name);
+}
+
+#if defined (NO_READ_RESTART_ON_SIGNAL)
+static char localbuf[128];
+static int local_index = 0, local_bufused = 0;
+
+/* Posix and USG systems do not guarantee to restart read () if it is
+ interrupted by a signal. We do the read ourselves, and restart it
+ if it returns EINTR. */
+int
+getc_with_restart (stream)
+ FILE *stream;
+{
+ /* Try local buffering to reduce the number of read(2) calls. */
+ if (local_index == local_bufused || local_bufused == 0)
+ {
+ while (1)
+ {
+ local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
+ if (local_bufused > 0)
+ break;
+ else if (local_bufused == 0 || errno != EINTR)
+ {
+ local_index = 0;
+ return EOF;
+ }
+ }
+ local_index = 0;
+ }
+ return (localbuf[local_index++]);
+}
+
+int
+ungetc_with_restart (c, fp)
+ int c;
+ FILE *fp;
+{
+ if (local_index == 0 || local_bufused == 0 || c == EOF)
+ return EOF;
+ return (localbuf[--local_index] = c);
+}
+
+#endif /* NO_READ_RESTART_ON_SIGNAL */
+
+#if defined (USG) || defined (AIX) || (defined (_POSIX_VERSION) && defined (Ultrix))
+/* USG and strict POSIX systems do not have killpg (). But we use it in
+ jobs.c, nojobs.c and some of the builtins. This can also be redefined
+ as a macro if necessary. */
+#if !defined (_POSIX_VERSION)
+# define pid_t int
+#endif /* _POSIX_VERSION */
+
+int
+killpg (pgrp, sig)
+ pid_t pgrp;
+ int sig;
+{
+ return (kill (-pgrp, sig));
+}
+#endif /* USG || AIX || (_POSIX_VERSION && Ultrix) */
+
+/* **************************************************************** */
+/* */
+/* Tilde Initialization and Expansion */
+/* */
+/* **************************************************************** */
+
+/* If tilde_expand hasn't been able to expand the text, perhaps it
+ is a special shell expansion. This function is installed as the
+ tilde_expansion_failure_hook. It knows how to expand ~- and ~+. */
+static char *
+bash_tilde_expand (text)
+ char *text;
+{
+ char *result = (char *)NULL;
+
+ if (!text[1])
+ {
+ if (*text == '+')
+ result = get_string_value ("PWD");
+ else if (*text == '-')
+ result = get_string_value ("OLDPWD");
+ }
+
+ if (result)
+ result = savestring (result);
+
+ return (result);
+}
+
+/* Initialize the tilde expander. In Bash, we handle `~-' and `~+', as
+ well as handling special tilde prefixes; `:~" and `=~' are indications
+ that we should do tilde expansion. */
+void
+tilde_initialize ()
+{
+ static int times_called = 0;
+
+ /* Tell the tilde expander that we want a crack if it fails. */
+ tilde_expansion_failure_hook = (CPFunction *)bash_tilde_expand;
+
+ /* Tell the tilde expander about special strings which start a tilde
+ expansion, and the special strings that end one. Only do this once.
+ tilde_initialize () is called from within bashline_reinitialize (). */
+ if (times_called == 0)
+ {
+ tilde_additional_prefixes = (char **)xmalloc (3 * sizeof (char *));
+ tilde_additional_prefixes[0] = "=~";
+ tilde_additional_prefixes[1] = ":~";
+ tilde_additional_prefixes[2] = (char *)NULL;
+
+ tilde_additional_suffixes = (char **)xmalloc (3 * sizeof (char *));
+ tilde_additional_suffixes[0] = ":";
+ tilde_additional_suffixes[1] = "=~";
+ tilde_additional_suffixes[2] = (char *)NULL;
+ }
+ times_called++;
+}
+
+#if defined (_POSIX_VERSION)
+
+#if !defined (SA_INTERRUPT)
+# define SA_INTERRUPT 0
+#endif
+
+#if !defined (SA_RESTART)
+# define SA_RESTART 0
+#endif
+
+SigHandler *
+set_signal_handler (sig, handler)
+ int sig;
+ SigHandler *handler;
+{
+ struct sigaction act, oact;
+
+ act.sa_handler = handler;
+ act.sa_flags = 0;
+#if 0
+ if (sig == SIGALRM)
+ act.sa_flags |= SA_INTERRUPT; /* XXX */
+ else
+ act.sa_flags |= SA_RESTART; /* XXX */
+#endif
+ sigemptyset (&act.sa_mask);
+ sigemptyset (&oact.sa_mask);
+ sigaction (sig, &act, &oact);
+ return (oact.sa_handler);
+}
+#endif /* _POSIX_VERSION */