From ccbd1d7dc5189f4637468a8136f672e60ee0e531 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sun, 1 Nov 1992 05:44:29 +0000 Subject: Initial revision --- src/basename.c | 78 ++++ src/date.c | 190 +++++++++ src/dirname.c | 57 +++ src/echo.c | 179 ++++++++ src/env.c | 168 ++++++++ src/expr.c | 672 ++++++++++++++++++++++++++++++ src/groups.sh | 31 ++ src/id.c | 346 ++++++++++++++++ src/logname.c | 43 ++ src/nice.c | 150 +++++++ src/nohup.sh | 48 +++ src/pathchk.c | 332 +++++++++++++++ src/printenv.c | 69 ++++ src/printf.c | 481 ++++++++++++++++++++++ src/sleep.c | 84 ++++ src/stty.c | 1241 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/su.c | 519 ++++++++++++++++++++++++ src/tee.c | 155 +++++++ src/test.c | 1054 +++++++++++++++++++++++++++++++++++++++++++++++ src/tty.c | 88 ++++ src/uname.c | 155 +++++++ src/who.c | 434 ++++++++++++++++++++ src/whoami.c | 49 +++ src/yes.c | 39 ++ 24 files changed, 6662 insertions(+) create mode 100644 src/basename.c create mode 100644 src/date.c create mode 100644 src/dirname.c create mode 100644 src/echo.c create mode 100644 src/env.c create mode 100644 src/expr.c create mode 100755 src/groups.sh create mode 100644 src/id.c create mode 100644 src/logname.c create mode 100644 src/nice.c create mode 100755 src/nohup.sh create mode 100644 src/pathchk.c create mode 100644 src/printenv.c create mode 100644 src/printf.c create mode 100644 src/sleep.c create mode 100644 src/stty.c create mode 100644 src/su.c create mode 100644 src/tee.c create mode 100644 src/test.c create mode 100644 src/tty.c create mode 100644 src/uname.c create mode 100644 src/who.c create mode 100644 src/whoami.c create mode 100644 src/yes.c (limited to 'src') 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 +#include +#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 */ + +#include +#include +#include +#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 +#else +#include +#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 +#include +#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 +#include +#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 +#include +#include +#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 +#include +#include +#include +#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 . + +# 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 +#include +#include +#include +#include +#include "system.h" + +#ifdef _POSIX_VERSION +#include +#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 +#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 +#include +#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 */ + +#include +#include +#include +#ifndef NICE_PRIORITY +#include +#include +#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 . + +# 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 + and Jim Meyering */ + +#include +#include +#include +#include "system.h" + +#ifdef _POSIX_VERSION +#include +#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 +#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 +#include +#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 */ + +#include +#include +#include +#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 +#include +#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 */ + +#include +#include +#include +#ifdef _AIX +#include /* Needed to get window size. */ +#endif +#ifdef WINSIZE_IN_PTEM +#include +#include +#endif +#include +#ifdef __STDC__ +#include +#define VA_START(args, lastarg) va_start(args, lastarg) +#else +#include +#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 ""; + + 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 . */ + +#include +#include +#include +#include +#include "system.h" + +#ifdef HAVE_SYSLOG_H +#include +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 +#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 +#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 +#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 +#include +#include +#include +#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 +#include + +#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 +# else /* !_POSIX_VERSION */ +# include +# 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 +#endif /* !_POSIX_VERSION */ + +#include +#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 + * '-'(eq|ne|le|lt|ge|gt) + * file '-'(nt|ot|ef) file + * '(' ')' + * 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 +#include +#include +#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 */ + +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#ifndef _POSIX_SOURCE +#include +#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 */ + if (my_line_only) + who_am_i (argv[optind]); + else + who (argv[optind]); + break; + + case 2: /* who */ + 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 +#include +#include +#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 */ + +#include + +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' : ' '); + } +} -- cgit v1.2.3-54-g00ecf