summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>1992-11-01 05:44:29 +0000
committerJim Meyering <jim@meyering.net>1992-11-01 05:44:29 +0000
commitccbd1d7dc5189f4637468a8136f672e60ee0e531 (patch)
treef07938daa9443c4a699efa77d88eb9eb2c2e663b /src
parent144b82c6c22abaa2a3247dc33b286662a7aa90d9 (diff)
downloadcoreutils-ccbd1d7dc5189f4637468a8136f672e60ee0e531.tar.xz
Initial revision
Diffstat (limited to 'src')
-rw-r--r--src/basename.c78
-rw-r--r--src/date.c190
-rw-r--r--src/dirname.c57
-rw-r--r--src/echo.c179
-rw-r--r--src/env.c168
-rw-r--r--src/expr.c672
-rwxr-xr-xsrc/groups.sh31
-rw-r--r--src/id.c346
-rw-r--r--src/logname.c43
-rw-r--r--src/nice.c150
-rwxr-xr-xsrc/nohup.sh48
-rw-r--r--src/pathchk.c332
-rw-r--r--src/printenv.c69
-rw-r--r--src/printf.c481
-rw-r--r--src/sleep.c84
-rw-r--r--src/stty.c1241
-rw-r--r--src/su.c519
-rw-r--r--src/tee.c155
-rw-r--r--src/test.c1054
-rw-r--r--src/tty.c88
-rw-r--r--src/uname.c155
-rw-r--r--src/who.c434
-rw-r--r--src/whoami.c49
-rw-r--r--src/yes.c39
24 files changed, 6662 insertions, 0 deletions
diff --git a/src/basename.c b/src/basename.c
new file mode 100644
index 000000000..767dbafc6
--- /dev/null
+++ b/src/basename.c
@@ -0,0 +1,78 @@
+/* basename -- strip directory and suffix from filenames
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: basename name [suffix]
+ NAME is a pathname; SUFFIX is a suffix to strip from it.
+
+ basename /usr/foo/lossage/functions.l
+ => functions.l
+ basename /usr/foo/lossage/functions.l .l
+ => functions
+ basename functions.lisp p
+ => functions.lis */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+char *basename ();
+void remove_suffix ();
+void strip_trailing_slashes ();
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *name;
+
+ if (argc == 1 || argc > 3)
+ {
+ fprintf (stderr, "Usage: %s name [suffix]\n", argv[0]);
+ exit (1);
+ }
+
+ strip_trailing_slashes (argv[1]);
+
+ name = basename (argv[1]);
+
+ if (argc == 3)
+ remove_suffix (name, argv[2]);
+
+ puts (name);
+
+ exit (0);
+}
+
+/* Remove SUFFIX from the end of NAME if it is there, unless NAME
+ consists entirely of SUFFIX. */
+
+void
+remove_suffix (name, suffix)
+ register char *name, *suffix;
+{
+ register char *np, *sp;
+
+ np = name + strlen (name);
+ sp = suffix + strlen (suffix);
+
+ while (np > name && sp > suffix)
+ if (*--np != *--sp)
+ return;
+ if (np > name)
+ *np = '\0';
+}
diff --git a/src/date.c b/src/date.c
new file mode 100644
index 000000000..bb4c5816f
--- /dev/null
+++ b/src/date.c
@@ -0,0 +1,190 @@
+/* date - print or set the system date and time
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Options:
+ -d DATESTR Display the date DATESTR.
+ -s DATESTR Set the date to DATESTR.
+ -u Display or set the date in universal instead of local time.
+ +FORMAT Specify custom date output format, described below.
+ MMDDhhmm[[CC]YY][.ss] Set the date in the format described below.
+
+ If one non-option argument is given, it is used as the date to which
+ to set the system clock, and must have the format:
+ MM month (01..12)
+ DD day in month (01..31)
+ hh hour (00..23)
+ mm minute (00..59)
+ CC first 2 digits of year (optional, defaults to current) (00..99)
+ YY last 2 digits of year (optional, defaults to current) (00..99)
+ ss second (00..61)
+
+ If a non-option argument that starts with a `+' is specified, it
+ is used to control the format in which the date is printed; it
+ can contain any of the `%' substitutions allowed by the strftime
+ function. A newline is always added at the end of the output.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+/* This is portable and avoids bringing in all of the ctype stuff. */
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+#ifdef TM_IN_SYS_TIME
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+#ifndef STDC_HEADERS
+time_t mktime ();
+size_t strftime ();
+time_t time ();
+#endif
+
+int putenv ();
+int stime ();
+
+char *xrealloc ();
+time_t get_date ();
+time_t posixtime ();
+void error ();
+void show_date ();
+void usage ();
+
+/* putenv string to use Universal Coordinated Time.
+ POSIX.2 says it should be "TZ=UCT0" or "TZ=GMT0". */
+#ifndef TZ_UCT
+#if defined(hpux) || defined(__hpux__) || defined(ultrix) || defined(__ultrix__) || defined(USG)
+#define TZ_UCT "TZ=GMT0"
+#else
+#define TZ_UCT "TZ="
+#endif
+#endif
+
+/* The name this program was run with, for error messages. */
+char *program_name;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc;
+ char *datestr = NULL;
+ time_t when;
+ int set_date = 0;
+ int universal_time = 0;
+
+ program_name = argv[0];
+
+ while ((optc = getopt (argc, argv, "d:s:u")) != EOF)
+ switch (optc)
+ {
+ case 'd':
+ datestr = optarg;
+ break;
+ case 's':
+ datestr = optarg;
+ set_date = 1;
+ break;
+ case 'u':
+ universal_time = 1;
+ break;
+ default:
+ usage ();
+ }
+
+ if (argc - optind > 1)
+ usage ();
+
+ if (universal_time && putenv (TZ_UCT) != 0)
+ error (1, 0, "virtual memory exhausted");
+
+ time (&when);
+
+ if (datestr)
+ when = get_date (datestr, NULL);
+
+ if (argc - optind == 1 && argv[optind][0] != '+')
+ {
+ when = posixtime (argv[optind]);
+ set_date = 1;
+ }
+
+ if (when == -1)
+ error (1, 0, "invalid date");
+
+ if (set_date && stime (&when) == -1)
+ error (0, errno, "cannot set date");
+
+ if (argc - optind == 1 && argv[optind][0] == '+')
+ show_date (argv[optind] + 1, when);
+ else
+ show_date ((char *) NULL, when);
+
+ exit (0);
+}
+
+/* Display the date and/or time in WHEN according to the format specified
+ in FORMAT, followed by a newline. If FORMAT is NULL, use the
+ standard output format (ctime style but with a timezone inserted). */
+
+void
+show_date (format, when)
+ char *format;
+ time_t when;
+{
+ struct tm *tm;
+ char *out = NULL;
+ size_t out_length = 0;
+
+ tm = localtime (&when);
+
+ if (format == NULL)
+ /* Print the date in the default format. Vanilla ANSI C strftime
+ doesn't support %e, but POSIX requires it. If you don't use
+ a GNU strftime, make sure yours supports %e. */
+ format = "%a %b %e %H:%M:%S %Z %Y";
+ else if (*format == '\0')
+ {
+ printf ("\n");
+ return;
+ }
+
+ do
+ {
+ out_length += 200;
+ out = (char *) xrealloc (out, out_length);
+ }
+ while (strftime (out, out_length, format, tm) == 0);
+
+ printf ("%s\n", out);
+ free (out);
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-u] [-d datestr] [-s datestr] [+FORMAT] [MMDDhhmm[[CC]YY][.ss]]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/dirname.c b/src/dirname.c
new file mode 100644
index 000000000..05612353a
--- /dev/null
+++ b/src/dirname.c
@@ -0,0 +1,57 @@
+/* dirname -- strip filename suffix from pathname
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie and Jim Meyering. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+void strip_trailing_slashes ();
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register char *path;
+ register char *slash;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Usage: %s path\n", argv[0]);
+ exit (1);
+ }
+
+ path = argv[1];
+ strip_trailing_slashes (path);
+
+ slash = rindex (path, '/');
+ if (slash == NULL)
+ path = ".";
+ else
+ {
+ /* Remove any trailing slashes and final element. */
+ while (slash > path && *slash == '/')
+ --slash;
+ slash[1] = 0;
+ }
+ puts (path);
+
+ exit (0);
+}
+
diff --git a/src/echo.c b/src/echo.c
new file mode 100644
index 000000000..cae62ce8d
--- /dev/null
+++ b/src/echo.c
@@ -0,0 +1,179 @@
+/* echo.c, taken from Bash.
+Copyright (C) 1987, 1989, 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 <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+/* echo [-neE] [arg ...]
+Output the ARGs. If -n is specified, the trailing newline is
+suppressed. If the -e option is given, interpretation of the
+following backslash-escaped characters is turned on:
+ \a alert (bell)
+ \b backspace
+ \c suppress trailing newline
+ \f form feed
+ \n new line
+ \r carriage return
+ \t horizontal tab
+ \v vertical tab
+ \\ backslash
+ \num the character whose ASCII code is NUM (octal).
+
+You can explicitly turn off the interpretation of the above characters
+on System V systems with the -E option.
+*/
+
+#define V9_ECHO
+
+#if defined (V9_ECHO)
+# if defined (USG)
+# define VALID_ECHO_OPTIONS "neE"
+# else
+# define VALID_ECHO_OPTIONS "ne"
+# endif /* !USG */
+#else /* !V9_ECHO */
+# define VALID_ECHO_OPTIONS "n"
+#endif /* !V9_ECHO */
+
+/* Print the words in LIST to standard output. If the first word is
+ `-n', then don't print a trailing newline. We also support the
+ echo syntax from Version 9 unix systems. */
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int display_return = 1, do_v9 = 0;
+
+/* System V machines already have a /bin/sh with a v9 behaviour. We
+ use the identical behaviour for these machines so that the
+ existing system shell scripts won't barf. */
+#if defined (V9_ECHO) && defined (USG)
+ do_v9 = 1;
+#endif
+
+ --argc;
+ ++argv;
+
+ while (argc > 0 && *argv[0] == '-')
+ {
+ register char *temp;
+ register int i;
+
+ /* If it appears that we are handling options, then make sure that
+ all of the options specified are actually valid. Otherwise, the
+ string should just be echoed. */
+ temp = argv[0] + 1;
+
+ for (i = 0; temp[i]; i++)
+ {
+ if (rindex (VALID_ECHO_OPTIONS, temp[i]) == 0)
+ goto just_echo;
+ }
+
+ if (!*temp)
+ goto just_echo;
+
+ /* All of the options in TEMP are valid options to ECHO.
+ Handle them. */
+ while (*temp)
+ {
+ if (*temp == 'n')
+ display_return = 0;
+#if defined (V9_ECHO)
+ else if (*temp == 'e')
+ do_v9 = 1;
+#if defined (USG)
+ else if (*temp == 'E')
+ do_v9 = 0;
+#endif /* USG */
+#endif /* V9_ECHO */
+ else
+ goto just_echo;
+
+ temp++;
+ }
+ argc--;
+ argv++;
+ }
+
+just_echo:
+
+ if (argc > 0)
+ {
+#if defined (V9_ECHO)
+ if (do_v9)
+ {
+ while (argc > 0)
+ {
+ register char *s = argv[0];
+ register int c;
+
+ while (c = *s++)
+ {
+ if (c == '\\' && *s)
+ {
+ switch (c = *s++)
+ {
+ case 'a': c = '\007'; break;
+ case 'b': c = '\b'; break;
+ case 'c': display_return = 0; continue;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = (int) 0x0B; break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if (*s >= '0' && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ if (*s >= '0' && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ break;
+ case '\\': break;
+ default: putchar ('\\'); break;
+ }
+ }
+ putchar(c);
+ }
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar(' ');
+ }
+ }
+ else
+#endif /* V9_ECHO */
+ {
+ while (argc > 0)
+ {
+ fputs (argv[0], stdout);
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar (' ');
+ }
+ }
+ }
+ if (display_return)
+ printf ("\n");
+ exit (0);
+}
diff --git a/src/env.c b/src/env.c
new file mode 100644
index 000000000..b322dee60
--- /dev/null
+++ b/src/env.c
@@ -0,0 +1,168 @@
+/* env - run a program in a modified environment
+ Copyright (C) 1986, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Richard Mlynarik and David MacKenzie */
+
+/* Options:
+ -
+ -i
+ --ignore-environment
+ Construct a new environment from scratch; normally the
+ environment is inherited from the parent process, except as
+ modified by other options.
+
+ -u variable
+ --unset=variable
+ Unset variable VARIABLE (remove it from the environment).
+ If VARIABLE was not set, does nothing.
+
+ variable=value (an arg containing a "=" character)
+ Set the environment variable VARIABLE to value VALUE. VALUE
+ may be of zero length ("variable="). Setting a variable to a
+ zero-length value is different from unsetting it.
+
+ --
+ Indicate that the following argument is the program
+ to invoke. This is necessary when the program's name
+ begins with "-" or contains a "=".
+
+ The first remaining argument specifies a program to invoke;
+ it is searched for according to the specification of the PATH
+ environment variable. Any arguments following that are
+ passed as arguments to that program.
+
+ If no command name is specified following the environment
+ specifications, the resulting environment is printed.
+ This is like specifying a command name of "printenv".
+
+ Examples:
+
+ If the environment passed to "env" is
+ { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks }
+
+ env - foo
+ runs "foo" in a null environment.
+
+ env foo
+ runs "foo" in the environment
+ { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks }
+
+ env DISPLAY=gnu:0 nemacs
+ runs "nemacs" in the envionment
+ { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks DISPLAY=gnu:0 }
+
+ env - LOGNAME=foo /hacks/hack bar baz
+ runs the "hack" program on arguments "bar" and "baz" in an
+ environment in which the only variable is "LOGNAME". Note that
+ the "-" option clears out the PATH variable, so one should be
+ careful to specify in which directory to find the program to
+ call.
+
+ env -u EDITOR LOGNAME=foo PATH=/energy -- e=mc2 bar baz
+ runs the program "/energy/e=mc2" with environment
+ { LOGNAME=foo PATH=/energy }
+*/
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+int putenv ();
+void error ();
+void usage ();
+
+extern char **environ;
+
+/* The name by which this program was run. */
+char *program_name;
+
+struct option longopts[] =
+{
+ {"ignore-environment", 0, NULL, 'i'},
+ {"unset", 1, NULL, 'u'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv, envp)
+ register int argc;
+ register char **argv;
+ char **envp;
+{
+ char *dummy_environ[1];
+ int optc;
+ int ignore_environment = 0;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "+iu:", longopts, (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case 'i':
+ ignore_environment = 1;
+ break;
+ case 'u':
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind != argc && !strcmp (argv[optind], "-"))
+ ignore_environment = 1;
+
+ environ = dummy_environ;
+ environ[0] = NULL;
+
+ if (!ignore_environment)
+ for (; *envp; envp++)
+ putenv (*envp);
+
+ optind = 0; /* Force GNU getopt to re-initialize. */
+ while ((optc = getopt_long (argc, argv, "+iu:", longopts, (int *) 0)) != EOF)
+ if (optc == 'u')
+ putenv (optarg); /* Requires GNU putenv. */
+
+ if (optind != argc && !strcmp (argv[optind], "-"))
+ ++optind;
+
+ while (optind < argc && index (argv[optind], '='))
+ putenv (argv[optind++]);
+
+ /* If no program is specified, print the environment and exit. */
+ if (optind == argc)
+ {
+ while (*environ)
+ puts (*environ++);
+ exit (0);
+ }
+
+ execvp (argv[optind], &argv[optind]);
+ error (errno == ENOENT ? 127 : 126, errno, "%s", argv[optind]);
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-] [-i] [-u name] [--ignore-environment] [--unset=name]\n\
+ [name=value]... [command [args...]]\n",
+ program_name);
+ exit (2);
+}
diff --git a/src/expr.c b/src/expr.c
new file mode 100644
index 000000000..db9586922
--- /dev/null
+++ b/src/expr.c
@@ -0,0 +1,672 @@
+/* expr -- evaluate expressions.
+ Copyright (C) 1986, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Author: Mike Parker.
+
+ This program evaluates expressions. Each token (operator, operand,
+ parenthesis) of the expression must be a seperate argument. The
+ parser used is a reasonably general one, though any incarnation of
+ it is language-specific. It is especially nice for expressions.
+
+ No parse tree is needed; a new node is evaluated immediately.
+ One function can handle multiple operators all of equal precedence,
+ provided they all associate ((x op x) op x).
+
+ Define EVAL_TRACE to print an evaluation trace. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <regex.h>
+#include "system.h"
+
+#if !__STDC__
+#define const
+#endif
+
+#define NEW(type) ((type *) xmalloc (sizeof (type)))
+#define OLD(x) free ((char *) x)
+
+/* The kinds of value we can have. */
+enum valtype
+{
+ integer,
+ string
+};
+typedef enum valtype TYPE;
+
+/* A value is.... */
+struct valinfo
+{
+ TYPE type; /* Which kind. */
+ union
+ { /* The value itself. */
+ int i;
+ char *s;
+ } u;
+};
+typedef struct valinfo VALUE;
+
+/* The arguments given to the program, minus the program name. */
+char **args;
+
+/* The name this program was run with. */
+char *program_name;
+
+VALUE *docolon ();
+VALUE *eval ();
+VALUE *int_value ();
+VALUE *str_value ();
+char *xstrdup ();
+char *strstr ();
+char *xmalloc ();
+int isstring ();
+int nextarg ();
+int nomoreargs ();
+int null ();
+int toarith ();
+void error ();
+void freev ();
+void printv ();
+void tostring ();
+void trace ();
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ VALUE *v;
+
+ program_name = argv[0];
+
+ if (argc == 1)
+ {
+ fprintf (stderr, "Usage: %s expression...\n", argv[0]);
+ exit (1);
+ }
+ args = argv + 1;
+
+ v = eval ();
+ if (!nomoreargs ())
+ error (2, 0, "syntax error");
+ printv (v);
+
+ exit (null (v));
+}
+
+/* Return a VALUE for I. */
+
+VALUE *
+int_value (i)
+ int i;
+{
+ VALUE *v;
+
+ v = NEW (VALUE);
+ v->type = integer;
+ v->u.i = i;
+ return v;
+}
+
+/* Return a VALUE for S. */
+
+VALUE *
+str_value (s)
+ char *s;
+{
+ VALUE *v;
+
+ v = NEW (VALUE);
+ v->type = string;
+ v->u.s = xstrdup (s);
+ return v;
+}
+
+/* Free VALUE V, including structure components. */
+
+void
+freev (v)
+ VALUE *v;
+{
+ if (v->type == string)
+ free (v->u.s);
+ OLD (v);
+}
+
+/* Print VALUE V. */
+
+void
+printv (v)
+ VALUE *v;
+{
+ switch (v->type)
+ {
+ case integer:
+ printf ("%d\n", v->u.i);
+ break;
+ case string:
+ printf ("%s\n", v->u.s);
+ break;
+ }
+}
+
+/* Return nonzero if V is a null-string or zero-number. */
+
+int
+null (v)
+ VALUE *v;
+{
+ switch (v->type)
+ {
+ case integer:
+ return v->u.i == 0;
+ case string:
+ return v->u.s[0] == '\0';
+ }
+}
+
+/* Return nonzero if V is a string value. */
+
+int
+isstring (v)
+ VALUE *v;
+{
+ return v->type == string;
+}
+
+/* Coerce V to a string value (can't fail). */
+
+void
+tostring (v)
+ VALUE *v;
+{
+ char *temp;
+
+ switch (v->type)
+ {
+ case integer:
+ temp = xmalloc (4 * (sizeof (int) / sizeof (char)));
+ sprintf (temp, "%d", v->u.i);
+ v->u.s = temp;
+ v->type = string;
+ break;
+ case string:
+ break;
+ }
+}
+
+/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
+
+int
+toarith (v)
+ VALUE *v;
+{
+ int i;
+ int neg;
+ char *cp;
+
+ switch (v->type)
+ {
+ case integer:
+ return 1;
+ case string:
+ i = 0;
+ cp = v->u.s;
+ neg = (*cp == '-');
+ if (neg)
+ cp++;
+ for (; *cp; cp++)
+ {
+ if (isdigit (*cp))
+ i = i * 10 + *cp - '0';
+ else
+ return 0;
+ }
+ free (v->u.s);
+ v->u.i = i * (neg ? -1 : 1);
+ v->type = integer;
+ return 1;
+ }
+}
+
+/* Return nonzero if the next token matches STR exactly.
+ STR must not be NULL. */
+
+int
+nextarg (str)
+ char *str;
+{
+ if (*args == NULL)
+ return 0;
+ return strcmp (*args, str) == 0;
+}
+
+/* Return nonzero if there no more tokens. */
+
+int
+nomoreargs ()
+{
+ return *args == 0;
+}
+
+/* The comparison operator handling functions. */
+
+#define cmpf(name, rel) \
+int name (l, r) VALUE *l; VALUE *r; \
+{ \
+ if (isstring (l) || isstring (r)) \
+ { \
+ tostring (l); \
+ tostring (r); \
+ return strcmp (l->u.s, r->u.s) rel 0; \
+ } \
+ else \
+ return l->u.i rel r->u.i; \
+}
+
+cmpf (less_than, <)
+cmpf (less_equal, <=)
+cmpf (equal, ==)
+cmpf (not_equal, !=)
+cmpf (greater_equal, >=)
+cmpf (greater_than, >)
+
+#undef cmpf
+
+/* The arithmetic operator handling functions. */
+
+#define arithf(name, op) \
+int name (l, r) VALUE *l; VALUE *r; \
+{ \
+ if (!toarith (l) || !toarith (r)) \
+ error (2, 0, "non-numeric argument"); \
+ return l->u.i op r->u.i; \
+}
+
+arithf (plus, +)
+arithf (minus, -)
+arithf (multiply, *)
+arithf (divide, /)
+arithf (mod, %)
+
+#undef arithf
+
+#ifdef EVAL_TRACE
+/* Print evaluation trace and args remaining. */
+
+void
+trace (fxn)
+ char *fxn;
+{
+ char **a;
+
+ printf ("%s:", fxn);
+ for (a = args; *a; a++)
+ printf (" %s", *a);
+ putchar ('\n');
+}
+#endif
+
+/* Do the : operator.
+ SV is the VALUE for the lhs (the string),
+ PV is the VALUE for the rhs (the pattern). */
+
+VALUE *
+docolon (sv, pv)
+ VALUE *sv;
+ VALUE *pv;
+{
+ VALUE *v;
+ const char *errmsg;
+ struct re_pattern_buffer re_buffer;
+ struct re_registers re_regs;
+ int len;
+
+ tostring (sv);
+ tostring (pv);
+
+ len = strlen (pv->u.s);
+ re_buffer.allocated = 2 * len;
+ re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
+ re_buffer.translate = 0;
+ errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
+ if (errmsg)
+ error (2, 0, "%s", errmsg);
+
+ len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
+ if (len >= 0)
+ {
+ /* Were \(...\) used? */
+ if (re_buffer.re_nsub > 0)/* was (re_regs.start[1] >= 0) */
+ {
+ sv->u.s[re_regs.end[1]] = '\0';
+ v = str_value (sv->u.s + re_regs.start[1]);
+ }
+ else
+ v = int_value (len);
+ }
+ else
+ {
+ /* Match failed -- return the right kind of null. */
+ if (strstr (pv->u.s, "\\("))
+ v = str_value ("");
+ else
+ v = int_value (0);
+ }
+ free (re_buffer.buffer);
+ return v;
+}
+
+/* Handle bare operands and ( expr ) syntax. */
+
+VALUE *
+eval7 ()
+{
+ VALUE *v;
+
+#ifdef EVAL_TRACE
+ trace ("eval7");
+#endif
+ if (nomoreargs ())
+ error (2, 0, "syntax error");
+ else if (nextarg ("("))
+ {
+ args++;
+ v = eval ();
+ if (!nextarg (")"))
+ error (2, 0, "syntax error");
+ args++;
+ return v;
+ }
+ else if (nextarg (")"))
+ error (2, 0, "syntax error");
+ else
+ return str_value (*args++);
+}
+
+/* Handle match, substr, index, and length keywords. */
+
+VALUE *
+eval6 ()
+{
+ VALUE *l;
+ VALUE *r;
+ VALUE *v;
+ VALUE *i1;
+ VALUE *i2;
+
+#ifdef EVAL_TRACE
+ trace ("eval6");
+#endif
+ if (nextarg ("length"))
+ {
+ args++;
+ r = eval6 ();
+ tostring (r);
+ v = int_value (strlen (r->u.s));
+ freev (r);
+ return v;
+ }
+ else if (nextarg ("match"))
+ {
+ args++;
+ l = eval6 ();
+ r = eval6 ();
+ v = docolon (l, r);
+ freev (l);
+ freev (r);
+ return v;
+ }
+ else if (nextarg ("index"))
+ {
+ args++;
+ l = eval6 ();
+ r = eval6 ();
+ tostring (l);
+ tostring (r);
+ v = int_value (strcspn (l->u.s, r->u.s) + 1);
+ if (v->u.i == strlen (l->u.s) + 1)
+ v->u.i = 0;
+ freev (l);
+ freev (r);
+ return v;
+ }
+ else if (nextarg ("substr"))
+ {
+ args++;
+ l = eval6 ();
+ i1 = eval6 ();
+ i2 = eval6 ();
+ tostring (l);
+ if (!toarith (i1) || !toarith (i2)
+ || i1->u.i > strlen (l->u.s)
+ || i1->u.i <= 0 || i2->u.i <= 0)
+ v = str_value ("");
+ else
+ {
+ v = NEW (VALUE);
+ v->type = string;
+ v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
+ l->u.s + i1->u.i - 1, i2->u.i);
+ }
+ freev (l);
+ freev (i1);
+ freev (i2);
+ return v;
+ }
+ else
+ return eval7 ();
+}
+
+/* Handle : operator (pattern matching).
+ Calls docolon to do the real work. */
+
+VALUE *
+eval5 ()
+{
+ VALUE *l;
+ VALUE *r;
+ VALUE *v;
+
+#ifdef EVAL_TRACE
+ trace ("eval5");
+#endif
+ l = eval6 ();
+ while (1)
+ {
+ if (nextarg (":"))
+ {
+ args++;
+ r = eval6 ();
+ v = docolon (l, r);
+ freev (l);
+ freev (r);
+ l = v;
+ }
+ else
+ return l;
+ }
+}
+
+/* Handle *, /, % operators. */
+
+VALUE *
+eval4 ()
+{
+ VALUE *l;
+ VALUE *r;
+ int (*fxn) ();
+ int val;
+
+#ifdef EVAL_TRACE
+ trace ("eval4");
+#endif
+ l = eval5 ();
+ while (1)
+ {
+ if (nextarg ("*"))
+ fxn = multiply;
+ else if (nextarg ("/"))
+ fxn = divide;
+ else if (nextarg ("%"))
+ fxn = mod;
+ else
+ return l;
+ args++;
+ r = eval5 ();
+ val = (*fxn) (l, r);
+ freev (l);
+ freev (r);
+ l = int_value (val);
+ }
+}
+
+/* Handle +, - operators. */
+
+VALUE *
+eval3 ()
+{
+ VALUE *l;
+ VALUE *r;
+ int (*fxn) ();
+ int val;
+
+#ifdef EVAL_TRACE
+ trace ("eval3");
+#endif
+ l = eval4 ();
+ while (1)
+ {
+ if (nextarg ("+"))
+ fxn = plus;
+ else if (nextarg ("-"))
+ fxn = minus;
+ else
+ return l;
+ args++;
+ r = eval4 ();
+ val = (*fxn) (l, r);
+ freev (l);
+ freev (r);
+ l = int_value (val);
+ }
+}
+
+/* Handle comparisons. */
+
+VALUE *
+eval2 ()
+{
+ VALUE *l;
+ VALUE *r;
+ int (*fxn) ();
+ int val;
+
+#ifdef EVAL_TRACE
+ trace ("eval2");
+#endif
+ l = eval3 ();
+ while (1)
+ {
+ if (nextarg ("<"))
+ fxn = less_than;
+ else if (nextarg ("<="))
+ fxn = less_equal;
+ else if (nextarg ("=") || nextarg ("=="))
+ fxn = equal;
+ else if (nextarg ("!="))
+ fxn = not_equal;
+ else if (nextarg (">="))
+ fxn = greater_equal;
+ else if (nextarg (">"))
+ fxn = greater_than;
+ else
+ return l;
+ args++;
+ r = eval3 ();
+ toarith (l);
+ toarith (r);
+ val = (*fxn) (l, r);
+ freev (l);
+ freev (r);
+ l = int_value (val);
+ }
+}
+
+/* Handle &. */
+
+VALUE *
+eval1 ()
+{
+ VALUE *l;
+ VALUE *r;
+
+#ifdef EVAL_TRACE
+ trace ("eval1");
+#endif
+ l = eval2 ();
+ while (1)
+ {
+ if (nextarg ("&"))
+ {
+ args++;
+ r = eval2 ();
+ if (null (l) || null (r))
+ {
+ freev (l);
+ freev (r);
+ l = int_value (0);
+ }
+ else
+ freev (r);
+ }
+ else
+ return l;
+ }
+}
+
+/* Handle |. */
+
+VALUE *
+eval ()
+{
+ VALUE *l;
+ VALUE *r;
+
+#ifdef EVAL_TRACE
+ trace ("eval");
+#endif
+ l = eval1 ();
+ while (1)
+ {
+ if (nextarg ("|"))
+ {
+ args++;
+ r = eval1 ();
+ if (null (l))
+ {
+ freev (l);
+ l = r;
+ }
+ else
+ freev (r);
+ }
+ else
+ return l;
+ }
+}
diff --git a/src/groups.sh b/src/groups.sh
new file mode 100755
index 000000000..7d8405869
--- /dev/null
+++ b/src/groups.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# groups -- print the groups a user is in
+# Copyright (C) 1991 Free Software Foundation, Inc.
+
+# 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+
+# Make sure we get GNU id, if possible; also allow
+# it to be somewhere else in PATH if not installed yet.
+PATH=@bindir@:$PATH
+
+if [ $# -eq 0 ]; then
+ id -Gn
+else
+ for name in "$@"; do
+ echo $name : `id -Gn $name`
+ done
+fi
diff --git a/src/id.c b/src/id.c
new file mode 100644
index 000000000..beddc1356
--- /dev/null
+++ b/src/id.c
@@ -0,0 +1,346 @@
+/* id -- print real and effective UIDs and GIDs
+ Copyright (C) 1989, 1991 Free Software Foundation.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Arnold Robbins, arnold@audiofax.com.
+ Major rewrite by David MacKenzie, djm@gnu.ai.mit.edu. */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include "system.h"
+
+#ifdef _POSIX_VERSION
+#include <limits.h>
+#if !defined(NGROUPS_MAX) || NGROUPS_MAX < 1
+#define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
+#endif /* !NGROUPS_MAX */
+
+/* Even though SunOS 4, Ultrix 4, and 386BSD are mostly POSIX.1 compliant,
+ their getgroups system call (except in the `System V' environment, which
+ is troublesome in other ways) fills in an array of int, not gid_t
+ (which is `short' on those systems). Kludge, kludge. */
+
+#if !defined(sun) && !defined(ultrix) && !defined(__386BSD__)
+#define GETGROUPS_T gid_t
+#else /* sun or ultrix or 386BSD */
+#define GETGROUPS_T int
+#endif /* sun or ultrix or 386BSD */
+#else /* not _POSIX_VERSION */
+struct passwd *getpwuid ();
+struct group *getgrgid ();
+uid_t getuid ();
+gid_t getgid ();
+uid_t geteuid ();
+gid_t getegid ();
+#include <sys/param.h>
+#if !defined(NGROUPS_MAX) && defined(NGROUPS)
+#define NGROUPS_MAX NGROUPS
+#endif /* not NGROUPS_MAX and NGROUPS */
+#define GETGROUPS_T int
+#endif /* not _POSIX_VERSION */
+
+char *xmalloc ();
+int getugroups ();
+void error ();
+void print_user ();
+void print_group ();
+void print_group_list ();
+void print_full_info ();
+void usage ();
+
+/* The name this program was run with. */
+char *program_name;
+
+/* If nonzero, output only the group ID(s). -g */
+int just_group = 0;
+
+/* If nonzero, output user/group name instead of ID number. -n */
+int use_name = 0;
+
+/* If nonzero, output real UID/GID instead of default effective UID/GID. -r */
+int use_real = 0;
+
+/* If nonzero, output only the user ID(s). -u */
+int just_user = 0;
+
+/* If nonzero, output only the supplementary groups. -G */
+int just_group_list = 0;
+
+/* The real and effective IDs of the user to print. */
+uid_t ruid, euid;
+gid_t rgid, egid;
+
+/* The number of errors encountered so far. */
+int problems = 0;
+
+struct option longopts[] =
+{
+ {"group", 0, NULL, 'g'},
+ {"name", 0, NULL, 'n'},
+ {"real", 0, NULL, 'r'},
+ {"user", 0, NULL, 'u'},
+ {"groups", 0, NULL, 'G'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "gnruG", longopts, (int *) 0))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 'g':
+ just_group = 1;
+ break;
+ case 'n':
+ use_name = 1;
+ break;
+ case 'r':
+ use_real = 1;
+ break;
+ case 'u':
+ just_user = 1;
+ break;
+ case 'G':
+ just_group_list = 1;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (just_user + just_group + just_group_list > 1)
+ error (1, 0, "cannot print only user and only group");
+
+ if (just_user + just_group + just_group_list == 0 && (use_real || use_name))
+ error (1, 0, "cannot print only names or real IDs in default format");
+
+ if (argc - optind > 1)
+ usage ();
+
+ if (argc - optind == 1)
+ {
+ struct passwd *pwd = getpwnam (argv[optind]);
+ if (pwd == NULL)
+ error (1, 0, "%s: No such user", argv[optind]);
+ ruid = euid = pwd->pw_uid;
+ rgid = egid = pwd->pw_gid;
+ }
+ else
+ {
+ euid = geteuid ();
+ ruid = getuid ();
+ egid = getegid ();
+ rgid = getgid ();
+ }
+
+ if (just_user)
+ print_user (use_real ? ruid : euid);
+ else if (just_group)
+ print_group (use_real ? rgid : egid);
+ else if (just_group_list)
+ print_group_list (argv[optind]);
+ else
+ print_full_info (argv[optind]);
+ putchar ('\n');
+
+ exit (problems != 0);
+}
+
+/* Print the name or value of user ID UID. */
+
+void
+print_user (uid)
+ int uid;
+{
+ struct passwd *pwd = NULL;
+
+ if (use_name)
+ {
+ pwd = getpwuid (uid);
+ if (pwd == NULL)
+ problems++;
+ }
+
+ if (pwd == NULL)
+ printf ("%u", (unsigned) uid);
+ else
+ printf ("%s", pwd->pw_name);
+}
+
+/* Print the name or value of group ID GID. */
+
+void
+print_group (gid)
+ int gid;
+{
+ struct group *grp = NULL;
+
+ if (use_name)
+ {
+ grp = getgrgid (gid);
+ if (grp == NULL)
+ problems++;
+ }
+
+ if (grp == NULL)
+ printf ("%u", (unsigned) gid);
+ else
+ printf ("%s", grp->gr_name);
+}
+
+/* Print all of the distinct groups the user is in . */
+
+void
+print_group_list (username)
+ char *username;
+{
+ print_group (rgid);
+ if (egid != rgid)
+ {
+ putchar (' ');
+ print_group (egid);
+ }
+
+#ifdef NGROUPS_MAX
+ {
+ int ngroups;
+ GETGROUPS_T *groups;
+ register int i;
+
+ groups = (GETGROUPS_T *) xmalloc (NGROUPS_MAX * sizeof (GETGROUPS_T));
+ if (username == 0)
+ ngroups = getgroups (NGROUPS_MAX, groups);
+ else
+ ngroups = getugroups (NGROUPS_MAX, groups, username);
+ if (ngroups < 0)
+ {
+ error (0, errno, "cannot get supplemental group list");
+ problems++;
+ free (groups);
+ return;
+ }
+
+ for (i = 0; i < ngroups; i++)
+ if (groups[i] != rgid && groups[i] != egid)
+ {
+ putchar (' ');
+ print_group (groups[i]);
+ }
+ free (groups);
+ }
+#endif
+}
+
+/* Print all of the info about the user's user and group IDs. */
+
+void
+print_full_info (username)
+ char *username;
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ printf ("uid=%u", (unsigned) ruid);
+ pwd = getpwuid (ruid);
+ if (pwd == NULL)
+ problems++;
+ else
+ printf ("(%s)", pwd->pw_name);
+
+ printf (" gid=%u", (unsigned) rgid);
+ grp = getgrgid (rgid);
+ if (grp == NULL)
+ problems++;
+ else
+ printf ("(%s)", grp->gr_name);
+
+ if (euid != ruid)
+ {
+ printf (" euid=%u", (unsigned) euid);
+ pwd = getpwuid (euid);
+ if (pwd == NULL)
+ problems++;
+ else
+ printf ("(%s)", pwd->pw_name);
+ }
+
+ if (egid != rgid)
+ {
+ printf (" egid=%u", (unsigned) egid);
+ grp = getgrgid (egid);
+ if (grp == NULL)
+ problems++;
+ else
+ printf ("(%s)", grp->gr_name);
+ }
+
+#ifdef NGROUPS_MAX
+ {
+ int ngroups;
+ GETGROUPS_T *groups;
+ register int i;
+
+ groups = (GETGROUPS_T *) xmalloc (NGROUPS_MAX * sizeof (GETGROUPS_T));
+ if (username == 0)
+ ngroups = getgroups (NGROUPS_MAX, groups);
+ else
+ ngroups = getugroups (NGROUPS_MAX, groups, username);
+ if (ngroups < 0)
+ {
+ error (0, errno, "cannot get supplemental group list");
+ problems++;
+ free (groups);
+ return;
+ }
+
+ if (ngroups > 0)
+ fputs (" groups=", stdout);
+ for (i = 0; i < ngroups; i++)
+ {
+ if (i > 0)
+ putchar (',');
+ printf ("%u", (unsigned) groups[i]);
+ grp = getgrgid (groups[i]);
+ if (grp == NULL)
+ problems++;
+ else
+ printf ("(%s)", grp->gr_name);
+ }
+ free (groups);
+ }
+#endif
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-gnruG] [--group] [--name] [--real] [--user] [--groups] [username]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/logname.c b/src/logname.c
new file mode 100644
index 000000000..39d213709
--- /dev/null
+++ b/src/logname.c
@@ -0,0 +1,43 @@
+/* logname -- print user's login name
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register char *cp;
+
+ if (argc != 1)
+ {
+ fprintf (stderr, "Usage: %s\n", argv[0]);
+ exit (1);
+ }
+
+ cp = getlogin ();
+ if (cp)
+ {
+ puts (cp);
+ exit (0);
+ }
+ fprintf (stderr,"%s: no login name\n", argv[0]);
+ exit (1);
+}
diff --git a/src/nice.c b/src/nice.c
new file mode 100644
index 000000000..4911fcc70
--- /dev/null
+++ b/src/nice.c
@@ -0,0 +1,150 @@
+/* nice -- run a program with modified scheduling priority
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* David MacKenzie <djm@ai.mit.edu> */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#ifndef NICE_PRIORITY
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#include "system.h"
+
+int isinteger ();
+void error ();
+void usage ();
+
+/* The name this program was run with. */
+char *program_name;
+
+struct option longopts[] =
+{
+ {"adjustment", 1, NULL, 'n'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int current_priority;
+ int adjustment = 0;
+ int minusflag = 0;
+ int adjustment_given = 0;
+ int optc;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "+0123456789-n:", longopts,
+ (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case '?':
+ usage ();
+
+ case 'n':
+ if (!isinteger (optarg))
+ error (1, 0, "invalid priority `%s'", optarg);
+ adjustment = atoi (optarg);
+ adjustment_given = 1;
+ break;
+
+ case '-':
+ minusflag = 1;
+ break;
+
+ default:
+ adjustment = adjustment * 10 + optc - '0';
+ adjustment_given = 1;
+ }
+ }
+
+ if (minusflag)
+ adjustment = -adjustment;
+ if (!adjustment_given)
+ adjustment = 10;
+
+ if (optind == argc)
+ {
+ if (adjustment_given)
+ usage ();
+ /* No command given; print the priority. */
+ errno = 0;
+#ifndef NICE_PRIORITY
+ current_priority = getpriority (PRIO_PROCESS, 0);
+#else
+ current_priority = nice (0);
+#endif
+ if (current_priority == -1 && errno != 0)
+ error (1, errno, "cannot get priority");
+ printf ("%d\n", current_priority);
+ exit (0);
+ }
+
+ errno = 0;
+#ifndef NICE_PRIORITY
+ current_priority = getpriority (PRIO_PROCESS, 0);
+#else
+ current_priority = nice (0);
+#endif
+ if (current_priority == -1 && errno != 0)
+ error (1, errno, "cannot get priority");
+
+#ifndef NICE_PRIORITY
+ if (setpriority (PRIO_PROCESS, 0, current_priority + adjustment))
+#else
+ if (nice (adjustment) == -1)
+#endif
+ error (1, errno, "cannot set priority");
+
+ execvp (argv[optind], &argv[optind]);
+ error (errno == ENOENT ? 127 : 126, errno, "%s", argv[optind]);
+}
+
+/* Return nonzero if S represents a (possibly signed) decimal integer,
+ zero if not. */
+
+int
+isinteger (s)
+ char *s;
+{
+ if (*s == '-')
+ ++s;
+ if (*s == 0)
+ return 0;
+ while (*s)
+ {
+ if (*s < '0' || *s > '9')
+ return 0;
+ ++s;
+ }
+ return 1;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-n adjustment] [-adjustment] [--adjustment=adjustment]\n\
+ [command [arg...]]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/nohup.sh b/src/nohup.sh
new file mode 100755
index 000000000..00908b5ba
--- /dev/null
+++ b/src/nohup.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# nohup -- run a command immume to hangups, with output to a non-tty
+# Copyright (C) 1991 Free Software Foundation, Inc.
+
+# 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+
+# Make sure we get GNU nice, if possible; also allow
+# it to be somewhere else in PATH if not installed yet.
+PATH=@bindir@:$PATH
+
+if [ $# -eq 0 ]; then
+ echo "Usage: nohup command [arg...]" 2>&1
+ exit 1
+fi
+
+trap "" 1
+oldmask=`umask`; umask 077
+# Only redirect the output if the user didn't already do it.
+if [ -t 1 ]; then
+ # If we cannot write to the current directory, use the home directory.
+ if cat /dev/null >> nohup.out; then
+ echo "nohup: appending output to \`nohup.out'" 2>&1
+ umask $oldmask
+ exec nice -5 "$@" >> nohup.out 2>&1
+ else
+ cat /dev/null >> $HOME/nohup.out
+ echo "nohup: appending output to \`$HOME/nohup.out'" 2>&1
+ umask $oldmask
+ exec nice -5 "$@" >> $HOME/nohup.out 2>&1
+ fi
+else
+ umask $oldmask
+ exec nice -5 "$@"
+fi
diff --git a/src/pathchk.c b/src/pathchk.c
new file mode 100644
index 000000000..a8db2b28f
--- /dev/null
+++ b/src/pathchk.c
@@ -0,0 +1,332 @@
+/* pathchk -- check whether pathnames are valid or portable
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: pathchk [-p] [--portability] path...
+
+ For each PATH, print a message if any of these conditions are false:
+ * all existing leading directories in PATH have search (execute) permission
+ * strlen (PATH) <= PATH_MAX
+ * strlen (each_directory_in_PATH) <= NAME_MAX
+
+ Exit status:
+ 0 All PATH names passed all of the tests.
+ 1 An error occurred.
+
+ Options:
+ -p, --portability Instead of performing length checks on the
+ underlying filesystem, test the length of the
+ pathname and its components against the POSIX.1
+ minimum limits for portability, _POSIX_NAME_MAX
+ and _POSIX_PATH_MAX in 2.9.2. Also check that
+ the pathname contains no characters not in the
+ portable filename character set.
+
+ David MacKenzie <djm@gnu.ai.mit.edu>
+ and Jim Meyering <meyering@cs.utexas.edu> */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+#ifdef _POSIX_VERSION
+#include <limits.h>
+#ifndef PATH_MAX
+#define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
+#endif /* not PATH_MAX */
+#ifndef NAME_MAX
+#define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX);
+#endif /* not NAME_MAX */
+
+#else /* not _POSIX_VERSION */
+
+#include <sys/param.h>
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else /* not MAXPATHLEN */
+#define PATH_MAX _POSIX_PATH_MAX
+#endif /* not MAXPATHLEN */
+#endif /* not PATH_MAX */
+
+#ifndef NAME_MAX
+#ifdef MAXNAMLEN
+#define NAME_MAX MAXNAMLEN
+#else /* not MAXNAMLEN */
+#define NAME_MAX _POSIX_NAME_MAX
+#endif /* not MAXNAMLEN */
+#endif /* not NAME_MAX */
+
+#endif /* not _POSIX_VERSION */
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+#ifndef _POSIX_NAME_MAX
+#define _POSIX_NAME_MAX 14
+#endif
+
+#ifndef PATH_MAX_FOR
+#define PATH_MAX_FOR(p) PATH_MAX
+#endif
+#ifndef NAME_MAX_FOR
+#define NAME_MAX_FOR(p) NAME_MAX
+#endif
+
+char *xstrdup();
+int validate_path ();
+void error ();
+void usage ();
+
+/* The name this program was run with. */
+char *program_name;
+
+struct option longopts[] =
+{
+ {"portability", 0, NULL, 'p'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int exit_status = 0;
+ int check_portability = 0;
+ int optc;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "p", longopts, (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case 'p':
+ check_portability = 1;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind == argc)
+ usage ();
+
+ for (; optind < argc; ++optind)
+ exit_status |= validate_path (argv[optind], check_portability);
+
+ exit (exit_status);
+}
+
+/* Each element is nonzero if the corresponding ASCII character is
+ in the POSIX portable character set, and zero if it is not.
+ In addition, the entry for `/' is nonzero to simplify checking. */
+char portable_chars[] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* If PATH contains only portable characters, return 1, else 0. */
+
+int
+portable_chars_only (path)
+ char *path;
+{
+ char *p;
+
+ for (p = path; *p; ++p)
+ if (portable_chars[*p] == 0)
+ {
+ error (0, 0, "path `%s' contains nonportable character `%c'",
+ path, *p);
+ return 0;
+ }
+ return 1;
+}
+
+/* Return 1 if PATH is a usable leading directory, 0 if not,
+ 2 if it doesn't exist. */
+
+int
+dir_ok (path)
+ char *path;
+{
+ struct stat stats;
+
+ if (stat (path, &stats))
+ return 2;
+
+ if (!S_ISDIR (stats.st_mode))
+ {
+ error (0, 0, "`%s' is not a directory", path);
+ return 0;
+ }
+
+ /* Use access to test for search permission because
+ testing permission bits of st_mode can lose with new
+ access control mechanisms. Of course, access loses if you're
+ running setuid. */
+ if (access (path, X_OK) != 0)
+ {
+ if (errno == EACCES)
+ error (0, 0, "directory `%s' is not searchable", path);
+ else
+ error (0, errno, "%s", path);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Make sure that
+ strlen (PATH) <= PATH_MAX
+ && strlen (each-existing-directory-in-PATH) <= NAME_MAX
+
+ If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
+ _POSIX_NAME_MAX instead, and make sure that PATH contains no
+ characters not in the POSIX portable filename character set, which
+ consists of A-Z, a-z, 0-9, ., _, -.
+
+ Make sure that all leading directories along PATH that exist have
+ `x' permission.
+
+ Return 0 if all of these tests are successful, 1 if any fail. */
+
+int
+validate_path (path, portability)
+ char *path;
+ int portability;
+{
+ int path_max;
+ int last_elem; /* Nonzero if checking last element of path. */
+ int exists; /* 2 if the path element exists. */
+ char *slash;
+ char *parent; /* Last existing leading directory so far. */
+
+ if (portability && !portable_chars_only (path))
+ return 1;
+
+ if (*path == '\0')
+ return 0;
+
+ /* Figure out the parent of the first element in PATH. */
+ parent = xstrdup (*path == '/' ? "/" : ".");
+
+ slash = path;
+ last_elem = 0;
+ while (1)
+ {
+ int name_max;
+ int length; /* Length of partial path being checked. */
+ char *start; /* Start of path element being checked. */
+
+ /* Find the end of this element of the path.
+ Then chop off the rest of the path after this element. */
+ while (*slash == '/')
+ slash++;
+ start = slash;
+ slash = index (slash, '/');
+ if (slash != NULL)
+ *slash = '\0';
+ else
+ {
+ last_elem = 1;
+ slash = index (start, '\0');
+ }
+
+ if (!last_elem)
+ {
+ exists = dir_ok (path);
+ if (dir_ok == 0)
+ {
+ free (parent);
+ return 1;
+ }
+ }
+
+ length = slash - start;
+ /* Since we know that `parent' is a directory, it's ok to call
+ pathconf with it as the argument. (If `parent' isn't a directory
+ or doesn't exist, the behavior of pathconf is undefined.)
+ But if `parent' is a directory and is on a remote file system,
+ it's likely that pathconf can't give us a reasonable value
+ and will return -1. (NFS and tempfs are not POSIX . . .)
+ In that case, we have no choice but to assume the pessimal
+ POSIX minimums. */
+ name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
+ if (name_max < 0)
+ name_max = _POSIX_NAME_MAX;
+ if (length > name_max)
+ {
+ error (0, 0, "name `%s' has length %d; exceeds limit of %d",
+ start, length, name_max);
+ free (parent);
+ return 1;
+ }
+
+ if (last_elem)
+ break;
+
+ if (exists == 1)
+ {
+ free (parent);
+ parent = xstrdup (path);
+ }
+
+ *slash++ = '/';
+ }
+
+ /* `parent' is now the last existing leading directory in the whole path,
+ so it's ok to call pathconf with it as the argument. */
+ path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
+ if (path_max < 0)
+ path_max = _POSIX_PATH_MAX;
+ free (parent);
+ if (strlen (path) > path_max)
+ {
+ error (0, 0, "path `%s' has length %d; exceeds limit of %d",
+ path, strlen (path), path_max);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-p] [--portability] path...\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/printenv.c b/src/printenv.c
new file mode 100644
index 000000000..ac4511cc5
--- /dev/null
+++ b/src/printenv.c
@@ -0,0 +1,69 @@
+/* printenv -- print all or part of environment
+ Copyright (C) 1989, 1991 Free Software Foundation.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: printenv [variable...]
+
+ If no arguments are given, print the entire environment.
+ If one or more variable names are given, print the value of
+ each one that is set, and nothing for ones that are not set.
+
+ Exit status:
+ 0 if all variables specified were found
+ 1 if not
+
+ David MacKenzie and Richard Mlynarik */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+extern char **environ;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char **env;
+ char *ep, *ap;
+ int i;
+ int matches = 0;
+
+ if (argc == 1)
+ {
+ for (env = environ; *env; ++env)
+ puts (*env);
+ exit (0);
+ }
+
+ for (i = 1; i < argc; ++i)
+ {
+ for (env = environ; *env; ++env)
+ {
+ ep = *env;
+ ap = argv[i];
+ while (*ep != '\0' && *ap != '\0' && *ep++ == *ap++)
+ if (*ep == '=' && *ap == '\0')
+ {
+ puts (ep + 1);
+ ++matches;
+ break;
+ }
+ }
+ }
+ exit (matches != argc - 1);
+}
diff --git a/src/printf.c b/src/printf.c
new file mode 100644
index 000000000..b546ef65b
--- /dev/null
+++ b/src/printf.c
@@ -0,0 +1,481 @@
+/* printf - format and print data
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: printf format [argument...]
+
+ A front end to the printf function that lets it be used from the shell.
+
+ Backslash escapes:
+
+ \" = double quote
+ \\ = backslash
+ \a = alert (bell)
+ \b = backspace
+ \c = produce no further output
+ \f = form feed
+ \n = new line
+ \r = carriage return
+ \t = horizontal tab
+ \v = vertical tab
+ \0ooo = octal number (ooo is 0 to 3 digits)
+ \xhhh = hexadecimal number (hhh is 1 to 3 digits)
+
+ Additional directive:
+
+ %b = print an argument string, interpreting backslash escapes
+
+ The `format' argument is re-used as many times as necessary
+ to convert all of the given arguments.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "system.h"
+
+#ifndef STDC_HEADERS
+double strtod ();
+long strtol ();
+unsigned long strtoul ();
+#endif
+
+#define isodigit(c) ((c) >= '0' && (c) <= '7')
+#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
+#define octtobin(c) ((c) - '0')
+
+char *xmalloc ();
+double xstrtod ();
+int print_esc ();
+int print_formatted ();
+long xstrtol ();
+unsigned long xstrtoul ();
+void error ();
+void print_direc ();
+void print_esc_char ();
+void print_esc_string ();
+void verify ();
+
+/* The name this program was run with. */
+char *program_name;
+
+/* The value to return to the calling program. */
+int exit_status;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *format;
+ int args_used;
+
+ program_name = argv[0];
+ exit_status = 0;
+
+ if (argc == 1)
+ {
+ fprintf (stderr, "Usage: %s format [argument...]\n", program_name);
+ exit (1);
+ }
+
+ format = argv[1];
+ argc -= 2;
+ argv += 2;
+
+ do
+ {
+ args_used = print_formatted (format, argc, argv);
+ argc -= args_used;
+ argv += args_used;
+ }
+ while (args_used > 0 && argc > 0);
+
+ exit (exit_status);
+}
+
+/* Print the text in FORMAT, using ARGV (with ARGC elements) for
+ arguments to any `%' directives.
+ Return the number of elements of ARGV used. */
+
+int
+print_formatted (format, argc, argv)
+ char *format;
+ int argc;
+ char **argv;
+{
+ int save_argc = argc; /* Preserve original value. */
+ char *f; /* Pointer into `format'. */
+ char *direc_start; /* Start of % directive. */
+ int direc_length; /* Length of % directive. */
+ int field_width; /* Arg to first '*', or -1 if none. */
+ int precision; /* Arg to second '*', or -1 if none. */
+
+ for (f = format; *f; ++f)
+ {
+ switch (*f)
+ {
+ case '%':
+ direc_start = f++;
+ direc_length = 1;
+ field_width = precision = -1;
+ if (*f == '%')
+ {
+ putchar ('%');
+ break;
+ }
+ if (*f == 'b')
+ {
+ if (argc > 0)
+ {
+ print_esc_string (*argv);
+ ++argv;
+ --argc;
+ }
+ break;
+ }
+ if (index ("-+ #", *f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ if (*f == '*')
+ {
+ ++f;
+ ++direc_length;
+ if (argc > 0)
+ {
+ field_width = xstrtoul (*argv);
+ ++argv;
+ --argc;
+ }
+ else
+ field_width = 0;
+ }
+ else
+ while (isdigit (*f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ if (*f == '.')
+ {
+ ++f;
+ ++direc_length;
+ if (*f == '*')
+ {
+ ++f;
+ ++direc_length;
+ if (argc > 0)
+ {
+ precision = xstrtoul (*argv);
+ ++argv;
+ --argc;
+ }
+ else
+ precision = 0;
+ }
+ else
+ while (isdigit (*f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ }
+ if (*f == 'l' || *f == 'L' || *f == 'h')
+ {
+ ++f;
+ ++direc_length;
+ }
+ if (!index ("diouxXfeEgGcs", *f))
+ error (1, 0, "%%%c: invalid directive", *f);
+ ++direc_length;
+ if (argc > 0)
+ {
+ print_direc (direc_start, direc_length, field_width,
+ precision, *argv);
+ ++argv;
+ --argc;
+ }
+ else
+ print_direc (direc_start, direc_length, field_width,
+ precision, "");
+ break;
+
+ case '\\':
+ f += print_esc (f);
+ break;
+
+ default:
+ putchar (*f);
+ }
+ }
+
+ return save_argc - argc;
+}
+
+/* Print a \ escape sequence starting at ESCSTART.
+ Return the number of characters in the escape sequence
+ besides the backslash. */
+
+int
+print_esc (escstart)
+ char *escstart;
+{
+ register char *p = escstart + 1;
+ int esc_value = 0; /* Value of \nnn escape. */
+ int esc_length; /* Length of \nnn escape. */
+
+ /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
+ if (*p == 'x')
+ {
+ for (esc_length = 0, ++p;
+ esc_length < 3 && isxdigit (*p);
+ ++esc_length, ++p)
+ esc_value = esc_value * 16 + hextobin (*p);
+ if (esc_length == 0)
+ error (1, 0, "missing hexadecimal number in escape");
+ putchar (esc_value);
+ }
+ else if (*p == '0')
+ {
+ for (esc_length = 0, ++p;
+ esc_length < 3 && isodigit (*p);
+ ++esc_length, ++p)
+ esc_value = esc_value * 8 + octtobin (*p);
+ putchar (esc_value);
+ }
+ else if (index ("\"\\abcfnrtv", *p))
+ print_esc_char (*p++);
+ else
+ error (1, 0, "\\%c: invalid escape", *p);
+ return p - escstart - 1;
+}
+
+/* Output a single-character \ escape. */
+
+void
+print_esc_char (c)
+ char c;
+{
+ switch (c)
+ {
+ case 'a': /* Alert. */
+ putchar (7);
+ break;
+ case 'b': /* Backspace. */
+ putchar (8);
+ break;
+ case 'c': /* Cancel the rest of the output. */
+ exit (0);
+ break;
+ case 'f': /* Form feed. */
+ putchar (12);
+ break;
+ case 'n': /* New line. */
+ putchar (10);
+ break;
+ case 'r': /* Carriage return. */
+ putchar (13);
+ break;
+ case 't': /* Horizontal tab. */
+ putchar (9);
+ break;
+ case 'v': /* Vertical tab. */
+ putchar (11);
+ break;
+ default:
+ putchar (c);
+ break;
+ }
+}
+
+/* Print string STR, evaluating \ escapes. */
+
+void
+print_esc_string (str)
+ char *str;
+{
+ for (; *str; str++)
+ if (*str == '\\')
+ str += print_esc (str);
+ else
+ putchar (*str);
+}
+
+/* Output a % directive. START is the start of the directive,
+ LENGTH is its length, and ARGUMENT is its argument.
+ If FIELD_WIDTH or PRECISION is non-negative, they are args for
+ '*' values in those fields. */
+
+void
+print_direc (start, length, field_width, precision, argument)
+ char *start;
+ int length;
+ int field_width;
+ int precision;
+ char *argument;
+{
+ char *p; /* Null-terminated copy of % directive. */
+
+ p = xmalloc ((unsigned) (length + 1));
+ strncpy (p, start, length);
+ p[length] = 0;
+
+ switch (p[length - 1])
+ {
+ case 'd':
+ case 'i':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, xstrtol (argument));
+ else
+ printf (p, precision, xstrtol (argument));
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, xstrtol (argument));
+ else
+ printf (p, field_width, precision, xstrtol (argument));
+ }
+ break;
+
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, xstrtoul (argument));
+ else
+ printf (p, precision, xstrtoul (argument));
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, xstrtoul (argument));
+ else
+ printf (p, field_width, precision, xstrtoul (argument));
+ }
+ break;
+
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, xstrtod (argument));
+ else
+ printf (p, precision, xstrtod (argument));
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, xstrtod (argument));
+ else
+ printf (p, field_width, precision, xstrtod (argument));
+ }
+ break;
+
+ case 'c':
+ printf (p, *argument);
+ break;
+
+ case 's':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, argument);
+ else
+ printf (p, precision, argument);
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, argument);
+ else
+ printf (p, field_width, precision, argument);
+ }
+ break;
+ }
+
+ free (p);
+}
+
+unsigned long
+xstrtoul (s)
+ char *s;
+{
+ char *end;
+ unsigned long val;
+
+ errno = 0;
+ val = strtoul (s, &end, 0);
+ verify (s, end);
+ return val;
+}
+
+long
+xstrtol (s)
+ char *s;
+{
+ char *end;
+ long val;
+
+ errno = 0;
+ val = strtol (s, &end, 0);
+ verify (s, end);
+ return val;
+}
+
+double
+xstrtod (s)
+ char *s;
+{
+ char *end;
+ double val;
+
+ errno = 0;
+ val = strtod (s, &end);
+ verify (s, end);
+ return val;
+}
+
+void
+verify (s, end)
+ char *s, *end;
+{
+ if (errno)
+ {
+ error (0, errno, "%s", s);
+ exit_status = 1;
+ }
+ else if (*end)
+ {
+ if (s == end)
+ error (0, 0, "%s: expected a numeric value", s);
+ else
+ error (0, 0, "%s: value not completely converted", s);
+ exit_status = 1;
+ }
+}
diff --git a/src/sleep.c b/src/sleep.c
new file mode 100644
index 000000000..7212ba54f
--- /dev/null
+++ b/src/sleep.c
@@ -0,0 +1,84 @@
+/* sleep - delay for a specified amount of time.
+ Copyright (C) 1984, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+long argdecode ();
+void error ();
+
+/* The name by which this program was run. */
+char *program_name;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ unsigned seconds = 0;
+
+ program_name = argv[0];
+
+ if (argc == 1)
+ {
+ fprintf (stderr, "Usage: %s number[smhd]...\n", argv[0]);
+ exit (1);
+ }
+
+ for (i = 1; i < argc; i++)
+ seconds += argdecode (argv[i]);
+
+ sleep (seconds);
+
+ exit (0);
+}
+
+long
+argdecode (s)
+ char *s;
+{
+ long value;
+ register char *p = s;
+ register char c;
+
+ value = 0;
+ while ((c = *p++) >= '0' && c <= '9')
+ value = value * 10 + c - '0';
+
+ switch (c)
+ {
+ case 's':
+ break;
+ case 'm':
+ value *= 60;
+ break;
+ case 'h':
+ value *= 60 * 60;
+ break;
+ case 'd':
+ value *= 60 * 60 * 24;
+ break;
+ default:
+ p--;
+ }
+
+ if (*p)
+ error (1, 0, "invalid time interval `%s'", s);
+ return value;
+}
diff --git a/src/stty.c b/src/stty.c
new file mode 100644
index 000000000..5349af0f2
--- /dev/null
+++ b/src/stty.c
@@ -0,0 +1,1241 @@
+/* stty -- change and print terminal line settings
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: stty [-ag] [--all] [--save] [setting...]
+
+ Options:
+ -a, --all Write all current settings to stdout in human-readable form.
+ -g, --save Write all current settings to stdout in stty-readable form.
+
+ If no args are given, write to stdout the baud rate and settings that
+ have been changed from their defaults. Mode reading and changes
+ are done on stdin.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <termios.h>
+#ifdef _AIX
+#include <sys/ioctl.h> /* Needed to get window size. */
+#endif
+#ifdef WINSIZE_IN_PTEM
+#include <sys/stream.h>
+#include <sys/ptem.h>
+#endif
+#include <getopt.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif
+#include "system.h"
+
+#if defined(GWINSZ_BROKEN) /* Such as for SCO UNIX 3.2.2. */
+#undef TIOCGWINSZ
+#endif
+
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE ((unsigned char) 0)
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters. */
+#ifndef CINTR
+#define CINTR Control ('c')
+#endif
+#ifndef CQUIT
+#define CQUIT 28
+#endif
+#ifndef CERASE
+#define CERASE 127
+#endif
+#ifndef CKILL
+#define CKILL Control ('u')
+#endif
+#ifndef CEOF
+#define CEOF Control ('d')
+#endif
+#ifndef CEOL
+#define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+#define CSTART Control ('q')
+#endif
+#ifndef CSTOP
+#define CSTOP Control ('s')
+#endif
+#ifndef CSUSP
+#define CSUSP Control ('z')
+#endif
+#if defined(VEOL2) && !defined(CEOL2)
+#define CEOL2 _POSIX_VDISABLE
+#endif
+#if defined(VSWTCH) && !defined(CSWTCH)
+#define CSWTCH _POSIX_VDISABLE
+#endif
+#if defined(VDSUSP) && !defined (CDSUSP)
+#define CDSUSP Control ('y')
+#endif
+#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
+#define VREPRINT VRPRNT
+#endif
+#if defined(VREPRINT) && !defined(CRPRNT)
+#define CRPRNT Control ('r')
+#endif
+#if defined(VWERASE) && !defined(CWERASE)
+#define CWERASE Control ('w')
+#endif
+#if defined(VLNEXT) && !defined(CLNEXT)
+#define CLNEXT Control ('v')
+#endif
+
+char *visible ();
+unsigned long baud_to_value ();
+int recover_mode ();
+int screen_columns ();
+int set_mode ();
+long integer_arg ();
+speed_t string_to_baud ();
+tcflag_t *mode_type_flag ();
+void display_all ();
+void display_changed ();
+void display_recoverable ();
+void display_settings ();
+void display_speed ();
+void display_window_size ();
+void error ();
+void sane_mode ();
+void set_control_char ();
+void set_speed ();
+void set_window_size ();
+
+/* Which speeds to set. */
+enum speed_setting
+{
+ input_speed, output_speed, both_speeds
+};
+
+/* What to output and how. */
+enum output_type
+{
+ changed, all, recoverable /* Default, -a, -g. */
+};
+
+/* Which member(s) of `struct termios' a mode uses. */
+enum mode_type
+{
+ control, input, output, local, combination
+};
+
+/* Flags for `struct mode_info'. */
+#define SANE_SET 1 /* Set in `sane' mode. */
+#define SANE_UNSET 2 /* Unset in `sane' mode. */
+#define REV 4 /* Can be turned off by prepending `-'. */
+#define OMIT 8 /* Don't display value. */
+
+/* Each mode. */
+struct mode_info
+{
+ char *name; /* Name given on command line. */
+ enum mode_type type; /* Which structure element to change. */
+ char flags; /* Setting and display options. */
+ unsigned long bits; /* Bits to set for this mode. */
+ unsigned long mask; /* Other bits to turn off for this mode. */
+};
+
+struct mode_info mode_info[] =
+{
+ {"parenb", control, REV, PARENB, 0},
+ {"parodd", control, REV, PARODD, 0},
+ {"cs5", control, 0, CS5, CSIZE},
+ {"cs6", control, 0, CS6, CSIZE},
+ {"cs7", control, 0, CS7, CSIZE},
+ {"cs8", control, 0, CS8, CSIZE},
+ {"hupcl", control, REV, HUPCL, 0},
+ {"hup", control, REV | OMIT, HUPCL, 0},
+ {"cstopb", control, REV, CSTOPB, 0},
+ {"cread", control, SANE_SET | REV, CREAD, 0},
+ {"clocal", control, REV, CLOCAL, 0},
+#ifdef CRTSCTS
+ {"crtscts", control, REV, CRTSCTS, 0},
+#endif
+
+ {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0},
+ {"brkint", input, SANE_SET | REV, BRKINT, 0},
+ {"ignpar", input, REV, IGNPAR, 0},
+ {"parmrk", input, REV, PARMRK, 0},
+ {"inpck", input, REV, INPCK, 0},
+ {"istrip", input, REV, ISTRIP, 0},
+ {"inlcr", input, SANE_UNSET | REV, INLCR, 0},
+ {"igncr", input, SANE_UNSET | REV, IGNCR, 0},
+ {"icrnl", input, SANE_SET | REV, ICRNL, 0},
+ {"ixon", input, REV, IXON, 0},
+ {"ixoff", input, SANE_UNSET | REV, IXOFF, 0},
+ {"tandem", input, REV | OMIT, IXOFF, 0},
+#ifdef IUCLC
+ {"iuclc", input, SANE_UNSET | REV, IUCLC, 0},
+#endif
+#ifdef IXANY
+ {"ixany", input, SANE_UNSET | REV, IXANY, 0},
+#endif
+#ifdef IMAXBEL
+ {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0},
+#endif
+
+ {"opost", output, SANE_SET | REV, OPOST, 0},
+#ifdef OLCUC
+ {"olcuc", output, SANE_UNSET | REV, OLCUC, 0},
+#endif
+#ifdef OCRNL
+ {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0},
+#endif
+#ifdef ONLCR
+ {"onlcr", output, SANE_SET | REV, ONLCR, 0},
+#endif
+#ifdef ONOCR
+ {"onocr", output, SANE_UNSET | REV, ONOCR, 0},
+#endif
+#ifdef ONLRET
+ {"onlret", output, SANE_UNSET | REV, ONLRET, 0},
+#endif
+#ifdef OFILL
+ {"ofill", output, SANE_UNSET | REV, OFILL, 0},
+#endif
+#ifdef OFDEL
+ {"ofdel", output, SANE_UNSET | REV, OFDEL, 0},
+#endif
+#ifdef NLDLY
+ {"nl1", output, SANE_UNSET, NL1, NLDLY},
+ {"nl0", output, SANE_SET, NL0, NLDLY},
+#endif
+#ifdef CRDLY
+ {"cr3", output, SANE_UNSET, CR3, CRDLY},
+ {"cr2", output, SANE_UNSET, CR2, CRDLY},
+ {"cr1", output, SANE_UNSET, CR1, CRDLY},
+ {"cr0", output, SANE_SET, CR0, CRDLY},
+#endif
+#ifdef TABDLY
+ {"tab3", output, SANE_UNSET, TAB3, TABDLY},
+ {"tab2", output, SANE_UNSET, TAB2, TABDLY},
+ {"tab1", output, SANE_UNSET, TAB1, TABDLY},
+ {"tab0", output, SANE_SET, TAB0, TABDLY},
+#endif
+#ifdef BSDLY
+ {"bs1", output, SANE_UNSET, BS1, BSDLY},
+ {"bs0", output, SANE_SET, BS0, BSDLY},
+#endif
+#ifdef VTDLY
+ {"vt1", output, SANE_UNSET, VT1, VTDLY},
+ {"vt0", output, SANE_SET, VT0, VTDLY},
+#endif
+#ifdef FFDLY
+ {"ff1", output, SANE_UNSET, FF1, FFDLY},
+ {"ff0", output, SANE_SET, FF0, FFDLY},
+#endif
+
+ {"isig", local, SANE_SET | REV, ISIG, 0},
+ {"icanon", local, SANE_SET | REV, ICANON, 0},
+#ifdef IEXTEN
+ {"iexten", local, SANE_SET | REV, IEXTEN, 0},
+#endif
+ {"echo", local, SANE_SET | REV, ECHO, 0},
+ {"echoe", local, SANE_SET | REV, ECHOE, 0},
+ {"crterase", local, REV | OMIT, ECHOE, 0},
+ {"echok", local, SANE_SET | REV, ECHOK, 0},
+ {"echonl", local, SANE_UNSET | REV, ECHONL, 0},
+ {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0},
+#ifdef XCASE
+ {"xcase", local, SANE_UNSET | REV, XCASE, 0},
+#endif
+#ifdef TOSTOP
+ {"tostop", local, SANE_UNSET | REV, TOSTOP, 0},
+#endif
+#ifdef ECHOPRT
+ {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0},
+ {"prterase", local, REV | OMIT, ECHOPRT, 0},
+#endif
+#ifdef ECHOCTL
+ {"echoctl", local, SANE_SET | REV, ECHOCTL, 0},
+ {"ctlecho", local, REV | OMIT, ECHOCTL, 0},
+#endif
+#ifdef ECHOKE
+ {"echoke", local, SANE_SET | REV, ECHOKE, 0},
+ {"crtkill", local, REV | OMIT, ECHOKE, 0},
+#endif
+
+ {"evenp", combination, REV | OMIT, 0, 0},
+ {"parity", combination, REV | OMIT, 0, 0},
+ {"oddp", combination, REV | OMIT, 0, 0},
+ {"nl", combination, REV | OMIT, 0, 0},
+ {"ek", combination, OMIT, 0, 0},
+ {"sane", combination, OMIT, 0, 0},
+ {"cooked", combination, REV | OMIT, 0, 0},
+ {"raw", combination, REV | OMIT, 0, 0},
+ {"pass8", combination, REV | OMIT, 0, 0},
+ {"litout", combination, REV | OMIT, 0, 0},
+ {"cbreak", combination, REV | OMIT, 0, 0},
+#ifdef IXANY
+ {"decctlq", combination, REV | OMIT, 0, 0},
+#endif
+#ifdef TABDLY
+ {"tabs", combination, REV | OMIT, 0, 0},
+#endif
+#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+ {"lcase", combination, REV | OMIT, 0, 0},
+ {"LCASE", combination, REV | OMIT, 0, 0},
+#endif
+ {"crt", combination, OMIT, 0, 0},
+ {"dec", combination, OMIT, 0, 0},
+
+ {NULL, control, 0, 0, 0}
+};
+
+/* Control character settings. */
+struct control_info
+{
+ char *name; /* Name given on command line. */
+ unsigned char saneval; /* Value to set for `stty sane'. */
+ int offset; /* Offset in c_cc. */
+};
+
+/* Control characters. */
+
+struct control_info control_info[] =
+{
+ {"intr", CINTR, VINTR},
+ {"quit", CQUIT, VQUIT},
+ {"erase", CERASE, VERASE},
+ {"kill", CKILL, VKILL},
+ {"eof", CEOF, VEOF},
+ {"eol", CEOL, VEOL},
+#ifdef VEOL2
+ {"eol2", CEOL2, VEOL2},
+#endif
+#ifdef VSWTCH
+ {"swtch", CSWTCH, VSWTCH},
+#endif
+ {"start", CSTART, VSTART},
+ {"stop", CSTOP, VSTOP},
+ {"susp", CSUSP, VSUSP},
+#ifdef VDSUSP
+ {"dsusp", CDSUSP, VDSUSP},
+#endif
+#ifdef VREPRINT
+ {"rprnt", CRPRNT, VREPRINT},
+#endif
+#ifdef VWERASE
+ {"werase", CWERASE, VWERASE},
+#endif
+#ifdef VLNEXT
+ {"lnext", CLNEXT, VLNEXT},
+#endif
+
+ /* These must be last because of the display routines. */
+ {"min", 1, VMIN},
+ {"time", 0, VTIME},
+ {NULL, 0, 0}
+};
+
+/* The width of the screen, for output wrapping. */
+int max_col;
+
+/* Current position, to know when to wrap. */
+int current_col;
+
+struct option longopts[] =
+{
+ {"all", 0, NULL, 'a'},
+ {"save", 0, NULL, 'g'},
+ {NULL, 0, NULL, 0}
+};
+
+/* The name this program was run with. */
+char *program_name;
+
+/* Print format string MESSAGE and optional args.
+ Wrap to next line first if it won't fit.
+ Print a space first unless MESSAGE will start a new line. */
+
+/* VARARGS */
+void
+#ifdef __STDC__
+wrapf (char *message, ...)
+#else
+wrapf (message, va_alist)
+ char *message;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[1024]; /* Plenty long for our needs. */
+ int buflen;
+
+ VA_START (args, message);
+ vsprintf (buf, message, args);
+ va_end (args);
+ buflen = strlen (buf);
+ if (current_col + buflen >= max_col)
+ {
+ putchar ('\n');
+ current_col = 0;
+ }
+ if (current_col > 0)
+ {
+ putchar (' ');
+ current_col++;
+ }
+ fputs (buf, stdout);
+ current_col += buflen;
+}
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ struct termios mode;
+ enum output_type output_type = changed;
+ int optc;
+
+ program_name = argv[0];
+ opterr = 0;
+
+ while ((optc = getopt_long (argc, argv, "ag", longopts, (int *) 0)) != EOF)
+ {
+ if (optc == 'a')
+ output_type = all;
+ else if (optc == 'g')
+ output_type = recoverable;
+ else
+ break;
+ }
+
+ if (tcgetattr (0, &mode))
+ error (1, errno, "standard input");
+
+ max_col = screen_columns ();
+ current_col = 0;
+
+ if (optind == argc)
+ {
+ if (optc == '?')
+ error (1, 0, "invalid argument `%s'", argv[--optind]);
+ display_settings (output_type, &mode);
+ exit (0);
+ }
+
+ while (optind < argc)
+ {
+ int match_found = 0;
+ int reversed = 0;
+ int i;
+
+ if (argv[optind][0] == '-')
+ {
+ ++argv[optind];
+ reversed = 1;
+ }
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (!strcmp (argv[optind], mode_info[i].name))
+ {
+ match_found = set_mode (&mode_info[i], reversed, &mode);
+ break;
+ }
+ }
+ if (match_found == 0 && reversed)
+ error (1, 0, "invalid argument `%s'", --argv[optind]);
+ if (match_found == 0)
+ {
+ for (i = 0; control_info[i].name != NULL; ++i)
+ {
+ if (!strcmp (argv[optind], control_info[i].name))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ match_found = 1;
+ ++optind;
+ set_control_char (&control_info[i], argv[optind], &mode);
+ break;
+ }
+ }
+ }
+ if (match_found == 0)
+ {
+ if (!strcmp (argv[optind], "ispeed"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_speed (input_speed, argv[optind], &mode);
+ }
+ else if (!strcmp (argv[optind], "ospeed"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_speed (output_speed, argv[optind], &mode);
+ }
+#ifdef TIOCGWINSZ
+ else if (!strcmp (argv[optind], "rows"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_window_size ((int) integer_arg (argv[optind]), -1);
+ }
+ else if (!strcmp (argv[optind], "cols")
+ || !strcmp (argv[optind], "columns"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_window_size (-1, (int) integer_arg (argv[optind]));
+ }
+ else if (!strcmp (argv[optind], "size"))
+ display_window_size (0);
+#endif
+#ifdef HAVE_C_LINE
+ else if (!strcmp (argv[optind], "line"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ mode.c_line = integer_arg (argv[optind]);
+ }
+#endif
+ else if (!strcmp (argv[optind], "speed"))
+ display_speed (&mode, 0);
+ else if (string_to_baud (argv[optind]) != (speed_t) -1)
+ set_speed (both_speeds, argv[optind], &mode);
+ else if (recover_mode (argv[optind], &mode) == 0)
+ error (1, 0, "invalid argument `%s'", argv[optind]);
+ }
+ optind++;
+ }
+
+ if (tcsetattr (0, TCSADRAIN, &mode))
+ error (1, errno, "standard input");
+
+ exit (0);
+}
+
+/* Return 0 if not applied because not reversible; otherwise return 1. */
+
+int
+set_mode (info, reversed, mode)
+ struct mode_info *info;
+ int reversed;
+ struct termios *mode;
+{
+ tcflag_t *bitsp;
+
+ if (reversed && (info->flags & REV) == 0)
+ return 0;
+
+ bitsp = mode_type_flag (info->type, mode);
+
+ if (bitsp == NULL)
+ {
+ /* Combination mode. */
+ if (!strcmp (info->name, "evenp") || !strcmp (info->name, "parity"))
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+ }
+ else if (!strcmp (info->name, "oddp"))
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+ }
+ else if (!strcmp (info->name, "nl"))
+ {
+ if (reversed)
+ {
+ mode->c_iflag = mode->c_iflag | ICRNL & ~INLCR & ~IGNCR;
+ mode->c_oflag = mode->c_oflag
+#ifdef ONLCR
+ | ONLCR
+#endif
+#ifdef OCRNL
+ & ~OCRNL
+#endif
+#ifdef ONLRET
+ & ~ONLRET
+#endif
+ ;
+ }
+ else
+ {
+ mode->c_iflag = mode->c_iflag & ~ICRNL;
+#ifdef ONLCR
+ mode->c_oflag = mode->c_oflag & ~ONLCR;
+#endif
+ }
+ }
+ else if (!strcmp (info->name, "ek"))
+ {
+ mode->c_cc[VERASE] = CERASE;
+ mode->c_cc[VKILL] = CKILL;
+ }
+ else if (!strcmp (info->name, "sane"))
+ sane_mode (mode);
+ else if (!strcmp (info->name, "cbreak"))
+ {
+ if (reversed)
+ mode->c_lflag |= ICANON;
+ else
+ mode->c_lflag &= ~ICANON;
+ }
+ else if (!strcmp (info->name, "pass8"))
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ }
+ }
+ else if (!strcmp (info->name, "litout"))
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ mode->c_oflag |= OPOST;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ mode->c_oflag &= ~OPOST;
+ }
+ }
+ else if (!strcmp (info->name, "raw") || !strcmp (info->name, "cooked"))
+ {
+ if ((info->name[0] == 'r' && reversed)
+ || (info->name[0] == 'c' && !reversed))
+ {
+ /* Cooked mode. */
+ mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+ mode->c_oflag |= OPOST;
+ mode->c_lflag |= ISIG | ICANON;
+#if VMIN == VEOF
+ mode->c_cc[VEOF] = CEOF;
+#endif
+#if VTIME == VEOL
+ mode->c_cc[VEOL] = CEOL;
+#endif
+ }
+ else
+ {
+ /* Raw mode. */
+ mode->c_iflag = 0;
+ mode->c_oflag &= ~OPOST;
+ mode->c_lflag &= ~(ISIG | ICANON
+#ifdef XCASE
+ | XCASE
+#endif
+ );
+ mode->c_cc[VMIN] = 1;
+ mode->c_cc[VTIME] = 0;
+ }
+ }
+#ifdef IXANY
+ else if (!strcmp (info->name, "decctlq"))
+ {
+ if (reversed)
+ mode->c_iflag |= IXANY;
+ else
+ mode->c_iflag &= ~IXANY;
+ }
+#endif
+#ifdef TABDLY
+ else if (!strcmp (info->name, "tabs"))
+ {
+ if (reversed)
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+ else
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+ }
+#endif
+#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+ else if (!strcmp (info->name, "lcase")
+ || !strcmp (info->name, "LCASE"))
+ {
+ if (reversed)
+ {
+ mode->c_lflag &= ~XCASE;
+ mode->c_iflag &= ~IUCLC;
+ mode->c_oflag &= ~OLCUC;
+ }
+ else
+ {
+ mode->c_lflag |= XCASE;
+ mode->c_iflag |= IUCLC;
+ mode->c_oflag |= OLCUC;
+ }
+ }
+#endif
+ else if (!strcmp (info->name, "crt"))
+ mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ ;
+ else if (!strcmp (info->name, "dec"))
+ {
+ mode->c_cc[VINTR] = 3; /* ^C */
+ mode->c_cc[VERASE] = 127; /* DEL */
+ mode->c_cc[VKILL] = 21; /* ^U */
+ mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ ;
+#ifdef IXANY
+ mode->c_iflag &= ~IXANY;
+#endif
+ }
+ }
+ else if (reversed)
+ *bitsp = *bitsp & ~info->mask & ~info->bits;
+ else
+ *bitsp = (*bitsp & ~info->mask) | info->bits;
+
+ return 1;
+}
+
+void
+set_control_char (info, arg, mode)
+ struct control_info *info;
+ char *arg;
+ struct termios *mode;
+{
+ unsigned char value;
+
+ if (!strcmp (info->name, "min") || !strcmp (info->name, "time"))
+ value = integer_arg (arg);
+ else if (arg[0] == '\0' || arg[1] == '\0')
+ value = arg[0];
+ else if (!strcmp (arg, "^-") || !strcmp (arg, "undef"))
+ value = _POSIX_VDISABLE;
+ else if (arg[0] == '^' && arg[1] != '\0') /* Ignore any trailing junk. */
+ {
+ if (arg[1] == '?')
+ value = 127;
+ else
+ value = arg[1] & ~0140; /* Non-letters get weird results. */
+ }
+ else
+ value = integer_arg (arg);
+ mode->c_cc[info->offset] = value;
+}
+
+void
+set_speed (type, arg, mode)
+ enum speed_setting type;
+ char *arg;
+ struct termios *mode;
+{
+ speed_t baud;
+
+ baud = string_to_baud (arg);
+ if (type == input_speed || type == both_speeds)
+ cfsetispeed (mode, baud);
+ if (type == output_speed || type == both_speeds)
+ cfsetospeed (mode, baud);
+}
+
+#ifdef TIOCGWINSZ
+void
+set_window_size (rows, cols)
+ int rows, cols;
+{
+ struct winsize win;
+
+ if (ioctl (0, TIOCGWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+ if (rows >= 0)
+ win.ws_row = rows;
+ if (cols >= 0)
+ win.ws_col = cols;
+ if (ioctl (0, TIOCSWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+}
+
+void
+display_window_size (fancy)
+ int fancy;
+{
+ struct winsize win;
+
+ if (ioctl (0, TIOCGWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+ wrapf (fancy ? "rows %d; columns %d;" : "%d %d\n", win.ws_row, win.ws_col);
+ if (!fancy)
+ current_col = 0;
+}
+#endif
+
+int
+screen_columns ()
+{
+#ifdef TIOCGWINSZ
+ struct winsize win;
+
+ if (ioctl (0, TIOCGWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+ if (win.ws_col > 0)
+ return win.ws_col;
+#endif
+ if (getenv ("COLUMNS"))
+ return atoi (getenv ("COLUMNS"));
+ return 80;
+}
+
+tcflag_t *
+mode_type_flag (type, mode)
+ enum mode_type type;
+ struct termios *mode;
+{
+ switch (type)
+ {
+ case control:
+ return &mode->c_cflag;
+
+ case input:
+ return &mode->c_iflag;
+
+ case output:
+ return &mode->c_oflag;
+
+ case local:
+ return &mode->c_lflag;
+
+ case combination:
+ return NULL;
+ }
+}
+
+void
+display_settings (output_type, mode)
+ enum output_type output_type;
+ struct termios *mode;
+{
+ switch (output_type)
+ {
+ case changed:
+ display_changed (mode);
+ break;
+
+ case all:
+ display_all (mode);
+ break;
+
+ case recoverable:
+ display_recoverable (mode);
+ break;
+ }
+}
+
+void
+display_changed (mode)
+ struct termios *mode;
+{
+ int i;
+ int empty_line;
+ tcflag_t *bitsp;
+ unsigned long mask;
+ enum mode_type prev_type = control;
+
+ display_speed (mode, 1);
+#ifdef HAVE_C_LINE
+ wrapf ("line = %d;", mode->c_line);
+#endif
+ putchar ('\n');
+ current_col = 0;
+
+ empty_line = 1;
+ for (i = 0; strcmp (control_info[i].name, "min"); ++i)
+ {
+ if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
+ continue;
+ empty_line = 0;
+ wrapf ("%s = %s;", control_info[i].name,
+ visible (mode->c_cc[control_info[i].offset]));
+ }
+ if ((mode->c_lflag & ICANON) == 0)
+ {
+ wrapf ("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
+ (int) mode->c_cc[VTIME]);
+ }
+ else if (empty_line == 0)
+ putchar ('\n');
+ current_col = 0;
+
+ empty_line = 1;
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & OMIT)
+ continue;
+ if (mode_info[i].type != prev_type)
+ {
+ if (empty_line == 0)
+ {
+ putchar ('\n');
+ current_col = 0;
+ empty_line = 1;
+ }
+ prev_type = mode_info[i].type;
+ }
+
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ if ((*bitsp & mask) == mode_info[i].bits)
+ {
+ if (mode_info[i].flags & SANE_UNSET)
+ {
+ wrapf ("%s", mode_info[i].name);
+ empty_line = 0;
+ }
+ }
+ else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
+ {
+ wrapf ("-%s", mode_info[i].name);
+ empty_line = 0;
+ }
+ }
+ if (empty_line == 0)
+ putchar ('\n');
+ current_col = 0;
+}
+
+void
+display_all (mode)
+ struct termios *mode;
+{
+ int i;
+ tcflag_t *bitsp;
+ unsigned long mask;
+ enum mode_type prev_type = control;
+
+ display_speed (mode, 1);
+#ifdef TIOCGWINSZ
+ display_window_size (1);
+#endif
+#ifdef HAVE_C_LINE
+ wrapf ("line = %d;", mode->c_line);
+#endif
+ putchar ('\n');
+ current_col = 0;
+
+ for (i = 0; strcmp (control_info[i].name, "min"); ++i)
+ {
+ wrapf ("%s = %s;", control_info[i].name,
+ visible (mode->c_cc[control_info[i].offset]));
+ }
+ wrapf ("min = %d; time = %d;\n", mode->c_cc[VMIN], mode->c_cc[VTIME]);
+ current_col = 0;
+
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & OMIT)
+ continue;
+ if (mode_info[i].type != prev_type)
+ {
+ putchar ('\n');
+ current_col = 0;
+ prev_type = mode_info[i].type;
+ }
+
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ if ((*bitsp & mask) == mode_info[i].bits)
+ wrapf ("%s", mode_info[i].name);
+ else if (mode_info[i].flags & REV)
+ wrapf ("-%s", mode_info[i].name);
+ }
+ putchar ('\n');
+ current_col = 0;
+}
+
+void
+display_speed (mode, fancy)
+ struct termios *mode;
+ int fancy;
+{
+ if (cfgetispeed (mode) == 0 || cfgetispeed (mode) == cfgetospeed (mode))
+ wrapf (fancy ? "speed %lu baud;" : "%lu\n",
+ baud_to_value (cfgetospeed (mode)));
+ else
+ wrapf (fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
+ baud_to_value (cfgetispeed (mode)),
+ baud_to_value (cfgetospeed (mode)));
+ if (!fancy)
+ current_col = 0;
+}
+
+void
+display_recoverable (mode)
+ struct termios *mode;
+{
+ int i;
+
+ printf ("%lx:%lx:%lx:%lx",
+ (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
+ (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
+ for (i = 0; i < NCCS; ++i)
+ printf (":%x", (unsigned int) mode->c_cc[i]);
+ putchar ('\n');
+}
+
+int
+recover_mode (arg, mode)
+ char *arg;
+ struct termios *mode;
+{
+ int i, n;
+ unsigned int chr;
+ unsigned long iflag, oflag, cflag, lflag;
+
+ /* Scan into temporaries since it is too much trouble to figure out
+ the right format for `tcflag_t'. */
+ if (sscanf (arg, "%lx:%lx:%lx:%lx%n",
+ &iflag, &oflag, &cflag, &lflag, &n) != 4)
+ return 0;
+ mode->c_iflag = iflag;
+ mode->c_oflag = oflag;
+ mode->c_cflag = cflag;
+ mode->c_lflag = lflag;
+ arg += n;
+ for (i = 0; i < NCCS; ++i)
+ {
+ if (sscanf (arg, ":%x%n", &chr, &n) != 1)
+ return 0;
+ mode->c_cc[i] = chr;
+ arg += n;
+ }
+ return 1;
+}
+
+struct speed_map
+{
+ char *string; /* ASCII representation. */
+ speed_t speed; /* Internal form. */
+ unsigned long value; /* Numeric value. */
+};
+
+struct speed_map speeds[] =
+{
+ {"0", B0, 0},
+ {"50", B50, 50},
+ {"75", B75, 75},
+ {"110", B110, 110},
+ {"134", B134, 134},
+ {"134.5", B134, 134},
+ {"150", B150, 150},
+ {"200", B200, 200},
+ {"300", B300, 300},
+ {"600", B600, 600},
+ {"1200", B1200, 1200},
+ {"1800", B1800, 1800},
+ {"2400", B2400, 2400},
+ {"4800", B4800, 4800},
+ {"9600", B9600, 9600},
+ {"19200", B19200, 19200},
+ {"38400", B38400, 38400},
+ {"exta", B19200, 19200},
+ {"extb", B38400, 38400},
+#ifdef B57600
+ {"57600", B57600, 57600},
+#endif
+#ifdef B115200
+ {"115200", B115200, 115200},
+#endif
+ {NULL, 0, 0}
+};
+
+speed_t
+string_to_baud (arg)
+ char *arg;
+{
+ int i;
+
+ for (i = 0; speeds[i].string != NULL; ++i)
+ if (!strcmp (arg, speeds[i].string))
+ return speeds[i].speed;
+ return (speed_t) -1;
+}
+
+unsigned long
+baud_to_value (speed)
+ speed_t speed;
+{
+ int i;
+
+ for (i = 0; speeds[i].string != NULL; ++i)
+ if (speed == speeds[i].speed)
+ return speeds[i].value;
+ return 0;
+}
+
+void
+sane_mode (mode)
+ struct termios *mode;
+{
+ int i;
+ tcflag_t *bitsp;
+
+ for (i = 0; control_info[i].name; ++i)
+ {
+#if VMIN == VEOF
+ if (!strcmp (control_info[i].name, "min"))
+ break;
+#endif
+ mode->c_cc[control_info[i].offset] = control_info[i].saneval;
+ }
+
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & SANE_SET)
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
+ }
+ else if (mode_info[i].flags & SANE_UNSET)
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
+ }
+ }
+}
+
+/* Return a string that is the printable representation of character CH. */
+/* Adapted from `cat' by Torbjorn Granlund. */
+
+char *
+visible (ch)
+ unsigned char ch;
+{
+ static char buf[10];
+ char *bpout = buf;
+
+ if (ch == _POSIX_VDISABLE)
+ return "<undef>";
+
+ if (ch >= 32)
+ {
+ if (ch < 127)
+ *bpout++ = ch;
+ else if (ch == 127)
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ else
+ {
+ *bpout++ = 'M',
+ *bpout++ = '-';
+ if (ch >= 128 + 32)
+ {
+ if (ch < 128 + 127)
+ *bpout++ = ch - 128;
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch - 128 + 64;
+ }
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch + 64;
+ }
+ *bpout = '\0';
+ return buf;
+}
+
+/* Parse string S as an integer, using decimal radix by default,
+ but allowing octal and hex numbers as in C. */
+/* From `od' by Richard Stallman. */
+
+long
+integer_arg (s)
+ char *s;
+{
+ long value;
+ int radix = 10;
+ char *p = s;
+ int c;
+
+ if (*p != '0')
+ radix = 10;
+ else if (*++p == 'x')
+ {
+ radix = 16;
+ p++;
+ }
+ else
+ radix = 8;
+
+ value = 0;
+ while (((c = *p++) >= '0' && c <= '9')
+ || (radix == 16 && (c & ~40) >= 'A' && (c & ~40) <= 'Z'))
+ {
+ value *= radix;
+ if (c >= '0' && c <= '9')
+ value += c - '0';
+ else
+ value += (c & ~40) - 'A';
+ }
+
+ if (c == 'b')
+ value *= 512;
+ else if (c == 'B')
+ value *= 1024;
+ else
+ p--;
+
+ if (*p)
+ error (1, 0, "invalid integer argument `%s'", s);
+ return value;
+}
diff --git a/src/su.c b/src/su.c
new file mode 100644
index 000000000..dc29063cc
--- /dev/null
+++ b/src/su.c
@@ -0,0 +1,519 @@
+/* su for GNU. Run a shell with substitute user and group IDs.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Run a shell with the real and effective UID and GID and groups
+ of USER, default `root'.
+
+ The shell run is taken from USER's password entry, /bin/sh if
+ none is specified there. If the account has a password, su
+ prompts for a password unless run by a user with real UID 0.
+
+ Does not change the current directory.
+ Sets `HOME' and `SHELL' from the password entry for USER, and if
+ USER is not root, sets `USER' and `LOGNAME' to USER.
+ The subshell is not a login shell.
+
+ If one or more ARGs are given, they are passed as additional
+ arguments to the subshell.
+
+ Does not handle /bin/sh or other shells specially
+ (setting argv[0] to "-su", passing -c only to certain shells, etc.).
+ I don't see the point in doing that, and it's ugly.
+
+ This program intentionally does not support a "wheel group" that
+ restricts who can su to UID 0 accounts. RMS considers that to
+ be fascist.
+
+ Options:
+ -, -l, --login Make the subshell a login shell.
+ Unset all environment variables except
+ TERM, HOME and SHELL (set as above), and USER
+ and LOGNAME (set unconditionally as above), and
+ set PATH to a default value.
+ Change to USER's home directory.
+ Prepend "-" to the shell's name.
+ -c, --commmand=COMMAND
+ Pass COMMAND to the subshell with a -c option
+ instead of starting an interactive shell.
+ -f, --fast Pass the -f option to the subshell.
+ -m, -p, --preserve-environment
+ Do not change HOME, USER, LOGNAME, SHELL.
+ Run $SHELL instead of USER's shell from /etc/passwd
+ unless not the superuser and USER's shell is
+ restricted.
+ Overridden by --login and --shell.
+ -s, --shell=shell Run SHELL instead of USER's shell from /etc/passwd
+ unless not the superuser and USER's shell is
+ restricted.
+
+ Compile-time options:
+ -DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog.
+ -DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog.
+
+ -DSYSLOG_NON_ROOT Log all su's, not just those to root (UID 0).
+ Never logs attempted su's to nonexistent accounts.
+
+ Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "system.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+void log_su ();
+#else
+#ifdef SYSLOG_SUCCESS
+#undef SYSLOG_SUCCESS
+#endif
+#ifdef SYSLOG_FAILURE
+#undef SYSLOG_FAILURE
+#endif
+#ifdef SYSLOG_NON_ROOT
+#undef SYSLOG_NON_ROOT
+#endif
+#endif
+
+#ifdef _POSIX_VERSION
+#include <limits.h>
+#ifdef NGROUPS_MAX
+#undef NGROUPS_MAX
+#endif
+#define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
+#else /* not _POSIX_VERSION */
+struct passwd *getpwuid ();
+struct group *getgrgid ();
+uid_t getuid ();
+#include <sys/param.h>
+#if !defined(NGROUPS_MAX) && defined(NGROUPS)
+#define NGROUPS_MAX NGROUPS
+#endif
+#endif /* not _POSIX_VERSION */
+
+#ifdef _POSIX_SOURCE
+#define endgrent()
+#define endpwent()
+#endif
+
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+/* The default PATH for simulated logins to non-superuser accounts. */
+#define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
+
+/* The default PATH for simulated logins to superuser accounts. */
+#define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
+
+/* The shell to run if none is given in the user's passwd entry. */
+#define DEFAULT_SHELL "/bin/sh"
+
+/* The user to become if none is specified. */
+#define DEFAULT_USER "root"
+
+char *crypt ();
+char *getpass ();
+char *getusershell ();
+void endusershell ();
+void setusershell ();
+
+char *basename ();
+char *concat ();
+char *xmalloc ();
+char *xrealloc ();
+int correct_password ();
+int elements ();
+int restricted_shell ();
+void change_identity ();
+void error ();
+void modify_environment ();
+void run_shell ();
+void usage ();
+void xputenv ();
+
+extern char **environ;
+
+/* The name this program was run with. */
+char *program_name;
+
+/* If nonzero, pass the `-f' option to the subshell. */
+int fast_startup;
+
+/* If nonzero, simulate a login instead of just starting a shell. */
+int simulate_login;
+
+/* If nonzero, change some environment vars to indicate the user su'd to. */
+int change_environment;
+
+struct option longopts[] =
+{
+ {"command", 1, 0, 'c'},
+ {"fast", 0, &fast_startup, 1},
+ {"login", 0, &simulate_login, 1},
+ {"preserve-environment", 0, &change_environment, 0},
+ {"shell", 1, 0, 's'},
+ {0, 0, 0, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc;
+ char *new_user = DEFAULT_USER;
+ char *command = 0;
+ char **additional_args = 0;
+ char *shell = 0;
+ struct passwd *pw;
+
+ program_name = argv[0];
+ fast_startup = 0;
+ simulate_login = 0;
+ change_environment = 1;
+
+ while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, (int *) 0))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 0:
+ break;
+ case 'c':
+ command = optarg;
+ break;
+ case 'f':
+ fast_startup = 1;
+ break;
+ case 'l':
+ simulate_login = 1;
+ break;
+ case 'm':
+ case 'p':
+ change_environment = 0;
+ break;
+ case 's':
+ shell = optarg;
+ break;
+ default:
+ usage ();
+ }
+ }
+ if (optind < argc && !strcmp (argv[optind], "-"))
+ {
+ simulate_login = 1;
+ ++optind;
+ }
+ if (optind < argc)
+ new_user = argv[optind++];
+ if (optind < argc)
+ additional_args = argv + optind;
+
+ pw = getpwnam (new_user);
+ if (pw == 0)
+ error (1, 0, "user %s does not exist", new_user);
+ endpwent ();
+ if (!correct_password (pw))
+ {
+#ifdef SYSLOG_FAILURE
+ log_su (pw, 0);
+#endif
+ error (1, 0, "incorrect password");
+ }
+#ifdef SYSLOG_SUCCESS
+ else
+ {
+ log_su (pw, 1);
+ }
+#endif
+
+ if (pw->pw_shell == 0 || pw->pw_shell[0] == 0)
+ pw->pw_shell = DEFAULT_SHELL;
+ if (shell == 0 && change_environment == 0)
+ shell = getenv ("SHELL");
+ if (shell != 0 && getuid () && restricted_shell (pw->pw_shell))
+ {
+ /* The user being su'd to has a nonstandard shell, and so is
+ probably a uucp account or has restricted access. Don't
+ compromise the account by allowing access with a standard
+ shell. */
+ error (0, 0, "using restricted shell %s", pw->pw_shell);
+ shell = 0;
+ }
+ if (shell == 0)
+ shell = pw->pw_shell;
+ shell = strcpy (xmalloc (strlen (shell) + 1), shell);
+ modify_environment (pw, shell);
+
+ change_identity (pw);
+ if (simulate_login && chdir (pw->pw_dir))
+ error (0, errno, "warning: cannot change directory to %s", pw->pw_dir);
+ run_shell (shell, command, additional_args);
+}
+
+/* Ask the user for a password.
+ Return 1 if the user gives the correct password for entry PW,
+ 0 if not. Return 1 without asking for a password if run by UID 0
+ or if PW has an empty password. */
+
+int
+correct_password (pw)
+ struct passwd *pw;
+{
+ char *unencrypted, *encrypted, *correct;
+#ifdef HAVE_SHADOW_H
+ /* Shadow passwd stuff for SVR3 and maybe other systems. */
+ struct spwd *sp = getspnam (pw->pw_name);
+
+ endspent ();
+ if (sp)
+ correct = sp->sp_pwdp;
+ else
+#endif
+ correct = pw->pw_passwd;
+
+ if (getuid () == 0 || correct == 0 || correct[0] == '\0')
+ return 1;
+
+ unencrypted = getpass ("Password:");
+ encrypted = crypt (unencrypted, correct);
+ bzero (unencrypted, strlen (unencrypted));
+ return strcmp (encrypted, correct) == 0;
+}
+
+/* Update `environ' for the new shell based on PW, with SHELL being
+ the value for the SHELL environment variable. */
+
+void
+modify_environment (pw, shell)
+ struct passwd *pw;
+ char *shell;
+{
+ char *term;
+
+ if (simulate_login)
+ {
+ /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
+ Unset all other environment variables. */
+ term = getenv ("TERM");
+ environ = (char **) xmalloc (2 * sizeof (char *));
+ environ[0] = 0;
+ if (term)
+ xputenv (concat ("TERM", "=", term));
+ xputenv (concat ("HOME", "=", pw->pw_dir));
+ xputenv (concat ("SHELL", "=", shell));
+ xputenv (concat ("USER", "=", pw->pw_name));
+ xputenv (concat ("LOGNAME", "=", pw->pw_name));
+ xputenv (concat ("PATH", "=", pw->pw_uid
+ ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH));
+ }
+ else
+ {
+ /* Set HOME, SHELL, and if not becoming a super-user,
+ USER and LOGNAME. */
+ if (change_environment)
+ {
+ xputenv (concat ("HOME", "=", pw->pw_dir));
+ xputenv (concat ("SHELL", "=", shell));
+ if (pw->pw_uid)
+ {
+ xputenv (concat ("USER", "=", pw->pw_name));
+ xputenv (concat ("LOGNAME", "=", pw->pw_name));
+ }
+ }
+ }
+}
+
+/* Become the user and group(s) specified by PW. */
+
+void
+change_identity (pw)
+ struct passwd *pw;
+{
+#ifdef NGROUPS_MAX
+ errno = 0;
+ if (initgroups (pw->pw_name, pw->pw_gid) == -1)
+ error (1, errno, "cannot set groups");
+ endgrent ();
+#endif
+ if (setgid (pw->pw_gid))
+ error (1, errno, "cannot set group id");
+ if (setuid (pw->pw_uid))
+ error (1, errno, "cannot set user id");
+}
+
+/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
+ If COMMAND is nonzero, pass it to the shell with the -c option.
+ If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
+ arguments. */
+
+void
+run_shell (shell, command, additional_args)
+ char *shell;
+ char *command;
+ char **additional_args;
+{
+ char **args;
+ int argno = 1;
+
+ if (additional_args)
+ args = (char **) xmalloc (sizeof (char *)
+ * (10 + elements (additional_args)));
+ else
+ args = (char **) xmalloc (sizeof (char *) * 10);
+ if (simulate_login)
+ {
+ args[0] = xmalloc (strlen (shell) + 2);
+ args[0][0] = '-';
+ strcpy (args[0] + 1, basename (shell));
+ }
+ else
+ args[0] = basename (shell);
+ if (fast_startup)
+ args[argno++] = "-f";
+ if (command)
+ {
+ args[argno++] = "-c";
+ args[argno++] = command;
+ }
+ if (additional_args)
+ for (; *additional_args; ++additional_args)
+ args[argno++] = *additional_args;
+ args[argno] = 0;
+ execv (shell, args);
+ error (1, errno, "cannot run %s", shell);
+}
+
+#if defined (SYSLOG_SUCCESS) || defined (SYSLOG_FAILURE)
+/* Log the fact that someone has run su to the user given by PW;
+ if SUCCESSFUL is nonzero, they gave the correct password, etc. */
+
+void
+log_su (pw, successful)
+ struct passwd *pw;
+ int successful;
+{
+ char *new_user, *old_user, *tty;
+
+#ifndef SYSLOG_NON_ROOT
+ if (pw->pw_uid)
+ return;
+#endif
+ new_user = pw->pw_name;
+ /* The utmp entry (via getlogin) is probably the best way to identify
+ the user, especially if someone su's from a su-shell. */
+ old_user = getlogin ();
+ if (old_user == 0)
+ old_user = "";
+ tty = ttyname (2);
+ if (tty == 0)
+ tty = "";
+ /* 4.2BSD openlog doesn't have the third parameter. */
+ openlog (basename (program_name), 0
+#ifdef LOG_AUTH
+ , LOG_AUTH
+#endif
+ );
+ syslog (LOG_NOTICE,
+#ifdef SYSLOG_NON_ROOT
+ "%s(to %s) %s on %s",
+#else
+ "%s%s on %s",
+#endif
+ successful ? "" : "FAILED SU ",
+#ifdef SYSLOG_NON_ROOT
+ new_user,
+#endif
+ old_user, tty);
+ closelog ();
+}
+#endif
+
+/* Return 1 if SHELL is a restricted shell (one not returned by
+ getusershell), else 0, meaning it is a standard shell. */
+
+int
+restricted_shell (shell)
+ char *shell;
+{
+ char *line;
+
+ setusershell ();
+ while (line = getusershell ())
+ {
+ if (*line != '#' && strcmp (line, shell) == 0)
+ {
+ endusershell ();
+ return 0;
+ }
+ }
+ endusershell ();
+ return 1;
+}
+
+/* Return the number of elements in ARR, a null-terminated array. */
+
+int
+elements (arr)
+ char **arr;
+{
+ int n = 0;
+
+ for (n = 0; *arr; ++arr)
+ ++n;
+ return n;
+}
+
+/* Add VAL to the environment, checking for out of memory errors. */
+
+void
+xputenv (val)
+ char *val;
+{
+ if (putenv (val))
+ error (1, 0, "virtual memory exhausted");
+}
+
+/* Return a newly-allocated string whose contents concatenate
+ those of S1, S2, S3. */
+
+char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+ char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
+
+ strcpy (result, s1);
+ strcpy (result + len1, s2);
+ strcpy (result + len1 + len2, s3);
+ result[len1 + len2 + len3] = 0;
+
+ return result;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-flmp] [-c command] [-s shell] [--login] [--fast]\n\
+ [--preserve-environment] [--command=command] [--shell=shell] [-]\n\
+ [user [arg...]]\n", program_name);
+ exit (1);
+}
diff --git a/src/tee.c b/src/tee.c
new file mode 100644
index 000000000..721014b5d
--- /dev/null
+++ b/src/tee.c
@@ -0,0 +1,155 @@
+/* tee - read from standard input and write to standard output and files.
+ Copyright (C) 1985, 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Mike Parker, Richard M. Stallman, and David MacKenzie */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <getopt.h>
+#include "system.h"
+
+char *xmalloc ();
+int tee ();
+void error ();
+void xwrite ();
+
+/* If nonzero, append to output files rather than truncating them. */
+int append;
+
+/* If nonzero, ignore interrupts. */
+int ignore_interrupts;
+
+/* The name that this program was run with. */
+char *program_name;
+
+struct option long_options[] =
+{
+ {"append", 0, NULL, 'a'},
+ {"ignore-interrupts", 0, NULL, 'i'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int errs;
+ int optc;
+
+ program_name = argv[0];
+ append = 0;
+ ignore_interrupts = 0;
+
+ while ((optc = getopt_long (argc, argv, "ai", long_options, (int *) 0))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 'a':
+ append = 1;
+ break;
+ case 'i':
+ ignore_interrupts = 1;
+ break;
+ default:
+ fprintf (stderr, "\
+Usage: %s [-ai] [--append] [--ignore-interrupts] [file...]\n",
+ program_name);
+ exit (1);
+ }
+ }
+
+ if (ignore_interrupts)
+#ifdef _POSIX_VERSION
+ {
+ struct sigaction sigact;
+
+ sigact.sa_handler = SIG_IGN;
+ sigemptyset (&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction (SIGINT, &sigact, NULL);
+ }
+#else /* !_POSIX_VERSION */
+ signal (SIGINT, SIG_IGN);
+#endif /* _POSIX_VERSION */
+
+ errs = tee (argc - optind, &argv[optind]);
+ if (close (0) == -1)
+ error (1, errno, "standard input");
+ if (close (1) == -1)
+ error (1, errno, "standard output");
+ exit (errs);
+}
+
+/* Copy the standard input into each of the NFILES files in FILES
+ and into the standard output.
+ Return 0 if successful, 1 if any errors occur. */
+
+int
+tee (nfiles, files)
+ int nfiles;
+ char **files;
+{
+ int *descriptors;
+ char buffer[BUFSIZ];
+ register int bytes_read, i, ret = 0, mode;
+
+ if (nfiles)
+ descriptors = (int *) xmalloc (nfiles * sizeof (int));
+ mode = O_WRONLY | O_CREAT;
+ if (append)
+ mode |= O_APPEND;
+ else
+ mode |= O_TRUNC;
+
+ for (i = 0; i < nfiles; i++)
+ {
+ descriptors[i] = open (files[i], mode, 0666);
+ if (descriptors[i] == -1)
+ {
+ error (0, errno, "%s", files[i]);
+ ret = 1;
+ }
+ }
+
+ while ((bytes_read = read (0, buffer, sizeof buffer)) > 0)
+ {
+ xwrite (1, buffer, bytes_read);
+ for (i = 0; i < nfiles; i++)
+ if (descriptors[i] != -1)
+ xwrite (descriptors[i], buffer, bytes_read);
+ }
+ if (bytes_read == -1)
+ {
+ error (0, errno, "read error");
+ ret = 1;
+ }
+
+ for (i = 0; i < nfiles; i++)
+ if (descriptors[i] != -1 && close (descriptors[i]) == -1)
+ {
+ error (0, errno, "%s", files[i]);
+ ret = 1;
+ }
+
+ if (nfiles)
+ free (descriptors);
+
+ return ret;
+}
diff --git a/src/test.c b/src/test.c
new file mode 100644
index 000000000..a78af0968
--- /dev/null
+++ b/src/test.c
@@ -0,0 +1,1054 @@
+/* GNU test program (ksb and mjb) */
+
+/* Modified to run with the GNU shell by bfox. */
+
+/* Copyright (C) 1987, 1988, 1989, 1990, 1991 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. */
+
+/* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get
+ the shell builtin version. */
+/* #define TEST_STANDALONE */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#if !defined (TEST_STANDALONE)
+# include "shell.h"
+# include "posixstat.h"
+# include "filecntl.h"
+#else /* TEST_STANDALONE */
+# include "system.h"
+# if !defined (S_IXUGO)
+# define S_IXUGO 0111
+# endif /* S_IXUGO */
+# if defined (_POSIX_VERSION)
+# include <limits.h>
+# else /* !_POSIX_VERSION */
+# include <sys/param.h>
+# endif /* _POSIX_VERSION */
+# if defined (NGROUPS_MAX) || defined (_SC_NGROUPS_MAX) || defined (NGROUPS)
+# define HAVE_GETGROUPS
+# endif /* NGROUPS_MAX || _SC_NGROUPS_MAX || NGROUPS */
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#define digit(c) ((c) >= '0' && (c) <= '9')
+#define digit_value(c) ((c) - '0')
+char *program_name;
+#endif /* TEST_STANDALONE */
+
+#if !defined (_POSIX_VERSION)
+# include <sys/file.h>
+#endif /* !_POSIX_VERSION */
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if !defined (STREQ)
+# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
+#endif /* !STREQ */
+
+#if !defined (member)
+# define member(c, s) (int)((c) ? index ((s), (c)) : 0)
+#endif /* !member */
+
+#if defined (_POSIX_VERSION)
+
+/* Even though SunOS 4, Ultrix 4, and 386BSD are mostly POSIX.1 compliant,
+ their getgroups system call (except in the `System V' environment, which
+ is troublesome in other ways) fills in an array of int, not gid_t
+ (which is `short' on those systems). Kludge, kludge. */
+
+#if !defined(sun) && !defined(ultrix) && !defined(__386BSD__)
+#define GETGROUPS_T gid_t
+#else
+#define GETGROUPS_T int
+#endif
+#else /* !_POSIX_VERSION */
+#define GETGROUPS_T int
+#endif /* !_POSIX_VERSION */
+
+extern gid_t getgid (), getegid ();
+extern uid_t geteuid ();
+
+#if !defined (R_OK)
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#define F_OK 0
+#endif /* R_OK */
+
+/* The following few defines control the truth and false output of each stage.
+ TRUE and FALSE are what we use to compute the final output value.
+ SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
+ TRUTH_OR is how to do logical or with TRUE and FALSE.
+ TRUTH_AND is how to do logical and with TRUE and FALSE..
+ Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
+ SHELL_BOOLEAN = (!value). */
+#define TRUE 1
+#define FALSE 0
+#define SHELL_BOOLEAN(value) (!(value))
+#define TRUTH_OR(a, b) ((a) | (b))
+#define TRUTH_AND(a, b) ((a) & (b))
+
+#if defined (TEST_STANDALONE)
+# define test_exit(val) exit (val)
+#else
+ static jmp_buf test_exit_buf;
+ static int test_error_return = 0;
+# define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
+#endif /* !TEST_STANDALONE */
+
+char *xrealloc ();
+
+static int pos; /* The offset of the current argument in ARGV. */
+static int argc; /* The number of arguments present in ARGV. */
+static char **argv; /* The argument list. */
+
+static int unop ();
+static int binop ();
+static int unary_operator ();
+static int binary_operator ();
+static int two_arguments ();
+static int three_arguments ();
+static int posixtest ();
+
+static int expr ();
+static int term ();
+static int and ();
+static int or ();
+
+static void
+test_syntax_error (format, arg)
+ char *format, *arg;
+{
+ fprintf (stderr, "%s: ", argv[0]);
+ fprintf (stderr, format, arg);
+ fflush (stderr);
+ test_exit (SHELL_BOOLEAN (FALSE));
+}
+
+/* A wrapper for stat () which disallows pathnames that are empty strings. */
+static int
+test_stat (path, finfo)
+ char *path;
+ struct stat *finfo;
+{
+ if (*path == '\0')
+ {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (stat (path, finfo));
+}
+
+/* Do the same thing access(2) does, but use the effective uid and gid,
+ and don't make the mistake of telling root that any file is
+ executable. */
+static int
+eaccess (path, mode)
+ char *path;
+ int mode;
+{
+ extern int group_member ();
+ struct stat st;
+ static int euid = -1;
+
+ if (test_stat (path, &st) < 0)
+ return (-1);
+
+ if (euid == -1)
+ euid = geteuid ();
+
+ if (euid == 0)
+ {
+ /* Root can read or write any file. */
+ if (mode != X_OK)
+ return (0);
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return (0);
+ }
+
+ if (st.st_uid == euid) /* owner */
+ mode <<= 6;
+ else if (group_member (st.st_gid))
+ mode <<= 3;
+
+ if (st.st_mode & mode)
+ return (0);
+
+ return (-1);
+}
+
+#if defined (HAVE_GETGROUPS)
+/* The number of groups that this user is a member of. */
+static int ngroups = 0;
+static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL;
+static int default_group_array_size = 0;
+#endif /* HAVE_GETGROUPS */
+
+/* Return non-zero if GID is one that we have in our groups list. */
+int
+group_member (gid)
+ gid_t gid;
+{
+#if !defined (HAVE_GETGROUPS)
+ return ((gid == getgid ()) || (gid == getegid ()));
+#else
+ register int i;
+
+ /* getgroups () returns the number of elements that it was able to
+ place into the array. We simply continue to call getgroups ()
+ until the number of elements placed into the array is smaller than
+ the physical size of the array. */
+
+ while (ngroups == default_group_array_size)
+ {
+ default_group_array_size += 64;
+
+ group_array = (GETGROUPS_T *)
+ xrealloc (group_array,
+ default_group_array_size * sizeof (GETGROUPS_T));
+
+ ngroups = getgroups (default_group_array_size, group_array);
+ }
+
+ /* In case of error, the user loses. */
+ if (ngroups < 0)
+ return (0);
+
+ /* Search through the list looking for GID. */
+ for (i = 0; i < ngroups; i++)
+ if (gid == group_array[i])
+ return (1);
+
+ return (0);
+#endif /* HAVE_GETGROUPS */
+}
+
+/* Increment our position in the argument list. Check that we're not
+ past the end of the argument list. This check is supressed if the
+ argument is FALSE. Made a macro for efficiency. */
+#if !defined (lint)
+#define advance(f) (++pos, f && (pos < argc ? 0 : beyond()))
+#endif
+
+#if !defined (advance)
+static int
+advance (f)
+ int f;
+{
+ ++pos;
+
+ if (f && pos >= argc)
+ beyond ();
+}
+#endif /* advance */
+
+#define unary_advance() (advance (1),++pos)
+
+/*
+ * beyond - call when we're beyond the end of the argument list (an
+ * error condition)
+ */
+static int
+beyond ()
+{
+ test_syntax_error ("argument expected\n", (char *)NULL);
+}
+
+/* Syntax error for when an integer argument was expected, but
+ something else was found. */
+static void
+integer_expected_error (pch)
+ char *pch;
+{
+ test_syntax_error ("integer expression expected %s\n", pch);
+}
+
+/* Return non-zero if the characters pointed to by STRING constitute a
+ valid number. Stuff the converted number into RESULT if RESULT is
+ a non-null pointer to a long. */
+static int
+isint (string, result)
+ register char *string;
+ long *result;
+{
+ int sign;
+ long value;
+
+ sign = 1;
+ value = 0;
+
+ if (result)
+ *result = 0;
+
+ /* Skip leading whitespace characters. */
+ while (whitespace (*string))
+ string++;
+
+ if (!*string)
+ return (0);
+
+ /* We allow leading `-' or `+'. */
+ if (*string == '-' || *string == '+')
+ {
+ if (!digit (string[1]))
+ return (0);
+
+ if (*string == '-')
+ sign = -1;
+
+ string++;
+ }
+
+ while (digit (*string))
+ {
+ if (result)
+ value = (value * 10) + digit_value (*string);
+ string++;
+ }
+
+ /* Skip trailing whitespace, if any. */
+ while (whitespace (*string))
+ string++;
+
+ /* Error if not at end of string. */
+ if (*string)
+ return (0);
+
+ if (result)
+ {
+ value *= sign;
+ *result = value;
+ }
+
+ return (1);
+}
+
+/* Find the modification time of FILE, and stuff it into AGE, a pointer
+ to a long. Return non-zero if successful, else zero. */
+static int
+age_of (filename, age)
+ char *filename;
+ long *age;
+{
+ struct stat finfo;
+
+ if (test_stat (filename, &finfo) < 0)
+ return (0);
+
+ if (age)
+ *age = finfo.st_mtime;
+
+ return (1);
+}
+
+/*
+ * term - parse a term and return 1 or 0 depending on whether the term
+ * evaluates to true or false, respectively.
+ *
+ * term ::=
+ * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
+ * '-'('L'|'x') filename
+ * '-t' [ int ]
+ * '-'('z'|'n') string
+ * string
+ * string ('!='|'=') string
+ * <int> '-'(eq|ne|le|lt|ge|gt) <int>
+ * file '-'(nt|ot|ef) file
+ * '(' <expr> ')'
+ * int ::=
+ * '-l' string
+ * positive and negative integers
+ */
+static int
+term ()
+{
+ int value;
+
+ if (pos >= argc)
+ beyond ();
+
+ /* Deal with leading "not"'s. */
+ if ('!' == argv[pos][0] && '\000' == argv[pos][1])
+ {
+ value = FALSE;
+ while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
+ {
+ advance (1);
+ value ^= (TRUE);
+ }
+
+ return (value ^ (term ()));
+ }
+
+ /* A paren-bracketed argument. */
+ if (argv[pos][0] == '(' && !argv[pos][1])
+ {
+ advance (1);
+ value = expr ();
+ if (argv[pos][0] != ')' || argv[pos][1])
+ test_syntax_error ("')' expected, found %s\n", argv[pos]);
+ advance (0);
+ return (TRUE == (value));
+ }
+
+ /* are there enough arguments left that this could be dyadic? */
+ if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
+ ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
+ value = binary_operator ();
+
+ /* Might be a switch type argument */
+ else if ('-' == argv[pos][0] && 0 == argv[pos][2])
+ {
+ if (unop (argv[pos][1]))
+ value = unary_operator ();
+ else
+ test_syntax_error ("%s: unary operator expected\n", argv[pos]);
+ }
+ else
+ {
+ value = (argv[pos][0] != '\0');
+ advance (0);
+ }
+
+ return (value);
+}
+
+static int
+binary_operator ()
+{
+ register int op;
+ struct stat stat_buf, stat_spare;
+ long int l, r, value;
+ /* Are the left and right integer expressions of the form '-l string'? */
+ int l_is_l, r_is_l;
+
+ if (strcmp (argv[pos], "-l") == 0)
+ {
+ l_is_l = 1;
+ op = pos + 2;
+
+ /* Make sure that OP is still a valid binary operator. */
+ if ((op >= argc - 1) || (binop (argv[op]) == 0))
+ test_syntax_error ("%s: binary operator expected\n", argv[op]);
+
+ advance (0);
+ }
+ else
+ {
+ l_is_l = 0;
+ op = pos + 1;
+ }
+
+ if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
+ {
+ r_is_l = 1;
+ advance (0);
+ }
+ else
+ r_is_l = 0;
+
+ if (argv[op][0] == '-')
+ {
+ /* check for eq, nt, and stuff */
+ switch (argv[op][1])
+ {
+ default:
+ break;
+
+ case 'l':
+ if (argv[op][2] == 't' && !argv[op][3])
+ {
+ /* lt */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -lt");
+ }
+
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -lt");
+ }
+ pos += 3;
+ return (TRUE == (l < r));
+ }
+
+ if (argv[op][2] == 'e' && !argv[op][3])
+ {
+ /* le */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -le");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -le");
+ }
+ pos += 3;
+ return (TRUE == (l <= r));
+ }
+ break;
+
+ case 'g':
+ if (argv[op][2] == 't' && !argv[op][3])
+ {
+ /* gt integer greater than */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -gt");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -gt");
+ }
+ pos += 3;
+ return (TRUE == (l > r));
+ }
+
+ if (argv[op][2] == 'e' && !argv[op][3])
+ {
+ /* ge - integer greater than or equal to */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -ge");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -ge");
+ }
+ pos += 3;
+ return (TRUE == (l >= r));
+ }
+ break;
+
+ case 'n':
+ if (argv[op][2] == 't' && !argv[op][3])
+ {
+ /* nt - newer than */
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
+ if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
+ return (TRUE == (l > r));
+ else
+ return (FALSE);
+ }
+
+ if (argv[op][2] == 'e' && !argv[op][3])
+ {
+ /* ne - integer not equal */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -ne");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -ne");
+ }
+ pos += 3;
+ return (TRUE == (l != r));
+ }
+ break;
+
+ case 'e':
+ if (argv[op][2] == 'q' && !argv[op][3])
+ {
+ /* eq - integer equal */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -eq");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -eq");
+ }
+ pos += 3;
+ return (TRUE == (l == r));
+ }
+
+ if (argv[op][2] == 'f' && !argv[op][3])
+ {
+ /* ef - hard link? */
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error ("-ef does not accept -l\n", (char *)NULL);
+ if (stat (argv[op - 1], &stat_buf) < 0)
+ return (FALSE);
+ if (stat (argv[op + 1], &stat_spare) < 0)
+ return (FALSE);
+ return (TRUE ==
+ (stat_buf.st_dev == stat_spare.st_dev &&
+ stat_buf.st_ino == stat_spare.st_ino));
+ }
+ break;
+
+ case 'o':
+ if ('t' == argv[op][2] && '\000' == argv[op][3])
+ {
+ /* ot - older than */
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
+ if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
+ return (TRUE == (l < r));
+ return (FALSE);
+ }
+ break;
+ }
+ test_syntax_error ("unknown binary operator", argv[op]);
+ }
+
+ if (argv[op][0] == '=' && !argv[op][1])
+ {
+ value = (strcmp (argv[pos], argv[pos + 2]) == 0);
+ pos += 3;
+ return (TRUE == value);
+ }
+
+ if (strcmp (argv[op], "!=") == 0)
+ {
+ value = (strcmp (argv[pos], argv[pos + 2]) != 0);
+ pos += 3;
+ return (TRUE == value);
+ }
+}
+
+static int
+unary_operator ()
+{
+ long r, value;
+ struct stat stat_buf;
+
+ switch (argv[pos][1])
+ {
+ default:
+ return (FALSE);
+
+ /* All of the following unary operators use unary_advance (), which
+ checks to make sure that there is an argument, and then advances
+ pos right past it. This means that pos - 1 is the location of the
+ argument. */
+
+ case 'a': /* file exists in the file system? */
+ case 'e':
+ unary_advance ();
+ value = -1 != test_stat (argv[pos - 1], &stat_buf);
+ return (TRUE == value);
+
+ case 'r': /* file is readable? */
+ unary_advance ();
+ value = -1 != eaccess (argv[pos - 1], R_OK);
+ return (TRUE == value);
+
+ case 'w': /* File is writeable? */
+ unary_advance ();
+ value = -1 != eaccess (argv[pos - 1], W_OK);
+ return (TRUE == value);
+
+ case 'x': /* File is executable? */
+ unary_advance ();
+ value = -1 != eaccess (argv[pos - 1], X_OK);
+ return (TRUE == value);
+
+ case 'O': /* File is owned by you? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (geteuid () == stat_buf.st_uid));
+
+ case 'G': /* File is owned by your group? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (getegid () == stat_buf.st_gid));
+
+ case 'f': /* File is a file? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ /* Under POSIX, -f is true if the given file exists
+ and is a regular file. */
+ return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
+ (0 == (stat_buf.st_mode & S_IFMT))));
+
+ case 'd': /* File is a directory? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISDIR (stat_buf.st_mode)));
+
+ case 's': /* File has something in it? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (stat_buf.st_size > (off_t) 0));
+
+ case 'S': /* File is a socket? */
+#if !defined (S_ISSOCK)
+ return (FALSE);
+#else
+ unary_advance ();
+
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
+#endif /* S_ISSOCK */
+
+ case 'c': /* File is character special? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISCHR (stat_buf.st_mode)));
+
+ case 'b': /* File is block special? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISBLK (stat_buf.st_mode)));
+
+ case 'p': /* File is a named pipe? */
+ unary_advance ();
+#ifndef S_ISFIFO
+ return (FALSE);
+#else
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+ return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
+#endif /* S_ISFIFO */
+
+ case 'L': /* Same as -h */
+ /*FALLTHROUGH*/
+
+ case 'h': /* File is a symbolic link? */
+ unary_advance ();
+#ifndef S_ISLNK
+ return (FALSE);
+#else
+ /* An empty filename is not a valid pathname. */
+ if ((argv[pos - 1][0] == '\0') ||
+ (lstat (argv[pos - 1], &stat_buf) < 0))
+ return (FALSE);
+
+ return (TRUE == (S_ISLNK (stat_buf.st_mode)));
+#endif /* S_IFLNK */
+
+ case 'u': /* File is setuid? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
+
+ case 'g': /* File is setgid? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
+
+ case 'k': /* File has sticky bit set? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+#if !defined (S_ISVTX)
+ /* This is not Posix, and is not defined on some Posix systems. */
+ return (FALSE);
+#else
+ return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
+#endif
+
+ case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
+ advance (0);
+ if (pos < argc && isint (argv[pos], &r))
+ {
+ advance (0);
+ return (TRUE == (isatty ((int) r)));
+ }
+ return (TRUE == (isatty (1)));
+
+ case 'n': /* True if arg has some length. */
+ unary_advance ();
+ return (TRUE == (argv[pos - 1][0] != 0));
+
+ case 'z': /* True if arg has no length. */
+ unary_advance ();
+ return (TRUE == (argv[pos - 1][0] == '\0'));
+ }
+}
+
+/*
+ * and:
+ * term
+ * term '-a' and
+ */
+static int
+and ()
+{
+ int value;
+
+ value = term ();
+ while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
+ {
+ advance (0);
+ value = TRUTH_AND (value, and ());
+ }
+ return (TRUE == value);
+}
+
+/*
+ * or:
+ * and
+ * and '-o' or
+ */
+static int
+or ()
+{
+ int value;
+
+ value = and ();
+
+ while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
+ {
+ advance (0);
+ value = TRUTH_OR (value, or ());
+ }
+
+ return (TRUE == value);
+}
+
+/*
+ * expr:
+ * or
+ */
+static int
+expr ()
+{
+ if (pos >= argc)
+ beyond ();
+
+ return (FALSE ^ (or ())); /* Same with this. */
+}
+
+/* Return TRUE if S is one of the test command's binary operators. */
+static int
+binop (s)
+ char *s;
+{
+ return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
+ (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
+ (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
+ (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
+}
+
+/* Return non-zero if OP is one of the test command's unary operators. */
+static int
+unop (op)
+ int op;
+{
+ return (member (op, "abcdefgkLhprsStuwxOGnz"));
+}
+
+static int
+two_arguments ()
+{
+ int value;
+
+ if (STREQ (argv[pos], "!"))
+ value = strlen (argv[pos+1]) == 0;
+ else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
+ {
+ if (unop (argv[pos][1]))
+ value = unary_operator ();
+ else
+ test_syntax_error ("%s: unary operator expected\n", argv[pos]);
+ }
+ else
+ beyond ();
+ return (value);
+}
+
+static int
+three_arguments ()
+{
+ int value;
+
+ if (STREQ (argv[pos], "!"))
+ {
+ advance (1);
+ value = !two_arguments ();
+ }
+ else if (binop (argv[pos+1]))
+ {
+ value = binary_operator ();
+ pos = argc;
+ }
+ else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
+ (argv[pos][0] == '('))
+ value = expr ();
+ else
+ test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
+ return (value);
+}
+
+/* This is an implementation of a Posix.2 proposal by David Korn. */
+static int
+posixtest ()
+{
+ int value;
+
+ switch (argc - 1) /* one extra passed in */
+ {
+ case 0:
+ value = FALSE;
+ pos = argc;
+ break;
+
+ case 1:
+ value = strlen (argv[1]) != 0;
+ pos = argc;
+ break;
+
+ case 2:
+ value = two_arguments ();
+ pos = argc;
+ break;
+
+ case 3:
+ value = three_arguments ();
+ break;
+
+ case 4:
+ if (STREQ (argv[pos], "!"))
+ {
+ advance (1);
+ value = !three_arguments ();
+ break;
+ }
+ /* FALLTHROUGH */
+ case 5:
+ default:
+ value = expr ();
+ }
+
+ return (value);
+}
+
+/*
+ * [:
+ * '[' expr ']'
+ * test:
+ * test expr
+ */
+int
+#if defined (TEST_STANDALONE)
+main (margc, margv)
+#else
+test_command (margc, margv)
+#endif /* !TEST_STANDALONE */
+ int margc;
+ char **margv;
+{
+ auto int value;
+ int expr ();
+
+#if !defined (TEST_STANDALONE)
+ int code;
+
+ code = setjmp (test_exit_buf);
+
+ if (code)
+ return (test_error_return);
+#else /* TEST_STANDALONE */
+ program_name = margv[0];
+#endif /* TEST_STANDALONE */
+
+ argv = margv;
+
+ if (margv[0] && strcmp (margv[0], "[") == 0)
+ {
+ --margc;
+
+ if (margc < 2)
+ test_exit (SHELL_BOOLEAN (FALSE));
+
+ if (margv[margc] && strcmp (margv[margc], "]") != 0)
+ test_syntax_error ("missing `]'\n", (char *)NULL);
+ }
+
+ argc = margc;
+ pos = 1;
+
+ if (pos >= argc)
+ test_exit (SHELL_BOOLEAN (FALSE));
+
+ value = posixtest ();
+
+ if (pos != argc)
+ test_syntax_error ("too many arguments\n", (char *)NULL);
+
+ test_exit (SHELL_BOOLEAN (value));
+}
diff --git a/src/tty.c b/src/tty.c
new file mode 100644
index 000000000..056c7da8d
--- /dev/null
+++ b/src/tty.c
@@ -0,0 +1,88 @@
+/* tty -- print the path of the terminal connected to standard input
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Displays "not a tty" if stdin is not a terminal.
+ Displays nothing if -s option is given.
+ Exit status 0 if stdin is a tty, 1 if not, 2 if usage error.
+
+ Written by David MacKenzie (djm@ai.mit.edu). */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+void usage ();
+
+/* The name under which this program was run. */
+char *program_name;
+
+/* If nonzero, return an exit status but produce no output. */
+int silent;
+
+struct option longopts[] =
+{
+ {"silent", 0, NULL, 's'},
+ {"quiet", 0, NULL, 's'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *tty;
+ int optc;
+
+ program_name = argv[0];
+ silent = 0;
+
+ while ((optc = getopt_long (argc, argv, "s", longopts, (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case 's':
+ silent = 1;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind != argc)
+ usage ();
+
+ tty = ttyname (0);
+ if (!silent)
+ {
+ if (tty)
+ puts (tty);
+ else
+ puts ("not a tty");
+ }
+
+ exit (tty == NULL);
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-s] [--silent] [--quiet]\n", program_name);
+ exit (2);
+}
diff --git a/src/uname.c b/src/uname.c
new file mode 100644
index 000000000..77eaae677
--- /dev/null
+++ b/src/uname.c
@@ -0,0 +1,155 @@
+/* uname -- print system information
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Option Example
+
+ -s, --sysname SunOS
+ -n, --nodename rocky8
+ -r, --release 4.0
+ -v, --version
+ -m, --machine sun
+ -a, --all SunOS rocky8 4.0 sun
+
+ The default behavior is equivalent to `-s'.
+
+ David MacKenzie <djm@ai.mit.edu> */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <getopt.h>
+#include "system.h"
+
+void error ();
+void print_element ();
+void usage ();
+
+/* Values that are bitwise or'd into `toprint'. */
+/* Operating system name. */
+#define PRINT_SYSNAME 1
+
+/* Node name on a communications network. */
+#define PRINT_NODENAME 2
+
+/* Operating system release. */
+#define PRINT_RELEASE 4
+
+/* Operating system version. */
+#define PRINT_VERSION 8
+
+/* Machine hardware name. */
+#define PRINT_MACHINE 16
+
+/* Mask indicating which elements of the name to print. */
+unsigned char toprint;
+
+/* The name this program was run with, for error messages. */
+char *program_name;
+
+struct option long_options[] =
+{
+ {"sysname", 0, NULL, 's'},
+ {"nodename", 0, NULL, 'n'},
+ {"release", 0, NULL, 'r'},
+ {"version", 0, NULL, 'v'},
+ {"machine", 0, NULL, 'm'},
+ {"all", 0, NULL, 'a'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ struct utsname name;
+ int c;
+
+ program_name = argv[0];
+ toprint = 0;
+
+ while ((c = getopt_long (argc, argv, "snrvma", long_options, (int *) 0))
+ != EOF)
+ {
+ switch (c)
+ {
+ case 's':
+ toprint |= PRINT_SYSNAME;
+ break;
+ case 'n':
+ toprint |= PRINT_NODENAME;
+ break;
+ case 'r':
+ toprint |= PRINT_RELEASE;
+ break;
+ case 'v':
+ toprint |= PRINT_VERSION;
+ break;
+ case 'm':
+ toprint |= PRINT_MACHINE;
+ break;
+ case 'a':
+ toprint = PRINT_SYSNAME | PRINT_NODENAME | PRINT_RELEASE |
+ PRINT_VERSION | PRINT_MACHINE;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind != argc)
+ usage ();
+
+ if (toprint == 0)
+ toprint = PRINT_SYSNAME;
+
+ if (uname (&name) == -1)
+ error (1, errno, "cannot get system name");
+
+ print_element (PRINT_SYSNAME, name.sysname);
+ print_element (PRINT_NODENAME, name.nodename);
+ print_element (PRINT_RELEASE, name.release);
+ print_element (PRINT_VERSION, name.version);
+ print_element (PRINT_MACHINE, name.machine);
+
+ exit (0);
+}
+
+/* If the name element set in MASK is selected for printing in `toprint',
+ print ELEMENT; then print a space unless it is the last element to
+ be printed, in which case print a newline. */
+
+void
+print_element (mask, element)
+ unsigned char mask;
+ char *element;
+{
+ if (toprint & mask)
+ {
+ toprint &= ~mask;
+ printf ("%s%c", element, toprint ? ' ' : '\n');
+ }
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-snrvma] [--sysname] [--nodename] [--release] [--version]\n\
+ [--machine] [--all]\n", program_name);
+ exit (1);
+}
diff --git a/src/who.c b/src/who.c
new file mode 100644
index 000000000..d5f40dc55
--- /dev/null
+++ b/src/who.c
@@ -0,0 +1,434 @@
+/* GNU's who.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by jla; revised by djm */
+
+/* Output format:
+ name [state] line time [idle] host
+ state: -T
+ name, line, time: not -q
+ idle: -u
+
+ Options:
+ -m Same as 'who am i', for POSIX.
+ -q Only user names and # logged on; overrides all other options.
+ -s Name, line, time (default).
+ -i, -u Idle hours and minutes; '.' means active in last minute;
+ 'old' means idle for >24 hours.
+ -H Print column headings at top.
+ -w, -T -s plus mesg (+ or -, or ? if bad line). */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <utmp.h>
+#include <time.h>
+#include <getopt.h>
+#ifndef _POSIX_SOURCE
+#include <sys/param.h>
+#endif
+#include "system.h"
+
+#ifndef UTMP_FILE
+#ifdef _PATH_UTMP /* 4.4BSD. */
+#define UTMP_FILE _PATH_UTMP
+#else /* !_PATH_UTMP */
+#define UTMP_FILE "/etc/utmp"
+#endif /* !_PATH_UTMP */
+#endif /* !UTMP_FILE */
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define MESG_BIT 020 /* Group write bit. */
+
+char *ttyname ();
+
+char *idle_string ();
+char *xmalloc ();
+struct utmp *search_entries ();
+void error ();
+void list_entries ();
+void print_entry ();
+void print_heading ();
+void scan_entries ();
+void usage ();
+void who ();
+void who_am_i ();
+
+/* The name this program was run with. */
+char *program_name;
+
+/* If nonzero, display only a list of usernames and count of
+ the users logged on.
+ Ignored for `who am i'. */
+int short_list;
+
+/* If nonzero, display the hours:minutes since each user has touched
+ the keyboard, or "." if within the last minute, or "old" if
+ not within the last day. */
+int include_idle;
+
+/* If nonzero, display a line at the top describing each field. */
+int include_heading;
+
+/* If nonzero, display a `+' for each user if mesg y, a `-' if mesg n,
+ or a `?' if their tty cannot be statted. */
+int include_mesg;
+
+struct option longopts[] =
+{
+ {"count", 0, NULL, 'q'},
+ {"idle", 0, NULL, 'u'},
+ {"heading", 0, NULL, 'H'},
+ {"message", 0, NULL, 'T'},
+ {"mesg", 0, NULL, 'T'},
+ {"writable", 0, NULL, 'T'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc, longind;
+ int my_line_only = 0;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "imqsuwHT", longopts, &longind))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 'm':
+ my_line_only = 1;
+ break;
+
+ case 'q':
+ short_list = 1;
+ break;
+
+ case 's':
+ break;
+
+ case 'i':
+ case 'u':
+ include_idle = 1;
+ break;
+
+ case 'H':
+ include_heading = 1;
+ break;
+
+ case 'w':
+ case 'T':
+ include_mesg = 1;
+ break;
+
+ default:
+ usage ();
+ }
+ }
+
+ if (chdir ("/dev"))
+ error (1, errno, "cannot change directory to /dev");
+
+ switch (argc - optind)
+ {
+ case 0: /* who */
+ if (my_line_only)
+ who_am_i (UTMP_FILE);
+ else
+ who (UTMP_FILE);
+ break;
+
+ case 1: /* who <utmp file> */
+ if (my_line_only)
+ who_am_i (argv[optind]);
+ else
+ who (argv[optind]);
+ break;
+
+ case 2: /* who <blurf> <glop> */
+ who_am_i (UTMP_FILE);
+ break;
+
+ default: /* lose */
+ usage ();
+ }
+
+ exit (0);
+}
+
+static struct utmp *utmp_contents;
+
+/* Display a list of who is on the system, according to utmp file FILENAME. */
+
+void
+who (filename)
+ char *filename;
+{
+ int users;
+
+ users = read_utmp (filename);
+ if (short_list)
+ list_entries (users);
+ else
+ scan_entries (users);
+}
+
+/* Read the utmp file FILENAME into UTMP_CONTENTS and return the
+ number of entries it contains. */
+
+int
+read_utmp (filename)
+ char *filename;
+{
+ register int desc;
+ struct stat file_stats;
+
+ desc = open (filename, O_RDONLY, 0);
+ if (desc < 0)
+ error (1, errno, "%s", filename);
+
+ fstat (desc, &file_stats);
+ if (file_stats.st_size > 0)
+ utmp_contents = (struct utmp *) xmalloc ((unsigned) file_stats.st_size);
+ else
+ {
+ close (desc);
+ return 0;
+ }
+
+ /* Use < instead of != in case the utmp just grew. */
+ if (read (desc, utmp_contents, file_stats.st_size) < file_stats.st_size)
+ error (1, errno, "%s", filename);
+
+ if (close (desc))
+ error (1, errno, "%s", filename);
+
+ return file_stats.st_size / sizeof (struct utmp);
+}
+
+/* Display a line of information about entry THIS. */
+
+void
+print_entry (this)
+ struct utmp *this;
+{
+ struct stat stats;
+ time_t last_change;
+ char mesg;
+ char line[sizeof (this->ut_line) + 1];
+
+ strncpy (line, this->ut_line, sizeof (this->ut_line));
+ line[sizeof (this->ut_line)] = 0;
+ if (stat (line, &stats) == 0)
+ {
+ mesg = (stats.st_mode & MESG_BIT) ? '+' : '-';
+ last_change = stats.st_atime;
+ }
+ else
+ {
+ mesg = '?';
+ last_change = 0;
+ }
+
+ printf ("%-*.*s",
+ sizeof (this->ut_name), sizeof (this->ut_name),
+ this->ut_name);
+ if (include_mesg)
+ printf (" %c ", mesg);
+ printf (" %-*.*s",
+ sizeof (this->ut_line), sizeof (this->ut_line),
+ this->ut_line);
+ printf (" %-12.12s", ctime (&this->ut_time) + 4);
+ if (include_idle)
+ {
+ if (last_change)
+ printf (" %s", idle_string (last_change));
+ else
+ printf (" . ");
+ }
+#ifdef HAVE_UT_HOST
+ if (this->ut_host[0])
+ printf (" (%-.*s)", sizeof (this->ut_host), this->ut_host);
+#endif
+
+ putchar ('\n');
+}
+
+/* Print the username of each valid entry and the number of valid entries
+ in `utmp_contents', which should have N elements. */
+
+void
+list_entries (n)
+ int n;
+{
+ register struct utmp *this = utmp_contents;
+ register int entries = 0;
+
+ while (n--)
+ {
+ if (this->ut_name[0]
+#ifdef USER_PROCESS
+ && this->ut_type == USER_PROCESS
+#endif
+ )
+ {
+ printf ("%s ", this->ut_name);
+ entries++;
+ }
+ this++;
+ }
+ printf ("\n# users=%u\n", entries);
+}
+
+void
+print_heading ()
+{
+ struct utmp *ut;
+
+ printf ("%-*s ", sizeof (ut->ut_name), "USER");
+ if (include_mesg)
+ printf ("MESG ");
+ printf ("%-*s ", sizeof (ut->ut_line), "LINE");
+ printf ("LOGIN-TIME ");
+ if (include_idle)
+ printf ("IDLE ");
+ printf ("FROM\n");
+}
+
+/* Display `utmp_contents', which should have N entries. */
+
+void
+scan_entries (n)
+ int n;
+{
+ register struct utmp *this = utmp_contents;
+
+ if (include_heading)
+ print_heading ();
+
+ while (n--)
+ {
+ if (this->ut_name[0]
+#ifdef USER_PROCESS
+ && this->ut_type == USER_PROCESS
+#endif
+ )
+ print_entry (this);
+ this++;
+ }
+}
+
+/* Search `utmp_contents', which should have N entries, for
+ an entry with a `ut_line' field identical to LINE.
+ Return the first matching entry found, or NULL if there
+ is no matching entry. */
+
+struct utmp *
+search_entries (n, line)
+ int n;
+ char *line;
+{
+ register struct utmp *this = utmp_contents;
+
+ while (n--)
+ {
+ if (this->ut_name[0]
+#ifdef USER_PROCESS
+ && this->ut_type == USER_PROCESS
+#endif
+ && !strncmp (line, this->ut_line, sizeof (this->ut_line)))
+ return this;
+ this++;
+ }
+ return NULL;
+}
+
+/* Display the entry in utmp file FILENAME for this tty on standard input,
+ or nothing if there is no entry for it. */
+
+void
+who_am_i (filename)
+ char *filename;
+{
+ register struct utmp *utmp_entry;
+ char hostname[MAXHOSTNAMELEN + 1];
+ char *tty;
+
+ if (gethostname (hostname, MAXHOSTNAMELEN + 1))
+ *hostname = 0;
+
+ if (include_heading)
+ {
+ printf ("%*s ", strlen (hostname), " ");
+ print_heading ();
+ }
+
+ tty = ttyname (0);
+ if (tty == NULL)
+ return;
+ tty += 5; /* Remove "/dev/". */
+
+ utmp_entry = search_entries (read_utmp (filename), tty);
+ if (utmp_entry == NULL)
+ return;
+
+ printf ("%s!", hostname);
+ print_entry (utmp_entry);
+}
+
+/* Return a string representing the time between WHEN and the time
+ that this function is first run. */
+
+char *
+idle_string (when)
+ time_t when;
+{
+ static time_t now = 0;
+ static char idle[10];
+ time_t seconds_idle;
+
+ if (now == 0)
+ time (&now);
+
+ seconds_idle = now - when;
+ if (seconds_idle < 60) /* One minute. */
+ return " . ";
+ if (seconds_idle < (24 * 60 * 60)) /* One day. */
+ {
+ sprintf (idle, "%02d:%02d",
+ seconds_idle / (60 * 60),
+ (seconds_idle % (60 * 60)) / 60);
+ return idle;
+ }
+ return " old ";
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-imqsuwHT] [--count] [--idle] [--heading] [--message] [--mesg]\n\
+ [--writable] [file] [am i]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/whoami.c b/src/whoami.c
new file mode 100644
index 000000000..4a12406f1
--- /dev/null
+++ b/src/whoami.c
@@ -0,0 +1,49 @@
+/* whoami -- print effective userid
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Equivalent to `id -un'. */
+/* Written by Richard Mlynarik. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "system.h"
+
+void
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct passwd *pw;
+ register uid_t uid;
+
+ if (argc != 1)
+ {
+ fprintf (stderr, "Usage: %s\n", argv[0]);
+ exit (1);
+ }
+
+ uid = geteuid ();
+ pw = getpwuid (uid);
+ if (pw)
+ {
+ puts (pw->pw_name);
+ exit (0);
+ }
+ fprintf (stderr,"%s: cannot find username for UID %u\n", argv[0], uid);
+ exit (1);
+}
diff --git a/src/yes.c b/src/yes.c
new file mode 100644
index 000000000..71eabb020
--- /dev/null
+++ b/src/yes.c
@@ -0,0 +1,39 @@
+/* yes - output a string repeatedly until killed
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+ 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* David MacKenzie <djm@ai.mit.edu> */
+
+#include <stdio.h>
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+
+ if (argc == 1)
+ while (1)
+ puts ("y");
+
+ while (1)
+ for (i = 1; i < argc; i++)
+ {
+ fputs (argv[i], stdout);
+ putchar (i == argc - 1 ? '\n' : ' ');
+ }
+}