summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/alloca.c194
-rw-r--r--lib/basename.c35
-rw-r--r--lib/error.c105
-rw-r--r--lib/getdate.y965
-rw-r--r--lib/gethostname.c49
-rw-r--r--lib/getopt.c679
-rw-r--r--lib/getopt.h125
-rw-r--r--lib/getopt1.c153
-rw-r--r--lib/getugroups.c86
-rw-r--r--lib/getusershell.c196
-rw-r--r--lib/mktime.c224
-rw-r--r--lib/posixtm.y173
-rw-r--r--lib/putenv.c101
-rw-r--r--lib/stime.c34
-rw-r--r--lib/strcspn.c37
-rw-r--r--lib/strftime.c428
-rw-r--r--lib/strtod.c188
-rw-r--r--lib/xmalloc.c65
-rw-r--r--old/sh-utils/ChangeLog696
-rw-r--r--old/sh-utils/NEWS14
-rw-r--r--src/basename.c78
-rw-r--r--src/date.c190
-rw-r--r--src/dirname.c57
-rw-r--r--src/echo.c179
-rw-r--r--src/env.c168
-rw-r--r--src/expr.c672
-rwxr-xr-xsrc/groups.sh31
-rw-r--r--src/id.c346
-rw-r--r--src/logname.c43
-rw-r--r--src/nice.c150
-rwxr-xr-xsrc/nohup.sh48
-rw-r--r--src/pathchk.c332
-rw-r--r--src/printenv.c69
-rw-r--r--src/printf.c481
-rw-r--r--src/sleep.c84
-rw-r--r--src/stty.c1241
-rw-r--r--src/su.c519
-rw-r--r--src/tee.c155
-rw-r--r--src/test.c1054
-rw-r--r--src/tty.c88
-rw-r--r--src/uname.c155
-rw-r--r--src/who.c434
-rw-r--r--src/whoami.c49
-rw-r--r--src/yes.c39
44 files changed, 11209 insertions, 0 deletions
diff --git a/lib/alloca.c b/lib/alloca.c
new file mode 100644
index 000000000..c1ff22227
--- /dev/null
+++ b/lib/alloca.c
@@ -0,0 +1,194 @@
+/*
+ alloca -- (mostly) portable public-domain implementation -- D A Gwyn
+
+ last edit: 86/05/30 rms
+ include config.h, since on VMS it renames some symbols.
+ Use xmalloc instead of malloc.
+
+ This implementation of the PWB library alloca() function,
+ which is used to allocate space off the run-time stack so
+ that it is automatically reclaimed upon procedure exit,
+ was inspired by discussions with J. Q. Johnson of Cornell.
+
+ It should work under any C implementation that uses an
+ actual procedure stack (as opposed to a linked list of
+ frames). There are some preprocessor constants that can
+ be defined when compiling for your specific system, for
+ improved efficiency; however, the defaults should be okay.
+
+ The general concept of this implementation is to keep
+ track of all alloca()-allocated blocks, and reclaim any
+ that are found to be deeper in the stack than the current
+ invocation. This heuristic does not reclaim storage as
+ soon as it becomes invalid, but it will do so eventually.
+
+ As a special case, alloca(0) reclaims storage without
+ allocating any. It is a good idea to use alloca(0) in
+ your main control loop, etc. to force garbage collection.
+*/
+#ifndef lint
+static char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */
+#endif
+
+#ifdef emacs
+#include "config.h"
+#ifdef static
+/* actually, only want this if static is defined as ""
+ -- this is for usg, in which emacs must undefine static
+ in order to make unexec workable
+ */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+#ifndef alloca /* If compiling with GCC, this file's not needed. */
+
+#ifdef __STDC__
+typedef void *pointer; /* generic pointer type */
+#else
+typedef char *pointer; /* generic pointer type */
+#endif
+
+#define NULL 0 /* null pointer constant */
+
+extern void free();
+extern pointer xmalloc();
+
+/*
+ Define STACK_DIRECTION if you know the direction of stack
+ growth for your system; otherwise it will be automatically
+ deduced at run-time.
+
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+*/
+
+#ifndef STACK_DIRECTION
+#define STACK_DIRECTION 0 /* direction unknown */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define STACK_DIR STACK_DIRECTION /* known at compile-time */
+
+#else /* STACK_DIRECTION == 0; need run-time code */
+
+static int stack_dir; /* 1 or -1 once known */
+#define STACK_DIR stack_dir
+
+static void
+find_stack_direction (/* void */)
+{
+ static char *addr = NULL; /* address of first
+ `dummy', once known */
+ auto char dummy; /* to get stack address */
+
+ if (addr == NULL)
+ { /* initial entry */
+ addr = &dummy;
+
+ find_stack_direction (); /* recurse once */
+ }
+ else /* second entry */
+ if (&dummy > addr)
+ stack_dir = 1; /* stack grew upward */
+ else
+ stack_dir = -1; /* stack grew downward */
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/*
+ An "alloca header" is used to:
+ (a) chain together all alloca()ed blocks;
+ (b) keep track of stack depth.
+
+ It is very important that sizeof(header) agree with malloc()
+ alignment chunk size. The following default should work okay.
+*/
+
+#ifndef ALIGN_SIZE
+#define ALIGN_SIZE sizeof(double)
+#endif
+
+typedef union hdr
+{
+ char align[ALIGN_SIZE]; /* to force sizeof(header) */
+ struct
+ {
+ union hdr *next; /* for chaining headers */
+ char *deep; /* for stack depth measure */
+ } h;
+} header;
+
+/*
+ alloca( size ) returns a pointer to at least `size' bytes of
+ storage which will be automatically reclaimed upon exit from
+ the procedure that called alloca(). Originally, this space
+ was supposed to be taken from the current stack frame of the
+ caller, but that method cannot be made to work for some
+ implementations of C, for example under Gould's UTX/32.
+*/
+
+static header *last_alloca_header = NULL; /* -> last alloca header */
+
+pointer
+alloca (size) /* returns pointer to storage */
+ unsigned size; /* # bytes to allocate */
+{
+ auto char probe; /* probes stack depth: */
+ register char *depth = &probe;
+
+#if STACK_DIRECTION == 0
+ if (STACK_DIR == 0) /* unknown growth direction */
+ find_stack_direction ();
+#endif
+
+ /* Reclaim garbage, defined as all alloca()ed storage that
+ was allocated from deeper in the stack than currently. */
+
+ {
+ register header *hp; /* traverses linked list */
+
+ for (hp = last_alloca_header; hp != NULL;)
+ if ((STACK_DIR > 0 && hp->h.deep > depth)
+ || (STACK_DIR < 0 && hp->h.deep < depth))
+ {
+ register header *np = hp->h.next;
+
+ free ((pointer) hp); /* collect garbage */
+
+ hp = np; /* -> next header */
+ }
+ else
+ break; /* rest are not deeper */
+
+ last_alloca_header = hp; /* -> last valid storage */
+ }
+
+ if (size == 0)
+ return NULL; /* no allocation required */
+
+ /* Allocate combined header + user data storage. */
+
+ {
+ register pointer new = xmalloc (sizeof (header) + size);
+ /* address of header */
+
+ ((header *)new)->h.next = last_alloca_header;
+ ((header *)new)->h.deep = depth;
+
+ last_alloca_header = (header *)new;
+
+ /* User storage begins just after header. */
+
+ return (pointer)((char *)new + sizeof(header));
+ }
+}
+
+#endif /* no alloca */
diff --git a/lib/basename.c b/lib/basename.c
new file mode 100644
index 000000000..b8e7e1f4f
--- /dev/null
+++ b/lib/basename.c
@@ -0,0 +1,35 @@
+/* basename.c -- return the last element in a path
+ Copyright (C) 1990 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. */
+
+#if defined(USG) || defined(STDC_HEADERS)
+#include <string.h>
+#define rindex strrchr
+#else
+#include <strings.h>
+#endif
+
+/* Return NAME with any leading path stripped off. */
+
+char *
+basename (name)
+ char *name;
+{
+ char *base;
+
+ base = rindex (name, '/');
+ return base ? base + 1 : name;
+}
diff --git a/lib/error.c b/lib/error.c
new file mode 100644
index 000000000..7e61f1507
--- /dev/null
+++ b/lib/error.c
@@ -0,0 +1,105 @@
+/* error.c -- error handler for noninteractive utilities
+ Copyright (C) 1990, 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. */
+
+/* Written by David MacKenzie. */
+
+#include <stdio.h>
+
+#ifdef HAVE_VPRINTF
+
+#if __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else /* !__STDC__ */
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif /* !__STDC__ */
+
+#else /* !HAVE_VPRINTF */
+
+#ifdef HAVE_DOPRNT
+#define va_alist args
+#define va_dcl int args;
+#else /* !HAVE_DOPRNT */
+#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif /* !HAVE_DOPRNT */
+
+#endif /* !HAVE_VPRINTF */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#else /* !STDC_HEADERS */
+void exit ();
+#endif /* !STDC_HEADERS */
+
+#ifndef HAVE_STRERROR
+static char *
+private_strerror (errnum)
+ int errnum;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+
+ if (errnum > 0 && errnum <= sys_nerr)
+ return sys_errlist[errnum];
+ return "Unknown system error";
+}
+#define strerror private_strerror
+#endif /* !HAVE_STRERROR */
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+void
+#if defined (HAVE_VPRINTF) && __STDC__
+error (int status, int errnum, char *message, ...)
+#else /* !HAVE_VPRINTF or !__STDC__ */
+error (status, errnum, message, va_alist)
+ int status;
+ int errnum;
+ char *message;
+ va_dcl
+#endif /* !HAVE_VPRINTF or !__STDC__ */
+{
+ extern char *program_name;
+#ifdef HAVE_VPRINTF
+ va_list args;
+#endif /* HAVE_VPRINTF */
+
+ fprintf (stderr, "%s: ", program_name);
+#ifdef HAVE_VPRINTF
+ VA_START (args, message);
+ vfprintf (stderr, message, args);
+ va_end (args);
+#else /* !HAVE_VPRINTF */
+#ifdef HAVE_DOPRNT
+ _doprnt (message, &args, stderr);
+#else /* !HAVE_DOPRNT */
+ fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif /* !HAVE_DOPRNT */
+#endif /* !HAVE_VPRINTF */
+ if (errnum)
+ fprintf (stderr, ": %s", strerror (errnum));
+ putc ('\n', stderr);
+ fflush (stderr);
+ if (status)
+ exit (status);
+}
diff --git a/lib/getdate.y b/lib/getdate.y
new file mode 100644
index 000000000..bff1a9b78
--- /dev/null
+++ b/lib/getdate.y
@@ -0,0 +1,965 @@
+%{
+/* $Revision: 2.1 $
+**
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+** send any email to Rich.
+**
+** This grammar has eight shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
+/* SUPPRESS 288 on yyerrlab *//* Label unused */
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef sparc
+#include <alloca.h>
+#else
+#ifdef _AIX /* for Bison */
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* The code at the top of get_date which figures out the offset of the
+ current time zone checks various CPP symbols to see if special
+ tricks are need, but defaults to using the gettimeofday system call.
+ Include <sys/time.h> if that will be used. */
+
+#if !defined (USG) && !defined (sgi) && !defined (__386BSD__)
+#include <sys/time.h>
+#endif
+
+#if defined(vms)
+
+#include <types.h>
+#include <time.h>
+
+#else
+
+#include <sys/types.h>
+
+#if defined(USG) || !defined(HAVE_FTIME)
+/*
+** If you need to do a tzset() call to set the
+** timezone, and don't have ftime().
+*/
+struct timeb {
+ time_t time; /* Seconds since the epoch */
+ unsigned short millitm; /* Field not used */
+ short timezone;
+ short dstflag; /* Field not used */
+};
+
+#else
+
+#include <sys/timeb.h>
+
+#endif /* defined(USG) && !defined(HAVE_FTIME) */
+
+#if defined(BSD4_2) || defined(BSD4_1C) || (defined (hp9000) && !defined (hpux))
+#include <sys/time.h>
+#else
+#if defined(_AIX)
+#include <sys/time.h>
+#endif
+#include <time.h>
+#endif /* defined(BSD4_2) */
+
+#endif /* defined(vms) */
+
+#if defined (STDC_HEADERS) || defined (USG)
+#include <string.h>
+#endif
+
+#if sgi
+#undef timezone
+#endif
+
+extern struct tm *localtime();
+
+#define yyparse getdate_yyparse
+#define yylex getdate_yylex
+#define yyerror getdate_yyerror
+
+#if !defined(lint) && !defined(SABER)
+static char RCS[] =
+ "$Header: str2date.y,v 2.1 90/09/06 08:15:06 cronan Exp $";
+#endif /* !defined(lint) && !defined(SABER) */
+
+
+#define EPOCH 1970
+#define HOUR(x) ((time_t)(x) * 60)
+#define SECSPERDAY (24L * 60L * 60L)
+
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+** Daylight-savings mode: on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+ DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static char *yyInput;
+static DSTMODE yyDSTmode;
+static time_t yyDayOrdinal;
+static time_t yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static time_t yyTimezone;
+static time_t yyDay;
+static time_t yyHour;
+static time_t yyMinutes;
+static time_t yyMonth;
+static time_t yySeconds;
+static time_t yyYear;
+static MERIDIAN yyMeridian;
+static time_t yyRelMonth;
+static time_t yyRelSeconds;
+
+%}
+
+%union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+}
+
+%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
+%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> tMERIDIAN o_merid
+
+%%
+
+spec : /* NULL */
+ | spec item
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ | number
+ ;
+
+time : tUNUMBER tMERIDIAN {
+ yyHour = $1;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = 0;
+ yyMeridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
+ }
+ ;
+
+zone : tZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSToff;
+ }
+ | tDAYZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ |
+ tZONE tDST {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ ;
+
+day : tDAY {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tDAY ',' {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tUNUMBER tDAY {
+ yyDayOrdinal = $1;
+ yyDayNumber = $2;
+ }
+ ;
+
+date : tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ yyYear = $5;
+ }
+ | tUNUMBER tSNUMBER tSNUMBER {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = $1;
+ yyMonth = -$2;
+ yyDay = -$3;
+ }
+ | tMONTH tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ yyYear = $4;
+ }
+ | tUNUMBER tMONTH {
+ yyMonth = $2;
+ yyDay = $1;
+ }
+ | tUNUMBER tMONTH tUNUMBER {
+ yyMonth = $2;
+ yyDay = $1;
+ yyYear = $3;
+ }
+ ;
+
+rel : relunit tAGO {
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMonth = -yyRelMonth;
+ }
+ | relunit
+ ;
+
+relunit : tUNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tSNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tMINUTE_UNIT {
+ yyRelSeconds += $1 * 60L;
+ }
+ | tSNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tUNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tSEC_UNIT {
+ yyRelSeconds++;
+ }
+ | tSNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tUNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tMONTH_UNIT {
+ yyRelMonth += $1;
+ }
+ ;
+
+number : tUNUMBER {
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = $1;
+ else {
+ if($1>10000) {
+ time_t date_part;
+
+ date_part= $1/10000;
+ yyHaveDate++;
+ yyDay= (date_part)%100;
+ yyMonth= (date_part/100)%100;
+ yyYear = date_part/10000;
+ }
+ yyHaveTime++;
+ if ($1 < 100) {
+ yyHour = $1;
+ yyMinutes = 0;
+ }
+ else {
+ yyHour = $1 / 100;
+ yyMinutes = $1 % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ ;
+
+o_merid : /* NULL */ {
+ $$ = MER24;
+ }
+ | tMERIDIAN {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* Month and day table. */
+static TABLE MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL }
+};
+
+/* Time units table. */
+static TABLE UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
+ { "week", tMINUTE_UNIT, 7 * 24 * 60 },
+ { "day", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "hour", tMINUTE_UNIT, 60 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL }
+};
+
+/* Assorted relative-time words. */
+static TABLE OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL }
+};
+
+/* The timezone table. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR(1) }, /* Central European */
+ { "met", tZONE, -HOUR(1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR(1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
+ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ { NULL }
+};
+
+/* Military timezone table. */
+static TABLE MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL }
+};
+
+
+
+
+/* ARGSUSED */
+int
+yyerror(s)
+ char *s;
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(Hours, Minutes, Seconds, Meridian)
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
+ }
+ /* NOTREACHED */
+}
+
+
+static time_t
+Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
+ time_t Month;
+ time_t Day;
+ time_t Year;
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+ DSTMODE DSTmode;
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ if (Year < EPOCH || Year > 1999
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ return -1;
+
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
+ Julian -= 60 * 60;
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(Start, Future)
+ time_t Start;
+ time_t Future;
+{
+ time_t StartDay;
+ time_t FutureDay;
+
+ StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+ FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(Start, DayOrdinal, DayNumber)
+ time_t Start;
+ time_t DayOrdinal;
+ time_t DayNumber;
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+
+static time_t
+RelativeMonth(Start, RelMonth)
+ time_t Start;
+ time_t RelMonth;
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(buff)
+ char *buff;
+{
+ register char *p;
+ register char *q;
+ register TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen(buff) == 3)
+ abbrev = 1;
+ else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && isalpha(*buff)) {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+
+int
+yylex()
+{
+ register char c;
+ register char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for ( ; ; ) {
+ while (isspace(*yyInput))
+ yyInput++;
+
+ if (isdigit(c = *yyInput) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit(*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit(c = *yyInput++); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha(c)) {
+ for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ }
+}
+
+
+time_t
+get_date(p, now)
+ char *p;
+ struct timeb *now;
+{
+ struct tm *tm;
+ struct timeb ftz;
+ time_t Start;
+ time_t tod;
+
+ yyInput = p;
+ if (now == NULL) {
+ now = &ftz;
+#if !defined(HAVE_FTIME)
+ (void)time(&ftz.time);
+ /* Set the timezone global. */
+ tzset();
+ {
+#if sgi
+ ftz.timezone = (int) _timezone / 60;
+#else /* not sgi */
+#ifdef __386BSD__
+ ftz.timezone = 0;
+#else /* neither sgi nor 386BSD */
+#if defined (USG)
+ extern time_t timezone;
+
+ ftz.timezone = (int) timezone / 60;
+#else /* neither sgi nor 386BSD nor USG */
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday (&tv, &tz);
+ ftz.timezone = (int) tz.tz_minuteswest;
+#endif /* neither sgi nor 386BSD nor USG */
+#endif /* neither sgi nor 386BSD */
+#endif /* not sgi */
+ }
+#else /* HAVE_FTIME */
+ (void)ftime(&ftz);
+#endif /* HAVE_FTIME */
+ }
+
+ tm = localtime(&now->time);
+ yyYear = tm->tm_year;
+ yyMonth = tm->tm_mon + 1;
+ yyDay = tm->tm_mday;
+ yyTimezone = now->timezone;
+ yyDSTmode = DSTmaybe;
+ yyHour = 0;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMonth = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ if (yyHaveDate || yyHaveTime || yyHaveDay) {
+ Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+ yyMeridian, yyDSTmode);
+ if (Start < 0)
+ return -1;
+ }
+ else {
+ Start = now->time;
+ if (!yyHaveRel)
+ Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
+ }
+
+ Start += yyRelSeconds;
+ Start += RelativeMonth(Start, yyRelMonth);
+
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
+ Start += tod;
+ }
+
+ /* Have to do *something* with a legitimate -1 so it's distinguishable
+ * from the error return value. (Alternately could set errno on error.) */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+main(ac, av)
+ int ac;
+ char *av[];
+{
+ char buff[128];
+ time_t d;
+
+ (void)printf("Enter date, or blank line to exit.\n\t> ");
+ (void)fflush(stdout);
+ while (gets(buff) && buff[0]) {
+ d = get_date(buff, (struct timeb *)NULL);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("%s", ctime(&d));
+ (void)printf("\t> ");
+ (void)fflush(stdout);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/lib/gethostname.c b/lib/gethostname.c
new file mode 100644
index 000000000..fe78dbb4a
--- /dev/null
+++ b/lib/gethostname.c
@@ -0,0 +1,49 @@
+/* gethostname emulation for SysV and POSIX.1.
+ 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. */
+
+/* David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#ifdef HAVE_UNAME
+#include <sys/utsname.h>
+#endif
+
+/* Put up to LEN chars of the host name into NAME.
+ Null terminate it if the name is shorter than LEN.
+ Return 0 if ok, -1 if error. */
+
+int
+gethostname (name, len)
+ char *name;
+ int len;
+{
+#ifdef HAVE_UNAME
+ struct utsname uts;
+
+ if (uname (&uts) == -1)
+ return -1;
+ if (len > sizeof (uts.nodename))
+ {
+ /* More space than we need is available. */
+ name[sizeof (uts.nodename)] = '\0';
+ len = sizeof (uts.nodename);
+ }
+ strncpy (name, uts.nodename, len);
+#else
+ strcpy (name, ""); /* Hardcode your system name if you want. */
+#endif
+ return 0;
+}
diff --git a/lib/getopt.c b/lib/getopt.c
new file mode 100644
index 000000000..771b51117
--- /dev/null
+++ b/lib/getopt.c
@@ -0,0 +1,679 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* AIX requires this to be the first thing in the file. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__))))
+#include <alloca.h>
+#else
+#ifdef _AIX
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif /* alloca.h */
+#endif /* not __GNUC__ */
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#undef alloca
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#else /* Not GNU C library. */
+#define __alloca alloca
+#endif /* GNU C library. */
+
+#if !__STDC__
+#define const
+#endif
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+#define GETOPT_COMPAT
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n))
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (string, chr)
+ char *string;
+ int chr;
+{
+ while (*string)
+ {
+ if (*string == chr)
+ return string;
+ string++;
+ }
+ return 0;
+}
+
+static void
+my_bcopy (from, to, size)
+ char *from, *to;
+ int size;
+{
+ int i;
+ for (i = 0; i < size; i++)
+ to[i] = from[i];
+}
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
+ char **temp = (char **) __alloca (nonopts_size);
+
+ /* Interchange the two blocks of data in ARGV. */
+
+ my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size);
+ my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt],
+ (optind - last_nonopt) * sizeof (char *));
+ my_bcopy ((char *) temp,
+ (char *) &argv[first_nonopt + optind - last_nonopt],
+ nonopts_size);
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+ }
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/lib/getopt.h b/lib/getopt.h
new file mode 100644
index 000000000..93a5cf778
--- /dev/null
+++ b/lib/getopt.h
@@ -0,0 +1,125 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/lib/getopt1.c b/lib/getopt1.c
new file mode 100644
index 000000000..36eb7cd8d
--- /dev/null
+++ b/lib/getopt1.c
@@ -0,0 +1,153 @@
+/* Getopt for GNU.
+ Copyright (C) 1987, 88, 89, 90, 91, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "getopt.h"
+
+#ifndef __STDC__
+#define const
+#endif
+
+#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__) || defined (LIBC)
+#include <stdlib.h>
+#else /* STDC_HEADERS or __GNU_LIBRARY__ */
+char *getenv ();
+#endif /* STDC_HEADERS or __GNU_LIBRARY__ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/lib/getugroups.c b/lib/getugroups.c
new file mode 100644
index 000000000..4ac6dfc87
--- /dev/null
+++ b/lib/getugroups.c
@@ -0,0 +1,86 @@
+/* getugroups.c -- return a list of the groups a user is in
+ Copyright (C) 1990, 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 David MacKenzie. */
+
+#include <sys/types.h>
+#include <grp.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* 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). We do the same, for consistency.
+ Kludge, kludge. */
+
+#ifdef _POSIX_VERSION
+#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 */
+#define GETGROUPS_T int
+#endif /* not _POSIX_VERSION */
+
+/* setgrent, getgrent, and endgrent are not specified by POSIX.1,
+ so header files might not declare them.
+ If you don't have them at all, we can't implement this function.
+ You lose! */
+struct group *getgrent ();
+
+#if defined(USG) || defined(STDC_HEADERS)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+/* Like `getgroups', but for user USERNAME instead of for
+ the current process. */
+
+int
+getugroups (maxcount, grouplist, username)
+ int maxcount;
+ GETGROUPS_T *grouplist;
+ char *username;
+{
+ struct group *grp;
+ register char **cp;
+ register int count = 0;
+
+ setgrent ();
+ while ((grp = getgrent ()) != 0)
+ for (cp = grp->gr_mem; *cp; ++cp)
+ if (!strcmp (username, *cp))
+ {
+ if (maxcount != 0)
+ {
+ if (count >= maxcount)
+ {
+ endgrent ();
+ return count;
+ }
+ grouplist[count] = grp->gr_gid;
+ }
+ count++;
+ }
+ endgrent ();
+ return count;
+}
diff --git a/lib/getusershell.c b/lib/getusershell.c
new file mode 100644
index 000000000..6e2e0c105
--- /dev/null
+++ b/lib/getusershell.c
@@ -0,0 +1,196 @@
+/* getusershell.c -- Return names of valid user shells.
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#ifndef SHELLS_FILE
+/* File containing a list of nonrestricted shells, one per line. */
+#define SHELLS_FILE "/etc/shells"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+#endif
+
+static int readname ();
+
+/* List of shells to use if the shells file is missing. */
+static char *default_shells[] =
+{
+ "/bin/sh", "/bin/csh", "/usr/bin/sh", "/usr/bin/csh", NULL
+};
+
+/* Index of the next shell in `default_shells' to return.
+ 0 means we are not using `default_shells'. */
+static int default_index = 0;
+
+/* Input stream from the shells file. */
+static FILE *shellstream = NULL;
+
+/* Line of input from the shells file. */
+static char *line = NULL;
+
+/* Number of bytes allocated for `line'. */
+static int line_size = 0;
+
+/* Return an entry from the shells file, ignoring comment lines.
+ Return NULL if there are no more entries. */
+
+char *
+getusershell ()
+{
+ if (default_index > 0)
+ {
+ if (default_shells[default_index])
+ /* Not at the end of the list yet. */
+ return default_shells[default_index++];
+ return NULL;
+ }
+
+ if (shellstream == NULL)
+ {
+ shellstream = fopen (SHELLS_FILE, "r");
+ if (shellstream == NULL)
+ {
+ /* No shells file. Use the default list. */
+ default_index = 1;
+ return default_shells[0];
+ }
+ }
+
+ while (readname (&line, &line_size, shellstream))
+ {
+ if (*line != '#')
+ return line;
+ }
+ return NULL; /* End of file. */
+}
+
+/* Rewind the shells file. */
+
+void
+setusershell ()
+{
+ default_index = 0;
+ if (shellstream == NULL)
+ shellstream = fopen (SHELLS_FILE, "r");
+ else
+ fseek (shellstream, 0L, 0);
+}
+
+/* Close the shells file. */
+
+void
+endusershell ()
+{
+ if (shellstream)
+ {
+ fclose (shellstream);
+ shellstream = NULL;
+ }
+}
+
+/* Allocate N bytes of memory dynamically, with error checking. */
+
+static char *
+xmalloc (n)
+ unsigned n;
+{
+ char *p;
+
+ p = malloc (n);
+ if (p == 0)
+ {
+ fprintf (stderr, "virtual memory exhausted\n");
+ exit (1);
+ }
+ return p;
+}
+
+/* Reallocate space P to size N, with error checking. */
+
+static char *
+xrealloc (p, n)
+ char *p;
+ unsigned n;
+{
+ p = realloc (p, n);
+ if (p == 0)
+ {
+ fprintf (stderr, "virtual memory exhausted\n");
+ exit (1);
+ }
+ return p;
+}
+
+/* Read a line from STREAM, removing any newline at the end.
+ Place the result in *NAME, which is malloc'd
+ and/or realloc'd as necessary and can start out NULL,
+ and whose size is passed and returned in *SIZE.
+
+ Return the number of characters placed in *NAME
+ if some nonempty sequence was found, otherwise 0. */
+
+static int
+readname (name, size, stream)
+ char **name;
+ int *size;
+ FILE *stream;
+{
+ int c;
+ int name_index = 0;
+
+ if (*name == NULL)
+ {
+ *size = 10;
+ *name = (char *) xmalloc (*size);
+ }
+
+ /* Skip blank space. */
+ while ((c = getc (stream)) != EOF && isspace (c))
+ /* Do nothing. */ ;
+
+ while (c != EOF && !isspace (c))
+ {
+ (*name)[name_index++] = c;
+ while (name_index >= *size)
+ {
+ *size *= 2;
+ *name = (char *) xrealloc (*name, *size);
+ }
+ c = getc (stream);
+ }
+ (*name)[name_index] = '\0';
+ return name_index;
+}
+
+#ifdef TEST
+main ()
+{
+ char *s;
+
+ while (s = getusershell ())
+ puts (s);
+ exit (0);
+}
+#endif
diff --git a/lib/mktime.c b/lib/mktime.c
new file mode 100644
index 000000000..1c5bf6466
--- /dev/null
+++ b/lib/mktime.c
@@ -0,0 +1,224 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#ifdef TM_IN_SYS_TIME
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#else
+#define LONG_MAX (~(1 << (sizeof (long) * 8 - 1)))
+#define LONG_MIN (-LONG_MAX - 1)
+#define INT_MAX (~(1 << (sizeof (int) * 8 - 1)))
+#define INT_MIN (-INT_MAX - 1)
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef __isleap
+/* Nonzero if YEAR is a leap year (every 4 years,
+ except every 100th isn't, and every 1000th is). */
+#define __isleap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 1000 == 0))
+#endif
+
+/* How many days are in each month. */
+static unsigned short int __mon_lengths[2][12] =
+{
+ /* Normal years. */
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ /* Leap years. */
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+#define invalid() return (time_t) -1
+
+/* Return the `time_t' representation of TP and normalizes TP.
+ Return (time_t) -1 if TP is not representable as a `time_t'.
+ Note that 31 Dec 1969 23:59:59 is not representable
+ because it is represented as (time_t) -1. */
+time_t
+mktime(tp)
+register struct tm *tp;
+{
+ static struct tm min, max;
+ static char init = 0;
+
+ register time_t result;
+ register time_t t;
+ register int i;
+ register unsigned short *l;
+ register struct tm *new;
+ time_t end;
+
+ if (tp == NULL)
+ {
+ errno = EINVAL;
+ invalid();
+ }
+
+ if (!init)
+ {
+ init = 1;
+ end = (time_t) LONG_MIN;
+ new = gmtime(&end);
+ if (new != NULL)
+ min = *new;
+ else
+ min.tm_sec = min.tm_min = min.tm_hour =
+ min.tm_mday = min.tm_mon = min.tm_year = INT_MIN;
+
+ end = (time_t) LONG_MAX;
+ new = gmtime(&end);
+ if (new != NULL)
+ max = *new;
+ else
+ max.tm_sec = max.tm_min = max.tm_hour =
+ max.tm_mday = max.tm_mon = max.tm_year = INT_MAX;
+ }
+
+ /* Make all the elements of TP that we pay attention to
+ be within the ranges of reasonable values for those things. */
+#define normalize(elt, min, max, nextelt) \
+ while (tp->elt < min) \
+ { \
+ --tp->nextelt; \
+ tp->elt += max + 1; \
+ } \
+ while (tp->elt > max) \
+ { \
+ ++tp->nextelt; \
+ tp->elt -= max + 1; \
+ }
+
+ normalize (tm_sec, 0, 59, tm_min);
+ normalize (tm_min, 0, 59, tm_hour);
+ normalize (tm_hour, 0, 24, tm_mday);
+
+ /* Normalize the month first so we can use
+ it to figure the range for the day. */
+ normalize (tm_mon, 0, 11, tm_year);
+ normalize (tm_mday, 1, __mon_lengths[__isleap (tp->tm_year)][tp->tm_mon],
+ tm_mon);
+
+ /* Normalize the month again, since normalizing
+ the day may have pushed it out of range. */
+ normalize (tm_mon, 0, 11, tm_year);
+
+ /* Normalize the day again, because normalizing
+ the month may have changed the range. */
+ normalize (tm_mday, 1, __mon_lengths[__isleap (tp->tm_year)][tp->tm_mon],
+ tm_mon);
+
+ /* Check for out-of-range values. */
+#define lowhigh(field, minmax, cmp) (tp->field cmp minmax.field)
+#define low(field) lowhigh(field, min, <)
+#define high(field) lowhigh(field, max, >)
+#define oor(field) (low(field) || high(field))
+#define lowbound(field) (tp->field == min.field)
+#define highbound(field) (tp->field == max.field)
+ if (oor(tm_year))
+ invalid();
+ else if (lowbound(tm_year))
+ {
+ if (low(tm_mon))
+ invalid();
+ else if (lowbound(tm_mon))
+ {
+ if (low(tm_mday))
+ invalid();
+ else if (lowbound(tm_mday))
+ {
+ if (low(tm_hour))
+ invalid();
+ else if (lowbound(tm_hour))
+ {
+ if (low(tm_min))
+ invalid();
+ else if (lowbound(tm_min))
+ {
+ if (low(tm_sec))
+ invalid();
+ }
+ }
+ }
+ }
+ }
+ else if (highbound(tm_year))
+ {
+ if (high(tm_mon))
+ invalid();
+ else if (highbound(tm_mon))
+ {
+ if (high(tm_mday))
+ invalid();
+ else if (highbound(tm_mday))
+ {
+ if (high(tm_hour))
+ invalid();
+ else if (highbound(tm_hour))
+ {
+ if (high(tm_min))
+ invalid();
+ else if (highbound(tm_min))
+ {
+ if (high(tm_sec))
+ invalid();
+ }
+ }
+ }
+ }
+ }
+
+ t = 0;
+ for (i = 1970; i > 1900 + tp->tm_year; --i)
+ t -= __isleap(i) ? 366 : 365;
+ for (i = 1970; i < 1900 + tp->tm_year; ++i)
+ t += __isleap(i) ? 366 : 365;
+ l = __mon_lengths[__isleap(1900 + tp->tm_year)];
+ for (i = 0; i < tp->tm_mon; ++i)
+ t += l[i];
+ t += tp->tm_mday - 1;
+ result = ((t * 60 * 60 * 24) +
+ (tp->tm_hour * 60 * 60) +
+ (tp->tm_min * 60) +
+ tp->tm_sec);
+
+ end = result;
+#if 0 /* This code breaks it, on SunOS anyway. */
+ if (tp->tm_isdst < 0)
+ new = localtime(&end);
+ else
+#endif
+ new = gmtime(&end);
+ if (new == NULL)
+ invalid();
+ new->tm_isdst = tp->tm_isdst;
+ *tp = *new;
+
+ return result;
+}
diff --git a/lib/posixtm.y b/lib/posixtm.y
new file mode 100644
index 000000000..bb5b40e75
--- /dev/null
+++ b/lib/posixtm.y
@@ -0,0 +1,173 @@
+/* Parse dates for touch.
+ Copyright (C) 1989, 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 Jim Kingdon and David MacKenzie. */
+%{
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef sparc
+#include <alloca.h>
+#else
+#ifdef _AIX
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+
+#define YYDEBUG 1
+
+/* Lexical analyzer's current scan position in the input string. */
+static char *curpos;
+
+/* The return value. */
+static struct tm t;
+
+time_t mktime ();
+
+#define yyparse posixtime_yyparse
+static int yylex ();
+static int yyerror ();
+%}
+
+%token DIGIT
+
+%%
+date :
+ digitpair /* month */
+ digitpair /* day */
+ digitpair /* hours */
+ digitpair /* minutes */
+ year
+ seconds {
+ if ($1 >= 1 && $1 <= 12)
+ t.tm_mon = $1 - 1;
+ else {
+ YYABORT;
+ }
+ if ($2 >= 1 && $2 <= 31)
+ t.tm_mday = $2;
+ else {
+ YYABORT;
+ }
+ if ($3 >= 0 && $3 <= 23)
+ t.tm_hour = $3;
+ else {
+ YYABORT;
+ }
+ if ($4 >= 0 && $4 <= 59)
+ t.tm_min = $4;
+ else {
+ YYABORT;
+ }
+ }
+
+year : digitpair {
+ t.tm_year = $1;
+ /* Deduce the century based on the year.
+ See POSIX.2 section 4.63.3. */
+ if ($1 <= 68)
+ t.tm_year += 100;
+ }
+ | digitpair digitpair {
+ t.tm_year = $1 * 100 + $2;
+ if (t.tm_year < 1900) {
+ YYABORT;
+ } else
+ t.tm_year -= 1900;
+ }
+ | /* empty */ {
+ time_t now;
+ struct tm *tmp;
+
+ /* Use current year. */
+ time (&now);
+ tmp = localtime (&now);
+ t.tm_year = tmp->tm_year;
+ }
+ ;
+
+seconds : /* empty */ {
+ t.tm_sec = 0;
+ }
+ | '.' digitpair {
+ if ($2 >= 0 && $2 <= 61)
+ t.tm_sec = $2;
+ else {
+ YYABORT;
+ }
+ }
+ ;
+
+digitpair : DIGIT DIGIT {
+ $$ = $1 * 10 + $2;
+ }
+ ;
+%%
+static int
+yylex ()
+{
+ char ch = *curpos++;
+
+ if (ch >= '0' && ch <= '9')
+ {
+ yylval = ch - '0';
+ return DIGIT;
+ }
+ else if (ch == '.' || ch == 0)
+ return ch;
+ else
+ return '?'; /* Cause an error. */
+}
+
+static int
+yyerror ()
+{
+ return 0;
+}
+
+/* Parse a POSIX-style date and return it, or (time_t)-1 for an error. */
+
+time_t
+posixtime (s)
+ char *s;
+{
+ curpos = s;
+ /* Let mktime decide whether it is daylight savings time. */
+ t.tm_isdst = -1;
+ if (yyparse ())
+ return (time_t)-1;
+ else
+ return mktime (&t);
+}
+
+/* Parse a POSIX-style date and return it, or NULL for an error. */
+
+struct tm *
+posixtm (s)
+ char *s;
+{
+ if (posixtime (s) == -1)
+ return NULL;
+ return &t;
+}
diff --git a/lib/putenv.c b/lib/putenv.c
new file mode 100644
index 000000000..512878c47
--- /dev/null
+++ b/lib/putenv.c
@@ -0,0 +1,101 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <errno.h>
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+extern int errno;
+#endif
+
+#if defined(STDC_HEADERS) || defined(USG)
+#include <string.h>
+#define index strchr
+#define bcopy(s, d, n) memcpy((d), (s), (n))
+#else /* not (STDC_HEADERS or USG) */
+#include <strings.h>
+#endif /* STDC_HEADERS or USG */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#if !__STDC__
+#define const
+#endif
+
+extern char **environ;
+
+/* Put STRING, which is of the form "NAME=VALUE", in the environment. */
+int
+putenv (string)
+ const char *string;
+{
+ char *name_end = index (string, '=');
+ register size_t size;
+ register char **ep;
+
+ if (name_end == NULL)
+ {
+ /* Remove the variable from the environment. */
+ size = strlen (string);
+ for (ep = environ; *ep != NULL; ++ep)
+ if (!strncmp (*ep, string, size) && (*ep)[size] == '=')
+ {
+ while (ep[1] != NULL)
+ {
+ ep[0] = ep[1];
+ ++ep;
+ }
+ *ep = NULL;
+ return 0;
+ }
+ }
+
+ size = 0;
+ for (ep = environ; *ep != NULL; ++ep)
+ if (!strncmp (*ep, string, name_end - string) &&
+ (*ep)[name_end - string] == '=')
+ break;
+ else
+ ++size;
+
+ if (*ep == NULL)
+ {
+ static char **last_environ = NULL;
+ char **new_environ = (char **) malloc ((size + 2) * sizeof (char *));
+ if (new_environ == NULL)
+ return -1;
+ (void) bcopy ((char *) environ, (char *) new_environ, size * sizeof (char *));
+ new_environ[size] = (char *) string;
+ new_environ[size + 1] = NULL;
+ if (last_environ != NULL)
+ free ((char *) last_environ);
+ last_environ = new_environ;
+ environ = new_environ;
+ }
+ else
+ *ep = (char *) string;
+
+ return 0;
+}
diff --git a/lib/stime.c b/lib/stime.c
new file mode 100644
index 000000000..e64795387
--- /dev/null
+++ b/lib/stime.c
@@ -0,0 +1,34 @@
+/* stime -- set the system clock
+ 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. */
+
+/* David MacKenzie <djm@ai.mit.edu> */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+/* Set the system time to *WHEN seconds past the start of 1970 GMT. */
+
+int
+stime (when)
+ time_t *when;
+{
+ struct timeval tv;
+
+ tv.tv_sec = *when;
+ tv.tv_usec = 0;
+ return settimeofday (&tv, (struct timezone *) 0);
+}
diff --git a/lib/strcspn.c b/lib/strcspn.c
new file mode 100644
index 000000000..ea61aa120
--- /dev/null
+++ b/lib/strcspn.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+char *index ();
+
+/* Return the length of the maximum inital segment of S
+ which contains no characters from REJECT. */
+int
+strcspn (s, reject)
+ register char *s;
+ register char *reject;
+{
+ register int count = 0;
+
+ while (*s != '\0')
+ if (index (reject, *s++) == 0)
+ ++count;
+ else
+ return count;
+
+ return count;
+}
diff --git a/lib/strftime.c b/lib/strftime.c
new file mode 100644
index 000000000..cc4953e68
--- /dev/null
+++ b/lib/strftime.c
@@ -0,0 +1,428 @@
+/* strftime - custom formatting of date and/or time
+ Copyright (C) 1989, 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. */
+
+/* Note: this version of strftime lacks locale support,
+ but it is standalone.
+
+ Performs `%' substitutions similar to those in printf. Except
+ where noted, substituted fields have a fixed size; numeric fields are
+ padded if necessary. Padding is with zeros by default; for fields
+ that display a single number, padding can be changed or inhibited by
+ following the `%' with one of the modifiers described below. Unknown
+ field specifiers are copied as normal characters. All other
+ characters are copied to the output without change.
+
+ Supports a superset of the ANSI C field specifiers.
+
+ Literal character fields:
+ % %
+ n newline
+ t tab
+
+ Numeric modifiers (a nonstandard extension):
+ - do not pad the field
+ _ pad the field with spaces
+
+ Time fields:
+ %H hour (00..23)
+ %I hour (01..12)
+ %k hour ( 0..23)
+ %l hour ( 1..12)
+ %M minute (00..59)
+ %p locale's AM or PM
+ %r time, 12-hour (hh:mm:ss [AP]M)
+ %R time, 24-hour (hh:mm)
+ %S second (00..61)
+ %T time, 24-hour (hh:mm:ss)
+ %X locale's time representation (%H:%M:%S)
+ %Z time zone (EDT), or nothing if no time zone is determinable
+
+ Date fields:
+ %a locale's abbreviated weekday name (Sun..Sat)
+ %A locale's full weekday name, variable length (Sunday..Saturday)
+ %b locale's abbreviated month name (Jan..Dec)
+ %B locale's full month name, variable length (January..December)
+ %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)
+ %C century (00..99)
+ %d day of month (01..31)
+ %e day of month ( 1..31)
+ %D date (mm/dd/yy)
+ %h same as %b
+ %j day of year (001..366)
+ %m month (01..12)
+ %U week number of year with Sunday as first day of week (00..53)
+ %w day of week (0..6)
+ %W week number of year with Monday as first day of week (00..53)
+ %x locale's date representation (mm/dd/yy)
+ %y last two digits of year (00..99)
+ %Y year (1970...)
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <sys/types.h>
+#if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+#if defined(HAVE_TZNAME)
+extern char *tzname[2];
+#endif
+
+#if !__STDC__
+#define const
+#endif
+
+/* Types of padding for numbers in date and time. */
+enum padding
+{
+ none, blank, zero
+};
+
+static char *days[] =
+{
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+};
+
+static char *months[] =
+{
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+
+/* Add character C to STRING and increment LENGTH,
+ unless LENGTH would exceed MAX. */
+
+#define add_char(c) (length + 1 <= max) && (string[length++] = (c))
+
+/* Add a 2 digit number to STRING, padding if specified.
+ Return the number of characters added, up to MAX. */
+
+static int
+add_num2 (string, num, max, pad)
+ char *string;
+ int num;
+ int max;
+ enum padding pad;
+{
+ int top = num / 10;
+ int length = 0;
+
+ if (top == 0 && pad == blank)
+ add_char (' ');
+ else if (top != 0 || pad == zero)
+ add_char (top + '0');
+ add_char (num % 10 + '0');
+ return length;
+}
+
+/* Add a 3 digit number to STRING, padding if specified.
+ Return the number of characters added, up to MAX. */
+
+static int
+add_num3 (string, num, max, pad)
+ char *string;
+ int num;
+ int max;
+ enum padding pad;
+{
+ int top = num / 100;
+ int mid = (num - top * 100) / 10;
+ int length = 0;
+
+ if (top == 0 && pad == blank)
+ add_char (' ');
+ else if (top != 0 || pad == zero)
+ add_char (top + '0');
+ if (mid == 0 && top == 0 && pad == blank)
+ add_char (' ');
+ else if (mid != 0 || top != 0 || pad == zero)
+ add_char (mid + '0');
+ add_char (num % 10 + '0');
+ return length;
+}
+
+/* Like strncpy except return the number of characters copied. */
+
+static int
+add_str (to, from, max)
+ char *to;
+ char *from;
+ int max;
+{
+ int i;
+
+ for (i = 0; from[i] && i <= max; ++i)
+ to[i] = from[i];
+ return i;
+}
+
+/* Return the week in the year of the time in TM, with the weeks
+ starting on Sundays. */
+
+static int
+sun_week (tm)
+ struct tm *tm;
+{
+ int dl;
+
+ /* Set `dl' to the day in the year of the last day of the week previous
+ to the one containing the day specified in TM. If the day specified
+ in TM is in the first week of the year, `dl' will be negative or 0.
+ Otherwise, calculate the number of complete weeks before our week
+ (dl / 7) and add any partial week at the start of the year (dl % 7). */
+ dl = tm->tm_yday - tm->tm_wday;
+ return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
+}
+
+/* Return the week in the year of the time in TM, with the weeks
+ starting on Mondays. */
+
+static int
+mon_week (tm)
+ struct tm *tm;
+{
+ int dl, wday;
+
+ if (tm->tm_wday == 0)
+ wday = 6;
+ else
+ wday = tm->tm_wday - 1;
+ dl = tm->tm_yday - wday;
+ return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
+}
+
+#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
+char *
+zone_name (tp)
+ struct tm *tp;
+{
+ char *timezone ();
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday (&tv, &tz);
+ return timezone (tz.tz_minuteswest, tp->tm_isdst);
+}
+#endif
+
+/* Format the time given in TM according to FORMAT, and put the
+ results in STRING.
+ Return the number of characters (not including terminating null)
+ that were put into STRING, or 0 if the length would have
+ exceeded MAX. */
+
+size_t
+strftime (string, max, format, tm)
+ char *string;
+ size_t max;
+ const char *format;
+ const struct tm *tm;
+{
+ enum padding pad; /* Type of padding to apply. */
+ size_t length = 0; /* Characters put in STRING so far. */
+
+ for (; *format && length < max; ++format)
+ {
+ if (*format != '%')
+ add_char (*format);
+ else
+ {
+ ++format;
+ /* Modifiers: */
+ if (*format == '-')
+ {
+ pad = none;
+ ++format;
+ }
+ else if (*format == '_')
+ {
+ pad = blank;
+ ++format;
+ }
+ else
+ pad = zero;
+
+ switch (*format)
+ {
+ /* Literal character fields: */
+ case 0:
+ case '%':
+ add_char ('%');
+ break;
+ case 'n':
+ add_char ('\n');
+ break;
+ case 't':
+ add_char ('\t');
+ break;
+ default:
+ add_char (*format);
+ break;
+
+ /* Time fields: */
+ case 'H':
+ case 'k':
+ length +=
+ add_num2 (&string[length], tm->tm_hour, max - length,
+ *format == 'H' ? pad : blank);
+ break;
+ case 'I':
+ case 'l':
+ {
+ int hour12;
+
+ if (tm->tm_hour == 0)
+ hour12 = 12;
+ else if (tm->tm_hour > 12)
+ hour12 = tm->tm_hour - 12;
+ else
+ hour12 = tm->tm_hour;
+ length +=
+ add_num2 (&string[length], hour12, max - length,
+ *format == 'I' ? pad : blank);
+ }
+ break;
+ case 'M':
+ length +=
+ add_num2 (&string[length], tm->tm_min, max - length, pad);
+ break;
+ case 'p':
+ if (tm->tm_hour < 12)
+ add_char ('A');
+ else
+ add_char ('P');
+ add_char ('M');
+ break;
+ case 'r':
+ length +=
+ strftime (&string[length], max - length, "%I:%M:%S %p", tm);
+ break;
+ case 'R':
+ length +=
+ strftime (&string[length], max - length, "%H:%M", tm);
+ break;
+ case 'S':
+ length +=
+ add_num2 (&string[length], tm->tm_sec, max - length, pad);
+ break;
+ case 'T':
+ length +=
+ strftime (&string[length], max - length, "%H:%M:%S", tm);
+ break;
+ case 'X':
+ length +=
+ strftime (&string[length], max - length, "%H:%M:%S", tm);
+ break;
+ case 'Z':
+#ifdef HAVE_TM_ZONE
+ length += add_str (&string[length], tm->tm_zone, max - length);
+#else
+#ifdef HAVE_TZNAME
+ if (tm->tm_isdst && tzname[1] && *tzname[1])
+ length += add_str (&string[length], tzname[1], max - length);
+ else
+ length += add_str (&string[length], tzname[0], max - length);
+#else
+ length += add_str (&string[length], zone_name (tm), max - length);
+#endif
+#endif
+ break;
+
+ /* Date fields: */
+ case 'a':
+ add_char (days[tm->tm_wday][0]);
+ add_char (days[tm->tm_wday][1]);
+ add_char (days[tm->tm_wday][2]);
+ break;
+ case 'A':
+ length +=
+ add_str (&string[length], days[tm->tm_wday], max - length);
+ break;
+ case 'b':
+ case 'h':
+ add_char (months[tm->tm_mon][0]);
+ add_char (months[tm->tm_mon][1]);
+ add_char (months[tm->tm_mon][2]);
+ break;
+ case 'B':
+ length +=
+ add_str (&string[length], months[tm->tm_mon], max - length);
+ break;
+ case 'c':
+ length +=
+ strftime (&string[length], max - length,
+ "%a %b %d %H:%M:%S %Z %Y", tm);
+ break;
+ case 'C':
+ length +=
+ add_num2 (&string[length], (tm->tm_year + 1900) / 100,
+ max - length, pad);
+ break;
+ case 'd':
+ length +=
+ add_num2 (&string[length], tm->tm_mday, max - length, pad);
+ break;
+ case 'e':
+ length +=
+ add_num2 (&string[length], tm->tm_mday, max - length, blank);
+ break;
+ case 'D':
+ length +=
+ strftime (&string[length], max - length, "%m/%d/%y", tm);
+ break;
+ case 'j':
+ length +=
+ add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
+ break;
+ case 'm':
+ length +=
+ add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
+ break;
+ case 'U':
+ length +=
+ add_num2 (&string[length], sun_week (tm), max - length, pad);
+ break;
+ case 'w':
+ add_char (tm->tm_wday + '0');
+ break;
+ case 'W':
+ length +=
+ add_num2 (&string[length], mon_week (tm), max - length, pad);
+ break;
+ case 'x':
+ length +=
+ strftime (&string[length], max - length, "%m/%d/%y", tm);
+ break;
+ case 'y':
+ length +=
+ add_num2 (&string[length], tm->tm_year % 100,
+ max - length, pad);
+ break;
+ case 'Y':
+ add_char ((tm->tm_year + 1900) / 1000 + '0');
+ length +=
+ add_num3 (&string[length],
+ (1900 + tm->tm_year) % 1000, max - length, zero);
+ break;
+ }
+ }
+ }
+ add_char (0);
+ return length - 1;
+}
diff --git a/lib/strtod.c b/lib/strtod.c
new file mode 100644
index 000000000..4441102df
--- /dev/null
+++ b/lib/strtod.c
@@ -0,0 +1,188 @@
+/* Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+
+#if STDC_HEADERS
+#include <float.h>
+#include <stdlib.h>
+#include <string.h>
+#else
+#define NULL 0
+#define DBL_MAX 1.7976931348623159e+308
+#define DBL_MIN 2.2250738585072010e-308
+extern int errno;
+#endif
+#ifndef HUGE_VAL
+#define HUGE_VAL HUGE
+#endif
+
+#if !__STDC__
+#define const
+#endif
+
+/* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the
+ character after the last one used in the number is put in *ENDPTR. */
+double
+strtod (nptr, endptr)
+ const char *nptr;
+ char **endptr;
+{
+ register const char *s;
+ short int sign;
+
+ /* The number so far. */
+ double num;
+
+ int got_dot; /* Found a decimal point. */
+ int got_digit; /* Seen any digits. */
+
+ /* The exponent of the number. */
+ long int exponent;
+
+ if (nptr == NULL)
+ {
+ errno = EINVAL;
+ goto noconv;
+ }
+
+ s = nptr;
+
+ /* Eat whitespace. */
+ while (isspace (*s))
+ ++s;
+
+ /* Get the sign. */
+ sign = *s == '-' ? -1 : 1;
+ if (*s == '-' || *s == '+')
+ ++s;
+
+ num = 0.0;
+ got_dot = 0;
+ got_digit = 0;
+ exponent = 0;
+ for (;; ++s)
+ {
+ if (isdigit (*s))
+ {
+ got_digit = 1;
+
+ /* Make sure that multiplication by 10 will not overflow. */
+ if (num > DBL_MAX * 0.1)
+ /* The value of the digit doesn't matter, since we have already
+ gotten as many digits as can be represented in a `double'.
+ This doesn't necessarily mean the result will overflow.
+ The exponent may reduce it to within range.
+
+ We just need to record that there was another
+ digit so that we can multiply by 10 later. */
+ ++exponent;
+ else
+ num = (num * 10.0) + (*s - '0');
+
+ /* Keep track of the number of digits after the decimal point.
+ If we just divided by 10 here, we would lose precision. */
+ if (got_dot)
+ --exponent;
+ }
+ else if (!got_dot && *s == '.')
+ /* Record that we have found the decimal point. */
+ got_dot = 1;
+ else
+ /* Any other character terminates the number. */
+ break;
+ }
+
+ if (!got_digit)
+ goto noconv;
+
+ if (tolower (*s) == 'e')
+ {
+ /* Get the exponent specified after the `e' or `E'. */
+ int save = errno;
+ char *end;
+ long int exp;
+
+ errno = 0;
+ ++s;
+ exp = strtol (s, &end, 10);
+ if (errno == ERANGE)
+ {
+ /* The exponent overflowed a `long int'. It is probably a safe
+ assumption that an exponent that cannot be represented by
+ a `long int' exceeds the limits of a `double'. */
+ if (endptr != NULL)
+ *endptr = end;
+ if (exp < 0)
+ goto underflow;
+ else
+ goto overflow;
+ }
+ else if (end == s)
+ /* There was no exponent. Reset END to point to
+ the 'e' or 'E', so *ENDPTR will be set there. */
+ end = (char *) s - 1;
+ errno = save;
+ s = end;
+ exponent += exp;
+ }
+
+ if (endptr != NULL)
+ *endptr = (char *) s;
+
+ if (num == 0.0)
+ return 0.0;
+
+ /* Multiply NUM by 10 to the EXPONENT power,
+ checking for overflow and underflow. */
+
+ if (exponent < 0)
+ {
+ if (num < DBL_MIN * pow (10.0, (double) -exponent))
+ goto underflow;
+ }
+ else if (exponent > 0)
+ {
+ if (num > DBL_MAX * pow (10.0, (double) -exponent))
+ goto overflow;
+ }
+
+ num *= pow (10.0, (double) exponent);
+
+ return num * sign;
+
+overflow:
+ /* Return an overflow error. */
+ errno = ERANGE;
+ return HUGE_VAL * sign;
+
+underflow:
+ /* Return an underflow error. */
+ if (endptr != NULL)
+ *endptr = (char *) nptr;
+ errno = ERANGE;
+ return 0.0;
+
+noconv:
+ /* There was no number. */
+ if (endptr != NULL)
+ *endptr = (char *) nptr;
+ return 0.0;
+}
diff --git a/lib/xmalloc.c b/lib/xmalloc.c
new file mode 100644
index 000000000..f989004be
--- /dev/null
+++ b/lib/xmalloc.c
@@ -0,0 +1,65 @@
+/* xmalloc.c -- malloc with out of memory checking
+ 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. */
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+char *realloc ();
+void free ();
+#endif
+
+void error ();
+
+/* Allocate N bytes of memory dynamically, with error checking. */
+
+char *
+xmalloc (n)
+ unsigned n;
+{
+ char *p;
+
+ p = malloc (n);
+ if (p == 0)
+ /* Must exit with 2 for `cmp'. */
+ error (2, 0, "virtual memory exhausted");
+ return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+ with error checking.
+ If P is NULL, run xmalloc.
+ If N is 0, run free and return NULL. */
+
+char *
+xrealloc (p, n)
+ char *p;
+ unsigned n;
+{
+ if (p == 0)
+ return xmalloc (n);
+ if (n == 0)
+ {
+ free (p);
+ return 0;
+ }
+ p = realloc (p, n);
+ if (p == 0)
+ /* Must exit with 2 for `cmp'. */
+ error (2, 0, "virtual memory exhausted");
+ return p;
+}
diff --git a/old/sh-utils/ChangeLog b/old/sh-utils/ChangeLog
new file mode 100644
index 000000000..b3cb28843
--- /dev/null
+++ b/old/sh-utils/ChangeLog
@@ -0,0 +1,696 @@
+Wed Oct 28 14:16:48 1992 David J. MacKenzie (djm@goldman.gnu.ai.mit.edu)
+
+ * Version 1.8.
+
+ * stty.c: Accept Irix VRPRNT for VREPRINT.
+ From Jim Meyering.
+
+ * stty.c: Fix some type mismatches. From Bruce Evans, bde@runx.oz.au.
+
+ * who.c (read_utmp): Close file on error.
+ From Bruce Evans.
+
+ * su.c, test.c: Add some decls. From Bruce Evans.
+
+ * sleep.c (main): Arg to sleep is unsigned, not long.
+ From Bruce Evans.
+
+Fri Sep 11 00:25:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * echo.c, echo.1: New files.
+
+Thu Sep 10 18:42:44 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * pathchk.c (main): Don't strip trailing slashes from args;
+ they might make a path invalid.
+ (portable_chars_only, dir_ok): New functions.
+ (validate_path): Renamed from validate_new_path.
+ Call them. Don't complain if a leading
+ dir doesn't exist. Don't replace `parent' with a dir that
+ doesn't exist. Don't print a message when falling back
+ from pathconf to constant values.
+
+ * who.c [!UTMP_FILE]: If _PATH_UTMP is defined, use it instead
+ of /etc/utmp. From Marc Boucher <marc@cam.org>.
+
+Tue Aug 25 17:02:25 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * Version 1.7.
+
+ * groups.sh, nohup.sh: Add $(bindir) to front of path.
+
+Mon Aug 24 16:39:39 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * stty.c: make sane value for "min" 1, not 0.
+ From haible@ma2s2.mathematik.uni-karlsruhe.de (Bruno Haible).
+
+Sun Aug 23 03:02:07 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * id.c, test.c: Use NGROUPS_MAX if it's defined. 386BSD is like sun.
+
+Sat Aug 22 03:16:41 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * test.c: Rename STANDALONE to TEST_STANDALONE to avoid IBM RT
+ ACIS sys/param.h conflict.
+
+ * su.c (correct_password) [HAVE_SHADOW_H]: Try to get the
+ encrypted correct password from the shadow password file.
+
+Fri Jul 17 15:25:01 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * su.c, getusershell.c: New files.
+
+Fri Jul 3 15:08:43 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu)
+
+ * stty.c, who.c: Change FOO_MISSING to HAVE_FOO.
+
+Fri Jun 5 01:49:29 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * strcspn.c: New file.
+
+ * expr.c: Misc. cleanups.
+
+ * expr.c (eval7): Renamed from eval6.
+ Give syntax error if no more args. Don't coerce all values to numbers.
+ (eval6): New function.
+ (eval5): Accept == as a synonym for =.
+ (eval2): Coerce values to numbers for comparisons.
+ Above all from Dana Jacobsen (jacobsd@prism.cs.orst.edu).
+
+Thu Jun 4 19:32:09 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * printf.c (print_formatted): Move main loop into new function.
+ (main): Add an outer loop to use the format multiple times.
+ (verify): Don't reject a completely empty string.
+ Check errno (for overflow).
+
+ * false.sh, true.sh: New programs. Oh, boy.
+
+Thu May 14 01:17:22 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * stty.c (set_mode): Support crt and dec modes partially if
+ necessary, so they work on, for example, Ultrix . . . .
+
+Wed May 13 14:47:45 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * stty.c (set_mode): Swap nl and -nl. Have them also affect
+ output as well as input.
+
+Tue May 12 00:07:28 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * date.c (show_date): Use strftime for the whole conversion.
+
+Tue May 5 15:20:24 1992 David J. MacKenzie (djm@hal)
+
+ * stty.c (wrapf): Print the formatted buffer; don't redo the
+ formatting using vprintf.
+
+Thu Apr 30 01:17:08 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu)
+
+ * printf.c (xstrtol, xstrtoul, xstrtod, verify): New functions.
+ (main, print_direc): Use them. Make error messages more specific.
+
+ * tee.c (tee): Only malloc and free the table of file descriptors
+ if >0 files are given.
+
+Fri Apr 17 11:56:48 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu)
+
+ * pathchk.c (validate_new_path): Print the name of the component that
+ failed the length test, not the whole path.
+ From Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de).
+
+Mon Apr 6 15:11:36 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu)
+
+ * who.c (read_utmp): Check close return for error.
+ (print_heading): Align columns based on sizes of utmp members.
+ (who_am_i): Skip past /dev/ instead of skipping leading path.
+
+Mon Mar 16 23:47:03 1992 David J. MacKenzie (djm@apple-gunkies.gnu.ai.mit.edu)
+
+ * date.c (show_date): Don't call strftime if FORMAT is the
+ empty string.
+
+ * date.c (main): Reorganize to reduce duplicated code.
+ Add -d option.
+ (usage): Document -d.
+ (set_date): Function removed.
+
+Tue Feb 11 16:12:18 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * printf.c (print_esc): When a numeric escape is given,
+ don't call print_esc_char, and return 1 less.
+ From Thorston Ohl.
+
+Mon Jan 20 02:17:18 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Version 1.6.
+
+ * test.c: HAVE_MULTIPLE_GROUPS -> HAVE_GETGROUPS, for bash 1.11.
+
+Fri Jan 17 15:46:18 1992 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * expr.c (docolon): Use re_nsub to find the number of
+ subexpressions . . . From Karl Berry, who knows.
+
+Wed Dec 25 23:27:53 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * expr.c (docolon): Use the new way (re_regs.num_regs > 0) to find
+ out if there were any subexpressions, instead of the old way
+ (re_regs.start[1] >= 0), which can cause random memory
+ accesses with regex 0.1. From Brian Matthews.
+
+Tue Dec 24 02:12:15 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * system.h, id.c, pathchk.c, tee.c: Change POSIX ifdefs to
+ HAVE_UNISTD_H and _POSIX_VERSION.
+
+Wed Dec 11 13:15:09 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Version 1.5.
+
+ * expr.c (main): Set obscure_syntax to tell re_match to
+ allocate memory for the group registers.
+
+Mon Dec 9 16:03:14 1991 Charles Hannum (mycroft at hal.gnu.ai.mit.edu)
+
+ * who.c (list_entries): Check type == USER_PROCESS if defined, for SysV.
+
+Sat Dec 7 00:32:02 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Version 1.4.
+
+ * env, id, nice, pathchk, stty, tee, tty, uname: Change usage
+ messages and documentation to list long-named options starting
+ with `--' rather than `+'.
+
+ * env.c (main), nice.c (main): Simplify test for which exit
+ status to use if exec fails.
+
+Fri Dec 6 23:49:42 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tee.c (main) [POSIX]: Use sigaction instead of signal, which
+ POSIX doesn't have.
+
+Fri Oct 18 00:31:35 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * test.c (two_arguments): Fix from Chet.
+
+ * expr.c: Include regex.h after sys/types.h, not before, so
+ size_t gets defined.
+
+ * test.c: New version, adapted from bash 1.10.
+
+ * id.c: GID_T -> GETGROUPS_T, for clarity.
+
+Sat Oct 12 14:38:34 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu)
+
+ * configure: Define uid_t and gid_t as int if they're not
+ defined in sys/types.h. That's probably right for old Unixes
+ and avoids trying to find the C preprocessor.
+
+Sat Sep 28 13:01:23 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu)
+
+ * stty.c (set_mode): Make `raw' and `cooked' not change parity
+ and character size, which would probably make them useless on
+ 7-bit lines.
+ Make `raw' set the `time' character to 0, not 1.
+ From Bruce Evans.
+
+ * nohup.sh: If creating nohup.out, give it mode 0600, for POSIX.
+
+Fri Sep 13 14:59:51 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu)
+
+ * id.c [POSIX]: Always use sysconf to get NGROUPS_MAX.
+
+Thu Aug 29 14:43:07 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * test.c: Don't include sys/file.h if POSIX.
+ Use gid_t for getgroups.
+
+ * stty.c (set_mode): Use CEOF and CEOL instead of hardcoding them.
+ (display_speed): Fix a printf string type mismatch.
+ From Bruce Evans.
+
+Mon Aug 26 16:52:51 1991 David J. MacKenzie (djm at pogo.gnu.ai.mit.edu)
+
+ * configure, src/Makefile.in, lib/Makefile.in: Only put $< in
+ Makefiles if VPATH is being used, because older makes don't
+ understand it.
+
+Mon Aug 19 01:57:46 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Version 1.3.
+
+Sat Aug 17 22:48:15 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * src/Makefile.in (install): Install a link to test called '['.
+
+Wed Aug 14 12:22:57 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * test.c (unary_operator): Check first char of string, not its address.
+
+Sun Aug 11 18:10:30 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Version 1.2.
+
+ * system.h: Define S_IFMT if needed, for test.c.
+
+ * test.c: New file, from bash.
+
+ * nice.c: Change +priority to +adjustment (more accurate).
+
+Sat Aug 10 13:09:51 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * stty.c [WINSIZE_IN_PTEM]: sys/ptem.h requires sys/stream.h.
+
+ * nice.c, configure: Use nice if available and setpriority is missing.
+
+Thu Aug 8 01:34:05 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * date.c: USG needs TZ=GMT0 for UCT timezone, also.
+
+ * stty.c: Add pass8 and litout modes.
+
+Sun Aug 4 22:45:51 1991 David J. MacKenzie (djm at wheat-chex)
+
+ * Version 1.1.
+
+Fri Aug 2 13:22:31 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * configure: Implement +srcdir. Don't check for bison.
+
+ * stty.c: Don't change ixon in "sane" mode.
+
+ * configure: Use 1 instead of 255 for checking tzname,
+ because of signedness.
+
+Thu Aug 1 13:40:58 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * printenv.c (main): Don't print the variable names when given
+ args, as people seem to use printenv in scripts after all . . . .
+
+ * stty.c: Don't change parity or character size settings in
+ "sane" mode. The right values for those depend on the hardware.
+
+Wed Jul 31 01:19:01 1991 David J. MacKenzie (djm at hal)
+
+ * stty.c [_AIX]: Include sys/ioctl.h -- needed on
+ AIX to get window size.
+
+Tue Jul 30 00:06:54 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * getdate.y: New file.
+ * date.c: Add -s option to set date in English.
+ * configure: Check for ftime.
+
+ * date.c: Remove COMPUTE_TM_ZONE code, which didn't work.
+ * configure: Instead of checking whether tzname is declared,
+ check whether it exists.
+
+ * logname.c (main): Go back to just printing an error message
+ if getlogin fails, as required by POSIX.
+
+ * stty.c (screen_columns, wrapf): New functions to implement
+ output wrapping.
+ Globally: use them.
+
+ * configure: Define uid_t and gid_t if sys/types.h doesn't.
+ * system.h: Define F_OK et al. if nothing else does.
+
+Mon Jul 29 21:11:16 1991 David J. MacKenzie (djm at wombat.gnu.ai.mit.edu)
+
+ * pathchk.c (validate_new_path): Rearrange tests so that
+ pathconf is only called on existing directories. Use access
+ instead of stat to determine directory searchability.
+ From Jim Meyering.
+
+ * stty.c, configure: Add WINSIZE_IN_PTEM and GWINSZ_BROKEN for SCO.
+
+Wed Jul 24 02:13:31 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * stty.c (sane_mode): Always set control chars to sane values.
+ Set min and time if they're different from eof and eol.
+
+ * whoami.c: Print UID as unsigned.
+ * logname.c: Do "whoami" if getlogin fails.
+
+ * logname.c (main): fprintf was missing an arg.
+
+Tue Jul 23 02:20:15 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * id.c: GID_T is int if ultrix as well as if sun.
+
+ * stty.c: Implement raw and cooked modes.
+
+Mon Jul 22 15:21:21 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * tee.c (main): close stdin and stdout to check for errors.
+
+ * stty.c: Use tcflag_t for termios bitmasks.
+ Use speed_t for speeds. Use unsigned long for baud rates to
+ accomodate large values, and support 57600 and 115200 if available.
+
+ * date.c, configure: Instead of SIZE_T_MISSING,
+ define size_t if it's missing.
+
+ * id.c, whoami.c: Use uid_t and gid_t.
+
+ * id.c: If POSIX and not sun (bogus!), pass getgroups and
+ getugroups an array of gid_t instead of int.
+
+ * system.h: New file.
+ * Most programs: include it.
+
+Fri Jul 19 12:04:58 1991 David J. MacKenzie (djm at apple-gunkies)
+
+ * env.c [!STDC_HEADERS]: Declare errno.
+ * printf.c, pathchk.c: Don't include errno.h; not needed.
+
+ * version.c: New file.
+ * All C programs: Link with it, to get version number in the
+ binary where at least `strings -' and grep can find it.
+
+ * pathchk.c (strip_trailing_slashes): Function removed; use
+ version in lib.
+
+Mon Jul 15 11:34:22 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * Version 1.0.
+
+ * pathchk.c: Always check whether _POSIX_PATH_MAX and
+ _POSIX_NAME_MAX need to be defined.
+ [POSIX]: If no PATH_MAX or NAME_MAX and pathconf for the path
+ returns -1 (some systems do this if the path does not exist),
+ use pathconf for "/".
+
+Sun Jul 14 21:17:22 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * date.c (date_seconds): Function removed, replaced with
+ posixtm.y in lib.
+ (set_date): Change caller.
+ * configure: Check for bison.
+
+ * stty.c [!C_LINE_MISSING]: Add support for setting and
+ printing the line discipline.
+ * configure: Check for C_LINE_MISSING.
+
+ * configure: Check for Minix.
+
+Sat Jul 13 01:33:59 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * Add `man' directory and manual pages.
+ * configure: Set INSTALLDATA and MAN.
+
+ * id.c: Add #ifdefs for POSIX ways of getting max groups list size.
+ (print_group_list, print_full_info): Allocate list of groups
+ with malloc since its size might not be constant.
+
+ * nice.c (main): Don't adjust priority if printing it.
+ Default adjustment of 10, not 0.
+
+ * printf.c: Add \c escape and %b conversion.
+ Implement '*' for field width and precision.
+ Make all errors fatal.
+ (print_esc_string, print_esc): New functions.
+
+ * configure, date.c: Change SYS_TIME_H to TM_IN_SYS_TIME.
+ * configure: Always check where to find struct tm.
+
+ * yes.c: Rewrite to accept multiple arguments.
+
+ * Add groups.sh.
+
+Fri Jul 12 10:57:00 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu)
+
+ * dirname.c: Move code from dirname function into main,
+ simplifying things quite a bit. From Jim Meyering.
+ * Omit strdup from lib; no longer used.
+ * configure: Don't check for strdup.
+
+ * printenv.c (main): If args given, print the values in the order
+ given on the command line rather than the order given in the
+ environment.
+
+ * tee.c, tty.c (struct longopts): Revise to make short-option
+ equivalents clear.
+
+Thu Jul 11 12:46:11 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * nice.c: Add long options.
+
+ * Add date command and libraries it needs.
+ * configure: Updated.
+
+ * env.c: Add long options. Use GNU putenv instead of custom
+ setenv function.
+
+ * id.c: Add long options.
+
+ * pathchk.c [POSIX]: Use pathconf if necessary to get NAME_MAX
+ and PATH_MAX.
+
+ * nice.c: Use exit status required for nohup by POSIX.2
+ (nohup execs nice).
+
+ * sleep.c: Don't bother with hex and octal.
+
+ * env.c: Fix exit status for POSIX.2 draft 11.1.
+
+ * Many files: Remove private copies of xmalloc, error, xstrdup,
+ etc. to use shared versions.
+ Fix #includes for USG, STDC_HEADERS, POSIX.
+
+Mon Jul 8 18:56:24 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu)
+
+ * date.c (main): For -u, set TZ to "" instead of "GMT0",
+ unless on HP-UX or Ultrix.
+
+ * Rename some feature-test macros.
+ * stime.c: Created from code in date.c.
+ * date.c (compute_tm_zone): New function.
+ (date_seconds, show_date): Use it.
+ (xmalloc, xrealloc): Functions removed; use xmalloc.c instead.
+
+Tue Jul 2 02:28:11 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu)
+
+ * tee.c (tee): Report errors in closing files.
+
+Mon Mar 18 10:13:59 1991 Jeffrey A. Law (law at geech.ai.mit.edu)
+
+ * date.c (date_seconds, show_date): #if COMPUTE_TMZONE then
+ compute the proper value to place in tm->tm_zone from
+ information returned by localtime and gettimeofday.
+
+Fri Apr 26 11:38:09 1991 David J. MacKenzie (djm at mole.gnu.ai.mit.edu)
+
+ * stty.c: Define default values for control chars if necessary.
+ Complain about invalid options if no other options follow.
+ Use POSIX functions instead of ioctl, for manipulating termios.
+
+ * expr.c (main): Exit status was backwards.
+
+Thu Dec 20 00:36:01 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * id.c: Reorganization and many changes to fix bugs and POSIX
+ compliance problems.
+
+Mon Dec 10 03:09:13 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * stty.c: Don't declare printf and some other functions that
+ might have variable numbers of args in system header file decls.
+
+Tue Nov 14 23:37:22 1990 Roland McGrath (roland at geech.ai.mit.edu)
+
+ * id.c (print_groups): Put spaces after commas.
+ (print_group): New fn, to print a group id. Uses numeric fmt
+ unless -n, in which case it uses group names.
+ (print_groups): Call it. Find the rgid and egid, and print them as
+ well as the supplementary groups. Make sure we print each group only
+ once.
+
+Sun Sep 16 01:49:14 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * id.c (main): Add -G option for POSIX.2 draft 10.
+ Allow a username to be given.
+ (print_groups): New function from code in main.
+ (getugroups): New function.
+
+Sun Aug 12 00:32:01 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * env.c (main): Instead of setting _POSIX_OPTION_ORDER,
+ tell getopt to not permute, with `+'.
+
+Sat Aug 11 01:32:53 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * expr.c: Use regex.c library instead of private regex routines.
+
+ * nice.c (main): Add -n option for POSIX.2a.
+ (usage): New function.
+
+Fri Aug 10 23:58:11 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * who.c: Add -m, -i, -w options for POSIX.2a.
+
+Tue Aug 7 00:01:02 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * expr.c: Use exit directly instead of longjmp on error.
+ Use argv[0] instead of hardcoded "expr" in messages.
+ Make some functions void.
+
+Sat Aug 4 21:19:25 1990 David J. MacKenzie (djm at pogo.ai.mit.edu)
+
+ * env.c: Change exit statuses for POSIX draft 10.
+
+Wed Jul 4 04:32:51 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * tee.c: Use error instead of perror_with_name and
+ out_of_memory.
+
+Wed Jun 20 02:39:49 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * date.c: Change -DSETTOD to -DSTIME_MISSING, -DSIZE_T to
+ -DSIZE_T_IN_TYPES, and -DSTDC_HDRS to -DSTDC_HEADERS.
+ Declare some more functions. Replace fatal, memory_out, and
+ nonfatal_perror with error.
+
+Mon Jun 18 00:16:52 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * stty.c: Add some Unix compatibility modes.
+
+Sat Jun 16 21:05:59 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * stty.c (display_changed, display_all): Print values of min
+ and time.
+
+Thu Jun 14 17:49:31 1990 David J. MacKenzie (djm at apple-gunkies)
+
+ * stty.c: Implement tab, backspace, etc. delay args.
+
+Thu May 31 12:25:40 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * nohup.sh: Don't ignore SIGTERM.
+ If ./nohup.out is unwritable, try $HOME/nohup.out.
+
+Thu May 3 22:33:32 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * who.c: Use error instead of fatal and fatal_perror.
+ (print_headings): Print headings in all caps, like SYSV does.
+ (list_entries): New function for -q to make it like SYSV -q.
+ (valid_entries): Function removed.
+
+Mon Apr 2 01:27:23 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * id.c (main): Don't strip off leading path from program name.
+ Revise a couple of error messages.
+
+ * whoami.c (main): Use geteuid, not getuid, for Unix compatibility.
+
+Tue Mar 20 14:28:25 1990 David J. MacKenzie (djm at pogo.ai.mit.edu)
+
+ * tee.c (main): Pass list of files and its size as args to tee
+ rather than as global vars. Exit with return value of tee
+ instead of always 0.
+ (tee): Use unbuffered I/O instead of stdio, for POSIX.
+ Return an error status.
+ (xwrite): New function.
+
+Tue Mar 13 00:38:13 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * who.c (who_am_i): Print heading before checking to see
+ whether there is an entry for the tty on stdin, for
+ consistency with the who function.
+ (main): Use argv[optind], not argv[1], as alternate file.
+
+Fri Mar 9 15:49:04 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * who.c: Rename UTMP to UTMP_FILE for compatibility with SysV
+ utmp.h. Include some additional header files.
+ (main): Recognize some options from SysVr3 who. Call usage.
+ Set new global var `program_name' from argv[0].
+ (usage): New function.
+ (who): If -q given, only print count of users logged on.
+ (print_entry): New function to format an entry on the output;
+ make format more like that of the Unix who programs.
+ (print_heading): New function to print a line describing each
+ output field.
+ (who, who_am_i): Call print_entry and print_heading.
+ (valid_entries): New function to return count of nonempty
+ entries in utmp.
+ (search_entries): Compare with utmp tty field instead of
+ username field. Don't assume null termination in utmp field.
+ (who_am_i): Print the entry for the tty on stdin rather than
+ the first entry found for the uid. If hostname is not
+ available, use a null one instead of "<unknown>".
+ Don't hardcode max hostname length.
+ (idle_string): New function to format idle time field.
+ (fatal, fatal_perror): Use program_name instead of hardcoded "who"
+ in error messages.
+
+Tue Mar 6 00:59:03 1990 David J. MacKenzie (djm at albert.ai.mit.edu)
+
+ * printenv.c (main): Allow multiple variables to be specified.
+ (barf): Function removed.
+
+Sat Jan 20 18:41:48 1990 Jim Kingdon (kingdon at geech)
+
+ * expr.c (nextarg): Do not pass *args to strcmp if NULL.
+
+Mon Dec 18 09:57:20 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * printenv.c (main): Simplify error messages.
+
+Sat Dec 16 15:15:50 1989 David J. MacKenzie (djm at hobbes.ai.mit.edu)
+
+ * expr.c: Indent to regularize spacing.
+ (cmpv, arithf): Change '#define foo (args)' to '#define foo(args)'
+ so they compile.
+ (docolon): Remove unused vars.
+ (multiply): Rename from times to avoid libc conflict.
+ (error): Include program name in message.
+ (xmalloc): Rename from Malloc.
+ (re_compiled): Rename from re_comp to avoid libc conflict.
+
+ * basename.c: Fix some weird indentation.
+ (main): Print a clearer usage message.
+ Use a simpler method for removing suffix, if given.
+ (fatal): Function no longer used; removed.
+
+ * sleep.c: (main): Rename `time' to `seconds'. Print usage
+ message if given no args.
+ Exit with status 0 instead of falling off end.
+ (error): Print to stderr, not stdout.
+
+ * tee.c: (main): Use getopt_long instead of custom parser,
+ and adjust usage message.
+ Use list of filenames in argv rather than making a copy.
+ (tee): New function created from the second half of main.
+ Fix bug where it tried to fclose a loop index instead of a stream.
+ (xmalloc): Ok to return 0 if 0 bytes requested.
+ (xrealloc): Unused function removed.
+
+ * whoami.c: Canonicalize usage message and fix error message.
+
+ * who.c: Declare some functions.
+ (fatal_perror): New function for printing errors after system
+ calls.
+ Global: Use it when appropriate.
+ (xmalloc): Return char *, not int.
+ (read_utmp): Ok if utmp file is empty.
+ Include filename in error messages.
+ (scan_entries): Adjust columns to line up better, particularly
+ when there are users with 8 character long usernames logged in.
+
+Sat Oct 28 13:20:43 1989 David J. MacKenzie (djm at spiff)
+
+ * uname.c: Added long options.
+ global: changed the word `part' to the word `element'
+ (more precise).
+ (program_name, long_options): New variables.
+ (main): Support long options.
+ (usage): Add long options summary to message.
+
+Local Variables:
+mode: indented-text
+left-margin: 8
+version-control: never
+End:
diff --git a/old/sh-utils/NEWS b/old/sh-utils/NEWS
new file mode 100644
index 000000000..ee859dd58
--- /dev/null
+++ b/old/sh-utils/NEWS
@@ -0,0 +1,14 @@
+Major changes in release 1.8:
+* add echo command
+* fix some incorrect warnings in pathchk
+* look at the right utmp file on 386BSD
+* date doesn't dump core on some systems now
+
+Major changes in release 1.7:
+* add su, who, true, false commands
+* add more tests to expr
+* fix printf program handling of \ escapes
+* printf can re-use format string for multiple groups of arguments
+* printf catches numeric conversion errors with an ANSI C library
+* stty nl and -nl were backwards
+* date can format an arbitrary date without setting it
diff --git a/src/basename.c b/src/basename.c
new file mode 100644
index 000000000..767dbafc6
--- /dev/null
+++ b/src/basename.c
@@ -0,0 +1,78 @@
+/* basename -- strip directory and suffix from filenames
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: basename name [suffix]
+ NAME is a pathname; SUFFIX is a suffix to strip from it.
+
+ basename /usr/foo/lossage/functions.l
+ => functions.l
+ basename /usr/foo/lossage/functions.l .l
+ => functions
+ basename functions.lisp p
+ => functions.lis */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+char *basename ();
+void remove_suffix ();
+void strip_trailing_slashes ();
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *name;
+
+ if (argc == 1 || argc > 3)
+ {
+ fprintf (stderr, "Usage: %s name [suffix]\n", argv[0]);
+ exit (1);
+ }
+
+ strip_trailing_slashes (argv[1]);
+
+ name = basename (argv[1]);
+
+ if (argc == 3)
+ remove_suffix (name, argv[2]);
+
+ puts (name);
+
+ exit (0);
+}
+
+/* Remove SUFFIX from the end of NAME if it is there, unless NAME
+ consists entirely of SUFFIX. */
+
+void
+remove_suffix (name, suffix)
+ register char *name, *suffix;
+{
+ register char *np, *sp;
+
+ np = name + strlen (name);
+ sp = suffix + strlen (suffix);
+
+ while (np > name && sp > suffix)
+ if (*--np != *--sp)
+ return;
+ if (np > name)
+ *np = '\0';
+}
diff --git a/src/date.c b/src/date.c
new file mode 100644
index 000000000..bb4c5816f
--- /dev/null
+++ b/src/date.c
@@ -0,0 +1,190 @@
+/* date - print or set the system date and time
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Options:
+ -d DATESTR Display the date DATESTR.
+ -s DATESTR Set the date to DATESTR.
+ -u Display or set the date in universal instead of local time.
+ +FORMAT Specify custom date output format, described below.
+ MMDDhhmm[[CC]YY][.ss] Set the date in the format described below.
+
+ If one non-option argument is given, it is used as the date to which
+ to set the system clock, and must have the format:
+ MM month (01..12)
+ DD day in month (01..31)
+ hh hour (00..23)
+ mm minute (00..59)
+ CC first 2 digits of year (optional, defaults to current) (00..99)
+ YY last 2 digits of year (optional, defaults to current) (00..99)
+ ss second (00..61)
+
+ If a non-option argument that starts with a `+' is specified, it
+ is used to control the format in which the date is printed; it
+ can contain any of the `%' substitutions allowed by the strftime
+ function. A newline is always added at the end of the output.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+/* This is portable and avoids bringing in all of the ctype stuff. */
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+#ifdef TM_IN_SYS_TIME
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+#ifndef STDC_HEADERS
+time_t mktime ();
+size_t strftime ();
+time_t time ();
+#endif
+
+int putenv ();
+int stime ();
+
+char *xrealloc ();
+time_t get_date ();
+time_t posixtime ();
+void error ();
+void show_date ();
+void usage ();
+
+/* putenv string to use Universal Coordinated Time.
+ POSIX.2 says it should be "TZ=UCT0" or "TZ=GMT0". */
+#ifndef TZ_UCT
+#if defined(hpux) || defined(__hpux__) || defined(ultrix) || defined(__ultrix__) || defined(USG)
+#define TZ_UCT "TZ=GMT0"
+#else
+#define TZ_UCT "TZ="
+#endif
+#endif
+
+/* The name this program was run with, for error messages. */
+char *program_name;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc;
+ char *datestr = NULL;
+ time_t when;
+ int set_date = 0;
+ int universal_time = 0;
+
+ program_name = argv[0];
+
+ while ((optc = getopt (argc, argv, "d:s:u")) != EOF)
+ switch (optc)
+ {
+ case 'd':
+ datestr = optarg;
+ break;
+ case 's':
+ datestr = optarg;
+ set_date = 1;
+ break;
+ case 'u':
+ universal_time = 1;
+ break;
+ default:
+ usage ();
+ }
+
+ if (argc - optind > 1)
+ usage ();
+
+ if (universal_time && putenv (TZ_UCT) != 0)
+ error (1, 0, "virtual memory exhausted");
+
+ time (&when);
+
+ if (datestr)
+ when = get_date (datestr, NULL);
+
+ if (argc - optind == 1 && argv[optind][0] != '+')
+ {
+ when = posixtime (argv[optind]);
+ set_date = 1;
+ }
+
+ if (when == -1)
+ error (1, 0, "invalid date");
+
+ if (set_date && stime (&when) == -1)
+ error (0, errno, "cannot set date");
+
+ if (argc - optind == 1 && argv[optind][0] == '+')
+ show_date (argv[optind] + 1, when);
+ else
+ show_date ((char *) NULL, when);
+
+ exit (0);
+}
+
+/* Display the date and/or time in WHEN according to the format specified
+ in FORMAT, followed by a newline. If FORMAT is NULL, use the
+ standard output format (ctime style but with a timezone inserted). */
+
+void
+show_date (format, when)
+ char *format;
+ time_t when;
+{
+ struct tm *tm;
+ char *out = NULL;
+ size_t out_length = 0;
+
+ tm = localtime (&when);
+
+ if (format == NULL)
+ /* Print the date in the default format. Vanilla ANSI C strftime
+ doesn't support %e, but POSIX requires it. If you don't use
+ a GNU strftime, make sure yours supports %e. */
+ format = "%a %b %e %H:%M:%S %Z %Y";
+ else if (*format == '\0')
+ {
+ printf ("\n");
+ return;
+ }
+
+ do
+ {
+ out_length += 200;
+ out = (char *) xrealloc (out, out_length);
+ }
+ while (strftime (out, out_length, format, tm) == 0);
+
+ printf ("%s\n", out);
+ free (out);
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-u] [-d datestr] [-s datestr] [+FORMAT] [MMDDhhmm[[CC]YY][.ss]]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/dirname.c b/src/dirname.c
new file mode 100644
index 000000000..05612353a
--- /dev/null
+++ b/src/dirname.c
@@ -0,0 +1,57 @@
+/* dirname -- strip filename suffix from pathname
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by David MacKenzie and Jim Meyering. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+void strip_trailing_slashes ();
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register char *path;
+ register char *slash;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Usage: %s path\n", argv[0]);
+ exit (1);
+ }
+
+ path = argv[1];
+ strip_trailing_slashes (path);
+
+ slash = rindex (path, '/');
+ if (slash == NULL)
+ path = ".";
+ else
+ {
+ /* Remove any trailing slashes and final element. */
+ while (slash > path && *slash == '/')
+ --slash;
+ slash[1] = 0;
+ }
+ puts (path);
+
+ exit (0);
+}
+
diff --git a/src/echo.c b/src/echo.c
new file mode 100644
index 000000000..cae62ce8d
--- /dev/null
+++ b/src/echo.c
@@ -0,0 +1,179 @@
+/* echo.c, taken from Bash.
+Copyright (C) 1987, 1989, 1991, 1992 Free Software Foundation, Inc.
+
+This file is part of GNU Bash, the Bourne Again SHell.
+
+Bash is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with Bash; see the file COPYING. If not, write to the Free Software
+Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+/* echo [-neE] [arg ...]
+Output the ARGs. If -n is specified, the trailing newline is
+suppressed. If the -e option is given, interpretation of the
+following backslash-escaped characters is turned on:
+ \a alert (bell)
+ \b backspace
+ \c suppress trailing newline
+ \f form feed
+ \n new line
+ \r carriage return
+ \t horizontal tab
+ \v vertical tab
+ \\ backslash
+ \num the character whose ASCII code is NUM (octal).
+
+You can explicitly turn off the interpretation of the above characters
+on System V systems with the -E option.
+*/
+
+#define V9_ECHO
+
+#if defined (V9_ECHO)
+# if defined (USG)
+# define VALID_ECHO_OPTIONS "neE"
+# else
+# define VALID_ECHO_OPTIONS "ne"
+# endif /* !USG */
+#else /* !V9_ECHO */
+# define VALID_ECHO_OPTIONS "n"
+#endif /* !V9_ECHO */
+
+/* Print the words in LIST to standard output. If the first word is
+ `-n', then don't print a trailing newline. We also support the
+ echo syntax from Version 9 unix systems. */
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int display_return = 1, do_v9 = 0;
+
+/* System V machines already have a /bin/sh with a v9 behaviour. We
+ use the identical behaviour for these machines so that the
+ existing system shell scripts won't barf. */
+#if defined (V9_ECHO) && defined (USG)
+ do_v9 = 1;
+#endif
+
+ --argc;
+ ++argv;
+
+ while (argc > 0 && *argv[0] == '-')
+ {
+ register char *temp;
+ register int i;
+
+ /* If it appears that we are handling options, then make sure that
+ all of the options specified are actually valid. Otherwise, the
+ string should just be echoed. */
+ temp = argv[0] + 1;
+
+ for (i = 0; temp[i]; i++)
+ {
+ if (rindex (VALID_ECHO_OPTIONS, temp[i]) == 0)
+ goto just_echo;
+ }
+
+ if (!*temp)
+ goto just_echo;
+
+ /* All of the options in TEMP are valid options to ECHO.
+ Handle them. */
+ while (*temp)
+ {
+ if (*temp == 'n')
+ display_return = 0;
+#if defined (V9_ECHO)
+ else if (*temp == 'e')
+ do_v9 = 1;
+#if defined (USG)
+ else if (*temp == 'E')
+ do_v9 = 0;
+#endif /* USG */
+#endif /* V9_ECHO */
+ else
+ goto just_echo;
+
+ temp++;
+ }
+ argc--;
+ argv++;
+ }
+
+just_echo:
+
+ if (argc > 0)
+ {
+#if defined (V9_ECHO)
+ if (do_v9)
+ {
+ while (argc > 0)
+ {
+ register char *s = argv[0];
+ register int c;
+
+ while (c = *s++)
+ {
+ if (c == '\\' && *s)
+ {
+ switch (c = *s++)
+ {
+ case 'a': c = '\007'; break;
+ case 'b': c = '\b'; break;
+ case 'c': display_return = 0; continue;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = (int) 0x0B; break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if (*s >= '0' && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ if (*s >= '0' && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ break;
+ case '\\': break;
+ default: putchar ('\\'); break;
+ }
+ }
+ putchar(c);
+ }
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar(' ');
+ }
+ }
+ else
+#endif /* V9_ECHO */
+ {
+ while (argc > 0)
+ {
+ fputs (argv[0], stdout);
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar (' ');
+ }
+ }
+ }
+ if (display_return)
+ printf ("\n");
+ exit (0);
+}
diff --git a/src/env.c b/src/env.c
new file mode 100644
index 000000000..b322dee60
--- /dev/null
+++ b/src/env.c
@@ -0,0 +1,168 @@
+/* env - run a program in a modified environment
+ Copyright (C) 1986, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Richard Mlynarik and David MacKenzie */
+
+/* Options:
+ -
+ -i
+ --ignore-environment
+ Construct a new environment from scratch; normally the
+ environment is inherited from the parent process, except as
+ modified by other options.
+
+ -u variable
+ --unset=variable
+ Unset variable VARIABLE (remove it from the environment).
+ If VARIABLE was not set, does nothing.
+
+ variable=value (an arg containing a "=" character)
+ Set the environment variable VARIABLE to value VALUE. VALUE
+ may be of zero length ("variable="). Setting a variable to a
+ zero-length value is different from unsetting it.
+
+ --
+ Indicate that the following argument is the program
+ to invoke. This is necessary when the program's name
+ begins with "-" or contains a "=".
+
+ The first remaining argument specifies a program to invoke;
+ it is searched for according to the specification of the PATH
+ environment variable. Any arguments following that are
+ passed as arguments to that program.
+
+ If no command name is specified following the environment
+ specifications, the resulting environment is printed.
+ This is like specifying a command name of "printenv".
+
+ Examples:
+
+ If the environment passed to "env" is
+ { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks }
+
+ env - foo
+ runs "foo" in a null environment.
+
+ env foo
+ runs "foo" in the environment
+ { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks }
+
+ env DISPLAY=gnu:0 nemacs
+ runs "nemacs" in the envionment
+ { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks DISPLAY=gnu:0 }
+
+ env - LOGNAME=foo /hacks/hack bar baz
+ runs the "hack" program on arguments "bar" and "baz" in an
+ environment in which the only variable is "LOGNAME". Note that
+ the "-" option clears out the PATH variable, so one should be
+ careful to specify in which directory to find the program to
+ call.
+
+ env -u EDITOR LOGNAME=foo PATH=/energy -- e=mc2 bar baz
+ runs the program "/energy/e=mc2" with environment
+ { LOGNAME=foo PATH=/energy }
+*/
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+int putenv ();
+void error ();
+void usage ();
+
+extern char **environ;
+
+/* The name by which this program was run. */
+char *program_name;
+
+struct option longopts[] =
+{
+ {"ignore-environment", 0, NULL, 'i'},
+ {"unset", 1, NULL, 'u'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv, envp)
+ register int argc;
+ register char **argv;
+ char **envp;
+{
+ char *dummy_environ[1];
+ int optc;
+ int ignore_environment = 0;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "+iu:", longopts, (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case 'i':
+ ignore_environment = 1;
+ break;
+ case 'u':
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind != argc && !strcmp (argv[optind], "-"))
+ ignore_environment = 1;
+
+ environ = dummy_environ;
+ environ[0] = NULL;
+
+ if (!ignore_environment)
+ for (; *envp; envp++)
+ putenv (*envp);
+
+ optind = 0; /* Force GNU getopt to re-initialize. */
+ while ((optc = getopt_long (argc, argv, "+iu:", longopts, (int *) 0)) != EOF)
+ if (optc == 'u')
+ putenv (optarg); /* Requires GNU putenv. */
+
+ if (optind != argc && !strcmp (argv[optind], "-"))
+ ++optind;
+
+ while (optind < argc && index (argv[optind], '='))
+ putenv (argv[optind++]);
+
+ /* If no program is specified, print the environment and exit. */
+ if (optind == argc)
+ {
+ while (*environ)
+ puts (*environ++);
+ exit (0);
+ }
+
+ execvp (argv[optind], &argv[optind]);
+ error (errno == ENOENT ? 127 : 126, errno, "%s", argv[optind]);
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-] [-i] [-u name] [--ignore-environment] [--unset=name]\n\
+ [name=value]... [command [args...]]\n",
+ program_name);
+ exit (2);
+}
diff --git a/src/expr.c b/src/expr.c
new file mode 100644
index 000000000..db9586922
--- /dev/null
+++ b/src/expr.c
@@ -0,0 +1,672 @@
+/* expr -- evaluate expressions.
+ Copyright (C) 1986, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Author: Mike Parker.
+
+ This program evaluates expressions. Each token (operator, operand,
+ parenthesis) of the expression must be a seperate argument. The
+ parser used is a reasonably general one, though any incarnation of
+ it is language-specific. It is especially nice for expressions.
+
+ No parse tree is needed; a new node is evaluated immediately.
+ One function can handle multiple operators all of equal precedence,
+ provided they all associate ((x op x) op x).
+
+ Define EVAL_TRACE to print an evaluation trace. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <regex.h>
+#include "system.h"
+
+#if !__STDC__
+#define const
+#endif
+
+#define NEW(type) ((type *) xmalloc (sizeof (type)))
+#define OLD(x) free ((char *) x)
+
+/* The kinds of value we can have. */
+enum valtype
+{
+ integer,
+ string
+};
+typedef enum valtype TYPE;
+
+/* A value is.... */
+struct valinfo
+{
+ TYPE type; /* Which kind. */
+ union
+ { /* The value itself. */
+ int i;
+ char *s;
+ } u;
+};
+typedef struct valinfo VALUE;
+
+/* The arguments given to the program, minus the program name. */
+char **args;
+
+/* The name this program was run with. */
+char *program_name;
+
+VALUE *docolon ();
+VALUE *eval ();
+VALUE *int_value ();
+VALUE *str_value ();
+char *xstrdup ();
+char *strstr ();
+char *xmalloc ();
+int isstring ();
+int nextarg ();
+int nomoreargs ();
+int null ();
+int toarith ();
+void error ();
+void freev ();
+void printv ();
+void tostring ();
+void trace ();
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ VALUE *v;
+
+ program_name = argv[0];
+
+ if (argc == 1)
+ {
+ fprintf (stderr, "Usage: %s expression...\n", argv[0]);
+ exit (1);
+ }
+ args = argv + 1;
+
+ v = eval ();
+ if (!nomoreargs ())
+ error (2, 0, "syntax error");
+ printv (v);
+
+ exit (null (v));
+}
+
+/* Return a VALUE for I. */
+
+VALUE *
+int_value (i)
+ int i;
+{
+ VALUE *v;
+
+ v = NEW (VALUE);
+ v->type = integer;
+ v->u.i = i;
+ return v;
+}
+
+/* Return a VALUE for S. */
+
+VALUE *
+str_value (s)
+ char *s;
+{
+ VALUE *v;
+
+ v = NEW (VALUE);
+ v->type = string;
+ v->u.s = xstrdup (s);
+ return v;
+}
+
+/* Free VALUE V, including structure components. */
+
+void
+freev (v)
+ VALUE *v;
+{
+ if (v->type == string)
+ free (v->u.s);
+ OLD (v);
+}
+
+/* Print VALUE V. */
+
+void
+printv (v)
+ VALUE *v;
+{
+ switch (v->type)
+ {
+ case integer:
+ printf ("%d\n", v->u.i);
+ break;
+ case string:
+ printf ("%s\n", v->u.s);
+ break;
+ }
+}
+
+/* Return nonzero if V is a null-string or zero-number. */
+
+int
+null (v)
+ VALUE *v;
+{
+ switch (v->type)
+ {
+ case integer:
+ return v->u.i == 0;
+ case string:
+ return v->u.s[0] == '\0';
+ }
+}
+
+/* Return nonzero if V is a string value. */
+
+int
+isstring (v)
+ VALUE *v;
+{
+ return v->type == string;
+}
+
+/* Coerce V to a string value (can't fail). */
+
+void
+tostring (v)
+ VALUE *v;
+{
+ char *temp;
+
+ switch (v->type)
+ {
+ case integer:
+ temp = xmalloc (4 * (sizeof (int) / sizeof (char)));
+ sprintf (temp, "%d", v->u.i);
+ v->u.s = temp;
+ v->type = string;
+ break;
+ case string:
+ break;
+ }
+}
+
+/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
+
+int
+toarith (v)
+ VALUE *v;
+{
+ int i;
+ int neg;
+ char *cp;
+
+ switch (v->type)
+ {
+ case integer:
+ return 1;
+ case string:
+ i = 0;
+ cp = v->u.s;
+ neg = (*cp == '-');
+ if (neg)
+ cp++;
+ for (; *cp; cp++)
+ {
+ if (isdigit (*cp))
+ i = i * 10 + *cp - '0';
+ else
+ return 0;
+ }
+ free (v->u.s);
+ v->u.i = i * (neg ? -1 : 1);
+ v->type = integer;
+ return 1;
+ }
+}
+
+/* Return nonzero if the next token matches STR exactly.
+ STR must not be NULL. */
+
+int
+nextarg (str)
+ char *str;
+{
+ if (*args == NULL)
+ return 0;
+ return strcmp (*args, str) == 0;
+}
+
+/* Return nonzero if there no more tokens. */
+
+int
+nomoreargs ()
+{
+ return *args == 0;
+}
+
+/* The comparison operator handling functions. */
+
+#define cmpf(name, rel) \
+int name (l, r) VALUE *l; VALUE *r; \
+{ \
+ if (isstring (l) || isstring (r)) \
+ { \
+ tostring (l); \
+ tostring (r); \
+ return strcmp (l->u.s, r->u.s) rel 0; \
+ } \
+ else \
+ return l->u.i rel r->u.i; \
+}
+
+cmpf (less_than, <)
+cmpf (less_equal, <=)
+cmpf (equal, ==)
+cmpf (not_equal, !=)
+cmpf (greater_equal, >=)
+cmpf (greater_than, >)
+
+#undef cmpf
+
+/* The arithmetic operator handling functions. */
+
+#define arithf(name, op) \
+int name (l, r) VALUE *l; VALUE *r; \
+{ \
+ if (!toarith (l) || !toarith (r)) \
+ error (2, 0, "non-numeric argument"); \
+ return l->u.i op r->u.i; \
+}
+
+arithf (plus, +)
+arithf (minus, -)
+arithf (multiply, *)
+arithf (divide, /)
+arithf (mod, %)
+
+#undef arithf
+
+#ifdef EVAL_TRACE
+/* Print evaluation trace and args remaining. */
+
+void
+trace (fxn)
+ char *fxn;
+{
+ char **a;
+
+ printf ("%s:", fxn);
+ for (a = args; *a; a++)
+ printf (" %s", *a);
+ putchar ('\n');
+}
+#endif
+
+/* Do the : operator.
+ SV is the VALUE for the lhs (the string),
+ PV is the VALUE for the rhs (the pattern). */
+
+VALUE *
+docolon (sv, pv)
+ VALUE *sv;
+ VALUE *pv;
+{
+ VALUE *v;
+ const char *errmsg;
+ struct re_pattern_buffer re_buffer;
+ struct re_registers re_regs;
+ int len;
+
+ tostring (sv);
+ tostring (pv);
+
+ len = strlen (pv->u.s);
+ re_buffer.allocated = 2 * len;
+ re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
+ re_buffer.translate = 0;
+ errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
+ if (errmsg)
+ error (2, 0, "%s", errmsg);
+
+ len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
+ if (len >= 0)
+ {
+ /* Were \(...\) used? */
+ if (re_buffer.re_nsub > 0)/* was (re_regs.start[1] >= 0) */
+ {
+ sv->u.s[re_regs.end[1]] = '\0';
+ v = str_value (sv->u.s + re_regs.start[1]);
+ }
+ else
+ v = int_value (len);
+ }
+ else
+ {
+ /* Match failed -- return the right kind of null. */
+ if (strstr (pv->u.s, "\\("))
+ v = str_value ("");
+ else
+ v = int_value (0);
+ }
+ free (re_buffer.buffer);
+ return v;
+}
+
+/* Handle bare operands and ( expr ) syntax. */
+
+VALUE *
+eval7 ()
+{
+ VALUE *v;
+
+#ifdef EVAL_TRACE
+ trace ("eval7");
+#endif
+ if (nomoreargs ())
+ error (2, 0, "syntax error");
+ else if (nextarg ("("))
+ {
+ args++;
+ v = eval ();
+ if (!nextarg (")"))
+ error (2, 0, "syntax error");
+ args++;
+ return v;
+ }
+ else if (nextarg (")"))
+ error (2, 0, "syntax error");
+ else
+ return str_value (*args++);
+}
+
+/* Handle match, substr, index, and length keywords. */
+
+VALUE *
+eval6 ()
+{
+ VALUE *l;
+ VALUE *r;
+ VALUE *v;
+ VALUE *i1;
+ VALUE *i2;
+
+#ifdef EVAL_TRACE
+ trace ("eval6");
+#endif
+ if (nextarg ("length"))
+ {
+ args++;
+ r = eval6 ();
+ tostring (r);
+ v = int_value (strlen (r->u.s));
+ freev (r);
+ return v;
+ }
+ else if (nextarg ("match"))
+ {
+ args++;
+ l = eval6 ();
+ r = eval6 ();
+ v = docolon (l, r);
+ freev (l);
+ freev (r);
+ return v;
+ }
+ else if (nextarg ("index"))
+ {
+ args++;
+ l = eval6 ();
+ r = eval6 ();
+ tostring (l);
+ tostring (r);
+ v = int_value (strcspn (l->u.s, r->u.s) + 1);
+ if (v->u.i == strlen (l->u.s) + 1)
+ v->u.i = 0;
+ freev (l);
+ freev (r);
+ return v;
+ }
+ else if (nextarg ("substr"))
+ {
+ args++;
+ l = eval6 ();
+ i1 = eval6 ();
+ i2 = eval6 ();
+ tostring (l);
+ if (!toarith (i1) || !toarith (i2)
+ || i1->u.i > strlen (l->u.s)
+ || i1->u.i <= 0 || i2->u.i <= 0)
+ v = str_value ("");
+ else
+ {
+ v = NEW (VALUE);
+ v->type = string;
+ v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
+ l->u.s + i1->u.i - 1, i2->u.i);
+ }
+ freev (l);
+ freev (i1);
+ freev (i2);
+ return v;
+ }
+ else
+ return eval7 ();
+}
+
+/* Handle : operator (pattern matching).
+ Calls docolon to do the real work. */
+
+VALUE *
+eval5 ()
+{
+ VALUE *l;
+ VALUE *r;
+ VALUE *v;
+
+#ifdef EVAL_TRACE
+ trace ("eval5");
+#endif
+ l = eval6 ();
+ while (1)
+ {
+ if (nextarg (":"))
+ {
+ args++;
+ r = eval6 ();
+ v = docolon (l, r);
+ freev (l);
+ freev (r);
+ l = v;
+ }
+ else
+ return l;
+ }
+}
+
+/* Handle *, /, % operators. */
+
+VALUE *
+eval4 ()
+{
+ VALUE *l;
+ VALUE *r;
+ int (*fxn) ();
+ int val;
+
+#ifdef EVAL_TRACE
+ trace ("eval4");
+#endif
+ l = eval5 ();
+ while (1)
+ {
+ if (nextarg ("*"))
+ fxn = multiply;
+ else if (nextarg ("/"))
+ fxn = divide;
+ else if (nextarg ("%"))
+ fxn = mod;
+ else
+ return l;
+ args++;
+ r = eval5 ();
+ val = (*fxn) (l, r);
+ freev (l);
+ freev (r);
+ l = int_value (val);
+ }
+}
+
+/* Handle +, - operators. */
+
+VALUE *
+eval3 ()
+{
+ VALUE *l;
+ VALUE *r;
+ int (*fxn) ();
+ int val;
+
+#ifdef EVAL_TRACE
+ trace ("eval3");
+#endif
+ l = eval4 ();
+ while (1)
+ {
+ if (nextarg ("+"))
+ fxn = plus;
+ else if (nextarg ("-"))
+ fxn = minus;
+ else
+ return l;
+ args++;
+ r = eval4 ();
+ val = (*fxn) (l, r);
+ freev (l);
+ freev (r);
+ l = int_value (val);
+ }
+}
+
+/* Handle comparisons. */
+
+VALUE *
+eval2 ()
+{
+ VALUE *l;
+ VALUE *r;
+ int (*fxn) ();
+ int val;
+
+#ifdef EVAL_TRACE
+ trace ("eval2");
+#endif
+ l = eval3 ();
+ while (1)
+ {
+ if (nextarg ("<"))
+ fxn = less_than;
+ else if (nextarg ("<="))
+ fxn = less_equal;
+ else if (nextarg ("=") || nextarg ("=="))
+ fxn = equal;
+ else if (nextarg ("!="))
+ fxn = not_equal;
+ else if (nextarg (">="))
+ fxn = greater_equal;
+ else if (nextarg (">"))
+ fxn = greater_than;
+ else
+ return l;
+ args++;
+ r = eval3 ();
+ toarith (l);
+ toarith (r);
+ val = (*fxn) (l, r);
+ freev (l);
+ freev (r);
+ l = int_value (val);
+ }
+}
+
+/* Handle &. */
+
+VALUE *
+eval1 ()
+{
+ VALUE *l;
+ VALUE *r;
+
+#ifdef EVAL_TRACE
+ trace ("eval1");
+#endif
+ l = eval2 ();
+ while (1)
+ {
+ if (nextarg ("&"))
+ {
+ args++;
+ r = eval2 ();
+ if (null (l) || null (r))
+ {
+ freev (l);
+ freev (r);
+ l = int_value (0);
+ }
+ else
+ freev (r);
+ }
+ else
+ return l;
+ }
+}
+
+/* Handle |. */
+
+VALUE *
+eval ()
+{
+ VALUE *l;
+ VALUE *r;
+
+#ifdef EVAL_TRACE
+ trace ("eval");
+#endif
+ l = eval1 ();
+ while (1)
+ {
+ if (nextarg ("|"))
+ {
+ args++;
+ r = eval1 ();
+ if (null (l))
+ {
+ freev (l);
+ l = r;
+ }
+ else
+ freev (r);
+ }
+ else
+ return l;
+ }
+}
diff --git a/src/groups.sh b/src/groups.sh
new file mode 100755
index 000000000..7d8405869
--- /dev/null
+++ b/src/groups.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# groups -- print the groups a user is in
+# Copyright (C) 1991 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+
+# Make sure we get GNU id, if possible; also allow
+# it to be somewhere else in PATH if not installed yet.
+PATH=@bindir@:$PATH
+
+if [ $# -eq 0 ]; then
+ id -Gn
+else
+ for name in "$@"; do
+ echo $name : `id -Gn $name`
+ done
+fi
diff --git a/src/id.c b/src/id.c
new file mode 100644
index 000000000..beddc1356
--- /dev/null
+++ b/src/id.c
@@ -0,0 +1,346 @@
+/* id -- print real and effective UIDs and GIDs
+ Copyright (C) 1989, 1991 Free Software Foundation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Arnold Robbins, arnold@audiofax.com.
+ Major rewrite by David MacKenzie, djm@gnu.ai.mit.edu. */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include "system.h"
+
+#ifdef _POSIX_VERSION
+#include <limits.h>
+#if !defined(NGROUPS_MAX) || NGROUPS_MAX < 1
+#define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
+#endif /* !NGROUPS_MAX */
+
+/* Even though SunOS 4, Ultrix 4, and 386BSD are mostly POSIX.1 compliant,
+ their getgroups system call (except in the `System V' environment, which
+ is troublesome in other ways) fills in an array of int, not gid_t
+ (which is `short' on those systems). Kludge, kludge. */
+
+#if !defined(sun) && !defined(ultrix) && !defined(__386BSD__)
+#define GETGROUPS_T gid_t
+#else /* sun or ultrix or 386BSD */
+#define GETGROUPS_T int
+#endif /* sun or ultrix or 386BSD */
+#else /* not _POSIX_VERSION */
+struct passwd *getpwuid ();
+struct group *getgrgid ();
+uid_t getuid ();
+gid_t getgid ();
+uid_t geteuid ();
+gid_t getegid ();
+#include <sys/param.h>
+#if !defined(NGROUPS_MAX) && defined(NGROUPS)
+#define NGROUPS_MAX NGROUPS
+#endif /* not NGROUPS_MAX and NGROUPS */
+#define GETGROUPS_T int
+#endif /* not _POSIX_VERSION */
+
+char *xmalloc ();
+int getugroups ();
+void error ();
+void print_user ();
+void print_group ();
+void print_group_list ();
+void print_full_info ();
+void usage ();
+
+/* The name this program was run with. */
+char *program_name;
+
+/* If nonzero, output only the group ID(s). -g */
+int just_group = 0;
+
+/* If nonzero, output user/group name instead of ID number. -n */
+int use_name = 0;
+
+/* If nonzero, output real UID/GID instead of default effective UID/GID. -r */
+int use_real = 0;
+
+/* If nonzero, output only the user ID(s). -u */
+int just_user = 0;
+
+/* If nonzero, output only the supplementary groups. -G */
+int just_group_list = 0;
+
+/* The real and effective IDs of the user to print. */
+uid_t ruid, euid;
+gid_t rgid, egid;
+
+/* The number of errors encountered so far. */
+int problems = 0;
+
+struct option longopts[] =
+{
+ {"group", 0, NULL, 'g'},
+ {"name", 0, NULL, 'n'},
+ {"real", 0, NULL, 'r'},
+ {"user", 0, NULL, 'u'},
+ {"groups", 0, NULL, 'G'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "gnruG", longopts, (int *) 0))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 'g':
+ just_group = 1;
+ break;
+ case 'n':
+ use_name = 1;
+ break;
+ case 'r':
+ use_real = 1;
+ break;
+ case 'u':
+ just_user = 1;
+ break;
+ case 'G':
+ just_group_list = 1;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (just_user + just_group + just_group_list > 1)
+ error (1, 0, "cannot print only user and only group");
+
+ if (just_user + just_group + just_group_list == 0 && (use_real || use_name))
+ error (1, 0, "cannot print only names or real IDs in default format");
+
+ if (argc - optind > 1)
+ usage ();
+
+ if (argc - optind == 1)
+ {
+ struct passwd *pwd = getpwnam (argv[optind]);
+ if (pwd == NULL)
+ error (1, 0, "%s: No such user", argv[optind]);
+ ruid = euid = pwd->pw_uid;
+ rgid = egid = pwd->pw_gid;
+ }
+ else
+ {
+ euid = geteuid ();
+ ruid = getuid ();
+ egid = getegid ();
+ rgid = getgid ();
+ }
+
+ if (just_user)
+ print_user (use_real ? ruid : euid);
+ else if (just_group)
+ print_group (use_real ? rgid : egid);
+ else if (just_group_list)
+ print_group_list (argv[optind]);
+ else
+ print_full_info (argv[optind]);
+ putchar ('\n');
+
+ exit (problems != 0);
+}
+
+/* Print the name or value of user ID UID. */
+
+void
+print_user (uid)
+ int uid;
+{
+ struct passwd *pwd = NULL;
+
+ if (use_name)
+ {
+ pwd = getpwuid (uid);
+ if (pwd == NULL)
+ problems++;
+ }
+
+ if (pwd == NULL)
+ printf ("%u", (unsigned) uid);
+ else
+ printf ("%s", pwd->pw_name);
+}
+
+/* Print the name or value of group ID GID. */
+
+void
+print_group (gid)
+ int gid;
+{
+ struct group *grp = NULL;
+
+ if (use_name)
+ {
+ grp = getgrgid (gid);
+ if (grp == NULL)
+ problems++;
+ }
+
+ if (grp == NULL)
+ printf ("%u", (unsigned) gid);
+ else
+ printf ("%s", grp->gr_name);
+}
+
+/* Print all of the distinct groups the user is in . */
+
+void
+print_group_list (username)
+ char *username;
+{
+ print_group (rgid);
+ if (egid != rgid)
+ {
+ putchar (' ');
+ print_group (egid);
+ }
+
+#ifdef NGROUPS_MAX
+ {
+ int ngroups;
+ GETGROUPS_T *groups;
+ register int i;
+
+ groups = (GETGROUPS_T *) xmalloc (NGROUPS_MAX * sizeof (GETGROUPS_T));
+ if (username == 0)
+ ngroups = getgroups (NGROUPS_MAX, groups);
+ else
+ ngroups = getugroups (NGROUPS_MAX, groups, username);
+ if (ngroups < 0)
+ {
+ error (0, errno, "cannot get supplemental group list");
+ problems++;
+ free (groups);
+ return;
+ }
+
+ for (i = 0; i < ngroups; i++)
+ if (groups[i] != rgid && groups[i] != egid)
+ {
+ putchar (' ');
+ print_group (groups[i]);
+ }
+ free (groups);
+ }
+#endif
+}
+
+/* Print all of the info about the user's user and group IDs. */
+
+void
+print_full_info (username)
+ char *username;
+{
+ struct passwd *pwd;
+ struct group *grp;
+
+ printf ("uid=%u", (unsigned) ruid);
+ pwd = getpwuid (ruid);
+ if (pwd == NULL)
+ problems++;
+ else
+ printf ("(%s)", pwd->pw_name);
+
+ printf (" gid=%u", (unsigned) rgid);
+ grp = getgrgid (rgid);
+ if (grp == NULL)
+ problems++;
+ else
+ printf ("(%s)", grp->gr_name);
+
+ if (euid != ruid)
+ {
+ printf (" euid=%u", (unsigned) euid);
+ pwd = getpwuid (euid);
+ if (pwd == NULL)
+ problems++;
+ else
+ printf ("(%s)", pwd->pw_name);
+ }
+
+ if (egid != rgid)
+ {
+ printf (" egid=%u", (unsigned) egid);
+ grp = getgrgid (egid);
+ if (grp == NULL)
+ problems++;
+ else
+ printf ("(%s)", grp->gr_name);
+ }
+
+#ifdef NGROUPS_MAX
+ {
+ int ngroups;
+ GETGROUPS_T *groups;
+ register int i;
+
+ groups = (GETGROUPS_T *) xmalloc (NGROUPS_MAX * sizeof (GETGROUPS_T));
+ if (username == 0)
+ ngroups = getgroups (NGROUPS_MAX, groups);
+ else
+ ngroups = getugroups (NGROUPS_MAX, groups, username);
+ if (ngroups < 0)
+ {
+ error (0, errno, "cannot get supplemental group list");
+ problems++;
+ free (groups);
+ return;
+ }
+
+ if (ngroups > 0)
+ fputs (" groups=", stdout);
+ for (i = 0; i < ngroups; i++)
+ {
+ if (i > 0)
+ putchar (',');
+ printf ("%u", (unsigned) groups[i]);
+ grp = getgrgid (groups[i]);
+ if (grp == NULL)
+ problems++;
+ else
+ printf ("(%s)", grp->gr_name);
+ }
+ free (groups);
+ }
+#endif
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-gnruG] [--group] [--name] [--real] [--user] [--groups] [username]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/logname.c b/src/logname.c
new file mode 100644
index 000000000..39d213709
--- /dev/null
+++ b/src/logname.c
@@ -0,0 +1,43 @@
+/* logname -- print user's login name
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ register char *cp;
+
+ if (argc != 1)
+ {
+ fprintf (stderr, "Usage: %s\n", argv[0]);
+ exit (1);
+ }
+
+ cp = getlogin ();
+ if (cp)
+ {
+ puts (cp);
+ exit (0);
+ }
+ fprintf (stderr,"%s: no login name\n", argv[0]);
+ exit (1);
+}
diff --git a/src/nice.c b/src/nice.c
new file mode 100644
index 000000000..4911fcc70
--- /dev/null
+++ b/src/nice.c
@@ -0,0 +1,150 @@
+/* nice -- run a program with modified scheduling priority
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* David MacKenzie <djm@ai.mit.edu> */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#ifndef NICE_PRIORITY
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#include "system.h"
+
+int isinteger ();
+void error ();
+void usage ();
+
+/* The name this program was run with. */
+char *program_name;
+
+struct option longopts[] =
+{
+ {"adjustment", 1, NULL, 'n'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int current_priority;
+ int adjustment = 0;
+ int minusflag = 0;
+ int adjustment_given = 0;
+ int optc;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "+0123456789-n:", longopts,
+ (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case '?':
+ usage ();
+
+ case 'n':
+ if (!isinteger (optarg))
+ error (1, 0, "invalid priority `%s'", optarg);
+ adjustment = atoi (optarg);
+ adjustment_given = 1;
+ break;
+
+ case '-':
+ minusflag = 1;
+ break;
+
+ default:
+ adjustment = adjustment * 10 + optc - '0';
+ adjustment_given = 1;
+ }
+ }
+
+ if (minusflag)
+ adjustment = -adjustment;
+ if (!adjustment_given)
+ adjustment = 10;
+
+ if (optind == argc)
+ {
+ if (adjustment_given)
+ usage ();
+ /* No command given; print the priority. */
+ errno = 0;
+#ifndef NICE_PRIORITY
+ current_priority = getpriority (PRIO_PROCESS, 0);
+#else
+ current_priority = nice (0);
+#endif
+ if (current_priority == -1 && errno != 0)
+ error (1, errno, "cannot get priority");
+ printf ("%d\n", current_priority);
+ exit (0);
+ }
+
+ errno = 0;
+#ifndef NICE_PRIORITY
+ current_priority = getpriority (PRIO_PROCESS, 0);
+#else
+ current_priority = nice (0);
+#endif
+ if (current_priority == -1 && errno != 0)
+ error (1, errno, "cannot get priority");
+
+#ifndef NICE_PRIORITY
+ if (setpriority (PRIO_PROCESS, 0, current_priority + adjustment))
+#else
+ if (nice (adjustment) == -1)
+#endif
+ error (1, errno, "cannot set priority");
+
+ execvp (argv[optind], &argv[optind]);
+ error (errno == ENOENT ? 127 : 126, errno, "%s", argv[optind]);
+}
+
+/* Return nonzero if S represents a (possibly signed) decimal integer,
+ zero if not. */
+
+int
+isinteger (s)
+ char *s;
+{
+ if (*s == '-')
+ ++s;
+ if (*s == 0)
+ return 0;
+ while (*s)
+ {
+ if (*s < '0' || *s > '9')
+ return 0;
+ ++s;
+ }
+ return 1;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-n adjustment] [-adjustment] [--adjustment=adjustment]\n\
+ [command [arg...]]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/nohup.sh b/src/nohup.sh
new file mode 100755
index 000000000..00908b5ba
--- /dev/null
+++ b/src/nohup.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+# nohup -- run a command immume to hangups, with output to a non-tty
+# Copyright (C) 1991 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+
+# Make sure we get GNU nice, if possible; also allow
+# it to be somewhere else in PATH if not installed yet.
+PATH=@bindir@:$PATH
+
+if [ $# -eq 0 ]; then
+ echo "Usage: nohup command [arg...]" 2>&1
+ exit 1
+fi
+
+trap "" 1
+oldmask=`umask`; umask 077
+# Only redirect the output if the user didn't already do it.
+if [ -t 1 ]; then
+ # If we cannot write to the current directory, use the home directory.
+ if cat /dev/null >> nohup.out; then
+ echo "nohup: appending output to \`nohup.out'" 2>&1
+ umask $oldmask
+ exec nice -5 "$@" >> nohup.out 2>&1
+ else
+ cat /dev/null >> $HOME/nohup.out
+ echo "nohup: appending output to \`$HOME/nohup.out'" 2>&1
+ umask $oldmask
+ exec nice -5 "$@" >> $HOME/nohup.out 2>&1
+ fi
+else
+ umask $oldmask
+ exec nice -5 "$@"
+fi
diff --git a/src/pathchk.c b/src/pathchk.c
new file mode 100644
index 000000000..a8db2b28f
--- /dev/null
+++ b/src/pathchk.c
@@ -0,0 +1,332 @@
+/* pathchk -- check whether pathnames are valid or portable
+ Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: pathchk [-p] [--portability] path...
+
+ For each PATH, print a message if any of these conditions are false:
+ * all existing leading directories in PATH have search (execute) permission
+ * strlen (PATH) <= PATH_MAX
+ * strlen (each_directory_in_PATH) <= NAME_MAX
+
+ Exit status:
+ 0 All PATH names passed all of the tests.
+ 1 An error occurred.
+
+ Options:
+ -p, --portability Instead of performing length checks on the
+ underlying filesystem, test the length of the
+ pathname and its components against the POSIX.1
+ minimum limits for portability, _POSIX_NAME_MAX
+ and _POSIX_PATH_MAX in 2.9.2. Also check that
+ the pathname contains no characters not in the
+ portable filename character set.
+
+ David MacKenzie <djm@gnu.ai.mit.edu>
+ and Jim Meyering <meyering@cs.utexas.edu> */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+#ifdef _POSIX_VERSION
+#include <limits.h>
+#ifndef PATH_MAX
+#define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
+#endif /* not PATH_MAX */
+#ifndef NAME_MAX
+#define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX);
+#endif /* not NAME_MAX */
+
+#else /* not _POSIX_VERSION */
+
+#include <sys/param.h>
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else /* not MAXPATHLEN */
+#define PATH_MAX _POSIX_PATH_MAX
+#endif /* not MAXPATHLEN */
+#endif /* not PATH_MAX */
+
+#ifndef NAME_MAX
+#ifdef MAXNAMLEN
+#define NAME_MAX MAXNAMLEN
+#else /* not MAXNAMLEN */
+#define NAME_MAX _POSIX_NAME_MAX
+#endif /* not MAXNAMLEN */
+#endif /* not NAME_MAX */
+
+#endif /* not _POSIX_VERSION */
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+#ifndef _POSIX_NAME_MAX
+#define _POSIX_NAME_MAX 14
+#endif
+
+#ifndef PATH_MAX_FOR
+#define PATH_MAX_FOR(p) PATH_MAX
+#endif
+#ifndef NAME_MAX_FOR
+#define NAME_MAX_FOR(p) NAME_MAX
+#endif
+
+char *xstrdup();
+int validate_path ();
+void error ();
+void usage ();
+
+/* The name this program was run with. */
+char *program_name;
+
+struct option longopts[] =
+{
+ {"portability", 0, NULL, 'p'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int exit_status = 0;
+ int check_portability = 0;
+ int optc;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "p", longopts, (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case 'p':
+ check_portability = 1;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind == argc)
+ usage ();
+
+ for (; optind < argc; ++optind)
+ exit_status |= validate_path (argv[optind], check_portability);
+
+ exit (exit_status);
+}
+
+/* Each element is nonzero if the corresponding ASCII character is
+ in the POSIX portable character set, and zero if it is not.
+ In addition, the entry for `/' is nonzero to simplify checking. */
+char portable_chars[] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* If PATH contains only portable characters, return 1, else 0. */
+
+int
+portable_chars_only (path)
+ char *path;
+{
+ char *p;
+
+ for (p = path; *p; ++p)
+ if (portable_chars[*p] == 0)
+ {
+ error (0, 0, "path `%s' contains nonportable character `%c'",
+ path, *p);
+ return 0;
+ }
+ return 1;
+}
+
+/* Return 1 if PATH is a usable leading directory, 0 if not,
+ 2 if it doesn't exist. */
+
+int
+dir_ok (path)
+ char *path;
+{
+ struct stat stats;
+
+ if (stat (path, &stats))
+ return 2;
+
+ if (!S_ISDIR (stats.st_mode))
+ {
+ error (0, 0, "`%s' is not a directory", path);
+ return 0;
+ }
+
+ /* Use access to test for search permission because
+ testing permission bits of st_mode can lose with new
+ access control mechanisms. Of course, access loses if you're
+ running setuid. */
+ if (access (path, X_OK) != 0)
+ {
+ if (errno == EACCES)
+ error (0, 0, "directory `%s' is not searchable", path);
+ else
+ error (0, errno, "%s", path);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Make sure that
+ strlen (PATH) <= PATH_MAX
+ && strlen (each-existing-directory-in-PATH) <= NAME_MAX
+
+ If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
+ _POSIX_NAME_MAX instead, and make sure that PATH contains no
+ characters not in the POSIX portable filename character set, which
+ consists of A-Z, a-z, 0-9, ., _, -.
+
+ Make sure that all leading directories along PATH that exist have
+ `x' permission.
+
+ Return 0 if all of these tests are successful, 1 if any fail. */
+
+int
+validate_path (path, portability)
+ char *path;
+ int portability;
+{
+ int path_max;
+ int last_elem; /* Nonzero if checking last element of path. */
+ int exists; /* 2 if the path element exists. */
+ char *slash;
+ char *parent; /* Last existing leading directory so far. */
+
+ if (portability && !portable_chars_only (path))
+ return 1;
+
+ if (*path == '\0')
+ return 0;
+
+ /* Figure out the parent of the first element in PATH. */
+ parent = xstrdup (*path == '/' ? "/" : ".");
+
+ slash = path;
+ last_elem = 0;
+ while (1)
+ {
+ int name_max;
+ int length; /* Length of partial path being checked. */
+ char *start; /* Start of path element being checked. */
+
+ /* Find the end of this element of the path.
+ Then chop off the rest of the path after this element. */
+ while (*slash == '/')
+ slash++;
+ start = slash;
+ slash = index (slash, '/');
+ if (slash != NULL)
+ *slash = '\0';
+ else
+ {
+ last_elem = 1;
+ slash = index (start, '\0');
+ }
+
+ if (!last_elem)
+ {
+ exists = dir_ok (path);
+ if (dir_ok == 0)
+ {
+ free (parent);
+ return 1;
+ }
+ }
+
+ length = slash - start;
+ /* Since we know that `parent' is a directory, it's ok to call
+ pathconf with it as the argument. (If `parent' isn't a directory
+ or doesn't exist, the behavior of pathconf is undefined.)
+ But if `parent' is a directory and is on a remote file system,
+ it's likely that pathconf can't give us a reasonable value
+ and will return -1. (NFS and tempfs are not POSIX . . .)
+ In that case, we have no choice but to assume the pessimal
+ POSIX minimums. */
+ name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
+ if (name_max < 0)
+ name_max = _POSIX_NAME_MAX;
+ if (length > name_max)
+ {
+ error (0, 0, "name `%s' has length %d; exceeds limit of %d",
+ start, length, name_max);
+ free (parent);
+ return 1;
+ }
+
+ if (last_elem)
+ break;
+
+ if (exists == 1)
+ {
+ free (parent);
+ parent = xstrdup (path);
+ }
+
+ *slash++ = '/';
+ }
+
+ /* `parent' is now the last existing leading directory in the whole path,
+ so it's ok to call pathconf with it as the argument. */
+ path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
+ if (path_max < 0)
+ path_max = _POSIX_PATH_MAX;
+ free (parent);
+ if (strlen (path) > path_max)
+ {
+ error (0, 0, "path `%s' has length %d; exceeds limit of %d",
+ path, strlen (path), path_max);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-p] [--portability] path...\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/printenv.c b/src/printenv.c
new file mode 100644
index 000000000..ac4511cc5
--- /dev/null
+++ b/src/printenv.c
@@ -0,0 +1,69 @@
+/* printenv -- print all or part of environment
+ Copyright (C) 1989, 1991 Free Software Foundation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: printenv [variable...]
+
+ If no arguments are given, print the entire environment.
+ If one or more variable names are given, print the value of
+ each one that is set, and nothing for ones that are not set.
+
+ Exit status:
+ 0 if all variables specified were found
+ 1 if not
+
+ David MacKenzie and Richard Mlynarik */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+extern char **environ;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char **env;
+ char *ep, *ap;
+ int i;
+ int matches = 0;
+
+ if (argc == 1)
+ {
+ for (env = environ; *env; ++env)
+ puts (*env);
+ exit (0);
+ }
+
+ for (i = 1; i < argc; ++i)
+ {
+ for (env = environ; *env; ++env)
+ {
+ ep = *env;
+ ap = argv[i];
+ while (*ep != '\0' && *ap != '\0' && *ep++ == *ap++)
+ if (*ep == '=' && *ap == '\0')
+ {
+ puts (ep + 1);
+ ++matches;
+ break;
+ }
+ }
+ }
+ exit (matches != argc - 1);
+}
diff --git a/src/printf.c b/src/printf.c
new file mode 100644
index 000000000..b546ef65b
--- /dev/null
+++ b/src/printf.c
@@ -0,0 +1,481 @@
+/* printf - format and print data
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: printf format [argument...]
+
+ A front end to the printf function that lets it be used from the shell.
+
+ Backslash escapes:
+
+ \" = double quote
+ \\ = backslash
+ \a = alert (bell)
+ \b = backspace
+ \c = produce no further output
+ \f = form feed
+ \n = new line
+ \r = carriage return
+ \t = horizontal tab
+ \v = vertical tab
+ \0ooo = octal number (ooo is 0 to 3 digits)
+ \xhhh = hexadecimal number (hhh is 1 to 3 digits)
+
+ Additional directive:
+
+ %b = print an argument string, interpreting backslash escapes
+
+ The `format' argument is re-used as many times as necessary
+ to convert all of the given arguments.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "system.h"
+
+#ifndef STDC_HEADERS
+double strtod ();
+long strtol ();
+unsigned long strtoul ();
+#endif
+
+#define isodigit(c) ((c) >= '0' && (c) <= '7')
+#define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
+#define octtobin(c) ((c) - '0')
+
+char *xmalloc ();
+double xstrtod ();
+int print_esc ();
+int print_formatted ();
+long xstrtol ();
+unsigned long xstrtoul ();
+void error ();
+void print_direc ();
+void print_esc_char ();
+void print_esc_string ();
+void verify ();
+
+/* The name this program was run with. */
+char *program_name;
+
+/* The value to return to the calling program. */
+int exit_status;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *format;
+ int args_used;
+
+ program_name = argv[0];
+ exit_status = 0;
+
+ if (argc == 1)
+ {
+ fprintf (stderr, "Usage: %s format [argument...]\n", program_name);
+ exit (1);
+ }
+
+ format = argv[1];
+ argc -= 2;
+ argv += 2;
+
+ do
+ {
+ args_used = print_formatted (format, argc, argv);
+ argc -= args_used;
+ argv += args_used;
+ }
+ while (args_used > 0 && argc > 0);
+
+ exit (exit_status);
+}
+
+/* Print the text in FORMAT, using ARGV (with ARGC elements) for
+ arguments to any `%' directives.
+ Return the number of elements of ARGV used. */
+
+int
+print_formatted (format, argc, argv)
+ char *format;
+ int argc;
+ char **argv;
+{
+ int save_argc = argc; /* Preserve original value. */
+ char *f; /* Pointer into `format'. */
+ char *direc_start; /* Start of % directive. */
+ int direc_length; /* Length of % directive. */
+ int field_width; /* Arg to first '*', or -1 if none. */
+ int precision; /* Arg to second '*', or -1 if none. */
+
+ for (f = format; *f; ++f)
+ {
+ switch (*f)
+ {
+ case '%':
+ direc_start = f++;
+ direc_length = 1;
+ field_width = precision = -1;
+ if (*f == '%')
+ {
+ putchar ('%');
+ break;
+ }
+ if (*f == 'b')
+ {
+ if (argc > 0)
+ {
+ print_esc_string (*argv);
+ ++argv;
+ --argc;
+ }
+ break;
+ }
+ if (index ("-+ #", *f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ if (*f == '*')
+ {
+ ++f;
+ ++direc_length;
+ if (argc > 0)
+ {
+ field_width = xstrtoul (*argv);
+ ++argv;
+ --argc;
+ }
+ else
+ field_width = 0;
+ }
+ else
+ while (isdigit (*f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ if (*f == '.')
+ {
+ ++f;
+ ++direc_length;
+ if (*f == '*')
+ {
+ ++f;
+ ++direc_length;
+ if (argc > 0)
+ {
+ precision = xstrtoul (*argv);
+ ++argv;
+ --argc;
+ }
+ else
+ precision = 0;
+ }
+ else
+ while (isdigit (*f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ }
+ if (*f == 'l' || *f == 'L' || *f == 'h')
+ {
+ ++f;
+ ++direc_length;
+ }
+ if (!index ("diouxXfeEgGcs", *f))
+ error (1, 0, "%%%c: invalid directive", *f);
+ ++direc_length;
+ if (argc > 0)
+ {
+ print_direc (direc_start, direc_length, field_width,
+ precision, *argv);
+ ++argv;
+ --argc;
+ }
+ else
+ print_direc (direc_start, direc_length, field_width,
+ precision, "");
+ break;
+
+ case '\\':
+ f += print_esc (f);
+ break;
+
+ default:
+ putchar (*f);
+ }
+ }
+
+ return save_argc - argc;
+}
+
+/* Print a \ escape sequence starting at ESCSTART.
+ Return the number of characters in the escape sequence
+ besides the backslash. */
+
+int
+print_esc (escstart)
+ char *escstart;
+{
+ register char *p = escstart + 1;
+ int esc_value = 0; /* Value of \nnn escape. */
+ int esc_length; /* Length of \nnn escape. */
+
+ /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
+ if (*p == 'x')
+ {
+ for (esc_length = 0, ++p;
+ esc_length < 3 && isxdigit (*p);
+ ++esc_length, ++p)
+ esc_value = esc_value * 16 + hextobin (*p);
+ if (esc_length == 0)
+ error (1, 0, "missing hexadecimal number in escape");
+ putchar (esc_value);
+ }
+ else if (*p == '0')
+ {
+ for (esc_length = 0, ++p;
+ esc_length < 3 && isodigit (*p);
+ ++esc_length, ++p)
+ esc_value = esc_value * 8 + octtobin (*p);
+ putchar (esc_value);
+ }
+ else if (index ("\"\\abcfnrtv", *p))
+ print_esc_char (*p++);
+ else
+ error (1, 0, "\\%c: invalid escape", *p);
+ return p - escstart - 1;
+}
+
+/* Output a single-character \ escape. */
+
+void
+print_esc_char (c)
+ char c;
+{
+ switch (c)
+ {
+ case 'a': /* Alert. */
+ putchar (7);
+ break;
+ case 'b': /* Backspace. */
+ putchar (8);
+ break;
+ case 'c': /* Cancel the rest of the output. */
+ exit (0);
+ break;
+ case 'f': /* Form feed. */
+ putchar (12);
+ break;
+ case 'n': /* New line. */
+ putchar (10);
+ break;
+ case 'r': /* Carriage return. */
+ putchar (13);
+ break;
+ case 't': /* Horizontal tab. */
+ putchar (9);
+ break;
+ case 'v': /* Vertical tab. */
+ putchar (11);
+ break;
+ default:
+ putchar (c);
+ break;
+ }
+}
+
+/* Print string STR, evaluating \ escapes. */
+
+void
+print_esc_string (str)
+ char *str;
+{
+ for (; *str; str++)
+ if (*str == '\\')
+ str += print_esc (str);
+ else
+ putchar (*str);
+}
+
+/* Output a % directive. START is the start of the directive,
+ LENGTH is its length, and ARGUMENT is its argument.
+ If FIELD_WIDTH or PRECISION is non-negative, they are args for
+ '*' values in those fields. */
+
+void
+print_direc (start, length, field_width, precision, argument)
+ char *start;
+ int length;
+ int field_width;
+ int precision;
+ char *argument;
+{
+ char *p; /* Null-terminated copy of % directive. */
+
+ p = xmalloc ((unsigned) (length + 1));
+ strncpy (p, start, length);
+ p[length] = 0;
+
+ switch (p[length - 1])
+ {
+ case 'd':
+ case 'i':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, xstrtol (argument));
+ else
+ printf (p, precision, xstrtol (argument));
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, xstrtol (argument));
+ else
+ printf (p, field_width, precision, xstrtol (argument));
+ }
+ break;
+
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, xstrtoul (argument));
+ else
+ printf (p, precision, xstrtoul (argument));
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, xstrtoul (argument));
+ else
+ printf (p, field_width, precision, xstrtoul (argument));
+ }
+ break;
+
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, xstrtod (argument));
+ else
+ printf (p, precision, xstrtod (argument));
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, xstrtod (argument));
+ else
+ printf (p, field_width, precision, xstrtod (argument));
+ }
+ break;
+
+ case 'c':
+ printf (p, *argument);
+ break;
+
+ case 's':
+ if (field_width < 0)
+ {
+ if (precision < 0)
+ printf (p, argument);
+ else
+ printf (p, precision, argument);
+ }
+ else
+ {
+ if (precision < 0)
+ printf (p, field_width, argument);
+ else
+ printf (p, field_width, precision, argument);
+ }
+ break;
+ }
+
+ free (p);
+}
+
+unsigned long
+xstrtoul (s)
+ char *s;
+{
+ char *end;
+ unsigned long val;
+
+ errno = 0;
+ val = strtoul (s, &end, 0);
+ verify (s, end);
+ return val;
+}
+
+long
+xstrtol (s)
+ char *s;
+{
+ char *end;
+ long val;
+
+ errno = 0;
+ val = strtol (s, &end, 0);
+ verify (s, end);
+ return val;
+}
+
+double
+xstrtod (s)
+ char *s;
+{
+ char *end;
+ double val;
+
+ errno = 0;
+ val = strtod (s, &end);
+ verify (s, end);
+ return val;
+}
+
+void
+verify (s, end)
+ char *s, *end;
+{
+ if (errno)
+ {
+ error (0, errno, "%s", s);
+ exit_status = 1;
+ }
+ else if (*end)
+ {
+ if (s == end)
+ error (0, 0, "%s: expected a numeric value", s);
+ else
+ error (0, 0, "%s: value not completely converted", s);
+ exit_status = 1;
+ }
+}
diff --git a/src/sleep.c b/src/sleep.c
new file mode 100644
index 000000000..7212ba54f
--- /dev/null
+++ b/src/sleep.c
@@ -0,0 +1,84 @@
+/* sleep - delay for a specified amount of time.
+ Copyright (C) 1984, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+
+long argdecode ();
+void error ();
+
+/* The name by which this program was run. */
+char *program_name;
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ unsigned seconds = 0;
+
+ program_name = argv[0];
+
+ if (argc == 1)
+ {
+ fprintf (stderr, "Usage: %s number[smhd]...\n", argv[0]);
+ exit (1);
+ }
+
+ for (i = 1; i < argc; i++)
+ seconds += argdecode (argv[i]);
+
+ sleep (seconds);
+
+ exit (0);
+}
+
+long
+argdecode (s)
+ char *s;
+{
+ long value;
+ register char *p = s;
+ register char c;
+
+ value = 0;
+ while ((c = *p++) >= '0' && c <= '9')
+ value = value * 10 + c - '0';
+
+ switch (c)
+ {
+ case 's':
+ break;
+ case 'm':
+ value *= 60;
+ break;
+ case 'h':
+ value *= 60 * 60;
+ break;
+ case 'd':
+ value *= 60 * 60 * 24;
+ break;
+ default:
+ p--;
+ }
+
+ if (*p)
+ error (1, 0, "invalid time interval `%s'", s);
+ return value;
+}
diff --git a/src/stty.c b/src/stty.c
new file mode 100644
index 000000000..5349af0f2
--- /dev/null
+++ b/src/stty.c
@@ -0,0 +1,1241 @@
+/* stty -- change and print terminal line settings
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Usage: stty [-ag] [--all] [--save] [setting...]
+
+ Options:
+ -a, --all Write all current settings to stdout in human-readable form.
+ -g, --save Write all current settings to stdout in stty-readable form.
+
+ If no args are given, write to stdout the baud rate and settings that
+ have been changed from their defaults. Mode reading and changes
+ are done on stdin.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <termios.h>
+#ifdef _AIX
+#include <sys/ioctl.h> /* Needed to get window size. */
+#endif
+#ifdef WINSIZE_IN_PTEM
+#include <sys/stream.h>
+#include <sys/ptem.h>
+#endif
+#include <getopt.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#else
+#include <varargs.h>
+#define VA_START(args, lastarg) va_start(args)
+#endif
+#include "system.h"
+
+#if defined(GWINSZ_BROKEN) /* Such as for SCO UNIX 3.2.2. */
+#undef TIOCGWINSZ
+#endif
+
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE ((unsigned char) 0)
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters. */
+#ifndef CINTR
+#define CINTR Control ('c')
+#endif
+#ifndef CQUIT
+#define CQUIT 28
+#endif
+#ifndef CERASE
+#define CERASE 127
+#endif
+#ifndef CKILL
+#define CKILL Control ('u')
+#endif
+#ifndef CEOF
+#define CEOF Control ('d')
+#endif
+#ifndef CEOL
+#define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+#define CSTART Control ('q')
+#endif
+#ifndef CSTOP
+#define CSTOP Control ('s')
+#endif
+#ifndef CSUSP
+#define CSUSP Control ('z')
+#endif
+#if defined(VEOL2) && !defined(CEOL2)
+#define CEOL2 _POSIX_VDISABLE
+#endif
+#if defined(VSWTCH) && !defined(CSWTCH)
+#define CSWTCH _POSIX_VDISABLE
+#endif
+#if defined(VDSUSP) && !defined (CDSUSP)
+#define CDSUSP Control ('y')
+#endif
+#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
+#define VREPRINT VRPRNT
+#endif
+#if defined(VREPRINT) && !defined(CRPRNT)
+#define CRPRNT Control ('r')
+#endif
+#if defined(VWERASE) && !defined(CWERASE)
+#define CWERASE Control ('w')
+#endif
+#if defined(VLNEXT) && !defined(CLNEXT)
+#define CLNEXT Control ('v')
+#endif
+
+char *visible ();
+unsigned long baud_to_value ();
+int recover_mode ();
+int screen_columns ();
+int set_mode ();
+long integer_arg ();
+speed_t string_to_baud ();
+tcflag_t *mode_type_flag ();
+void display_all ();
+void display_changed ();
+void display_recoverable ();
+void display_settings ();
+void display_speed ();
+void display_window_size ();
+void error ();
+void sane_mode ();
+void set_control_char ();
+void set_speed ();
+void set_window_size ();
+
+/* Which speeds to set. */
+enum speed_setting
+{
+ input_speed, output_speed, both_speeds
+};
+
+/* What to output and how. */
+enum output_type
+{
+ changed, all, recoverable /* Default, -a, -g. */
+};
+
+/* Which member(s) of `struct termios' a mode uses. */
+enum mode_type
+{
+ control, input, output, local, combination
+};
+
+/* Flags for `struct mode_info'. */
+#define SANE_SET 1 /* Set in `sane' mode. */
+#define SANE_UNSET 2 /* Unset in `sane' mode. */
+#define REV 4 /* Can be turned off by prepending `-'. */
+#define OMIT 8 /* Don't display value. */
+
+/* Each mode. */
+struct mode_info
+{
+ char *name; /* Name given on command line. */
+ enum mode_type type; /* Which structure element to change. */
+ char flags; /* Setting and display options. */
+ unsigned long bits; /* Bits to set for this mode. */
+ unsigned long mask; /* Other bits to turn off for this mode. */
+};
+
+struct mode_info mode_info[] =
+{
+ {"parenb", control, REV, PARENB, 0},
+ {"parodd", control, REV, PARODD, 0},
+ {"cs5", control, 0, CS5, CSIZE},
+ {"cs6", control, 0, CS6, CSIZE},
+ {"cs7", control, 0, CS7, CSIZE},
+ {"cs8", control, 0, CS8, CSIZE},
+ {"hupcl", control, REV, HUPCL, 0},
+ {"hup", control, REV | OMIT, HUPCL, 0},
+ {"cstopb", control, REV, CSTOPB, 0},
+ {"cread", control, SANE_SET | REV, CREAD, 0},
+ {"clocal", control, REV, CLOCAL, 0},
+#ifdef CRTSCTS
+ {"crtscts", control, REV, CRTSCTS, 0},
+#endif
+
+ {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0},
+ {"brkint", input, SANE_SET | REV, BRKINT, 0},
+ {"ignpar", input, REV, IGNPAR, 0},
+ {"parmrk", input, REV, PARMRK, 0},
+ {"inpck", input, REV, INPCK, 0},
+ {"istrip", input, REV, ISTRIP, 0},
+ {"inlcr", input, SANE_UNSET | REV, INLCR, 0},
+ {"igncr", input, SANE_UNSET | REV, IGNCR, 0},
+ {"icrnl", input, SANE_SET | REV, ICRNL, 0},
+ {"ixon", input, REV, IXON, 0},
+ {"ixoff", input, SANE_UNSET | REV, IXOFF, 0},
+ {"tandem", input, REV | OMIT, IXOFF, 0},
+#ifdef IUCLC
+ {"iuclc", input, SANE_UNSET | REV, IUCLC, 0},
+#endif
+#ifdef IXANY
+ {"ixany", input, SANE_UNSET | REV, IXANY, 0},
+#endif
+#ifdef IMAXBEL
+ {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0},
+#endif
+
+ {"opost", output, SANE_SET | REV, OPOST, 0},
+#ifdef OLCUC
+ {"olcuc", output, SANE_UNSET | REV, OLCUC, 0},
+#endif
+#ifdef OCRNL
+ {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0},
+#endif
+#ifdef ONLCR
+ {"onlcr", output, SANE_SET | REV, ONLCR, 0},
+#endif
+#ifdef ONOCR
+ {"onocr", output, SANE_UNSET | REV, ONOCR, 0},
+#endif
+#ifdef ONLRET
+ {"onlret", output, SANE_UNSET | REV, ONLRET, 0},
+#endif
+#ifdef OFILL
+ {"ofill", output, SANE_UNSET | REV, OFILL, 0},
+#endif
+#ifdef OFDEL
+ {"ofdel", output, SANE_UNSET | REV, OFDEL, 0},
+#endif
+#ifdef NLDLY
+ {"nl1", output, SANE_UNSET, NL1, NLDLY},
+ {"nl0", output, SANE_SET, NL0, NLDLY},
+#endif
+#ifdef CRDLY
+ {"cr3", output, SANE_UNSET, CR3, CRDLY},
+ {"cr2", output, SANE_UNSET, CR2, CRDLY},
+ {"cr1", output, SANE_UNSET, CR1, CRDLY},
+ {"cr0", output, SANE_SET, CR0, CRDLY},
+#endif
+#ifdef TABDLY
+ {"tab3", output, SANE_UNSET, TAB3, TABDLY},
+ {"tab2", output, SANE_UNSET, TAB2, TABDLY},
+ {"tab1", output, SANE_UNSET, TAB1, TABDLY},
+ {"tab0", output, SANE_SET, TAB0, TABDLY},
+#endif
+#ifdef BSDLY
+ {"bs1", output, SANE_UNSET, BS1, BSDLY},
+ {"bs0", output, SANE_SET, BS0, BSDLY},
+#endif
+#ifdef VTDLY
+ {"vt1", output, SANE_UNSET, VT1, VTDLY},
+ {"vt0", output, SANE_SET, VT0, VTDLY},
+#endif
+#ifdef FFDLY
+ {"ff1", output, SANE_UNSET, FF1, FFDLY},
+ {"ff0", output, SANE_SET, FF0, FFDLY},
+#endif
+
+ {"isig", local, SANE_SET | REV, ISIG, 0},
+ {"icanon", local, SANE_SET | REV, ICANON, 0},
+#ifdef IEXTEN
+ {"iexten", local, SANE_SET | REV, IEXTEN, 0},
+#endif
+ {"echo", local, SANE_SET | REV, ECHO, 0},
+ {"echoe", local, SANE_SET | REV, ECHOE, 0},
+ {"crterase", local, REV | OMIT, ECHOE, 0},
+ {"echok", local, SANE_SET | REV, ECHOK, 0},
+ {"echonl", local, SANE_UNSET | REV, ECHONL, 0},
+ {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0},
+#ifdef XCASE
+ {"xcase", local, SANE_UNSET | REV, XCASE, 0},
+#endif
+#ifdef TOSTOP
+ {"tostop", local, SANE_UNSET | REV, TOSTOP, 0},
+#endif
+#ifdef ECHOPRT
+ {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0},
+ {"prterase", local, REV | OMIT, ECHOPRT, 0},
+#endif
+#ifdef ECHOCTL
+ {"echoctl", local, SANE_SET | REV, ECHOCTL, 0},
+ {"ctlecho", local, REV | OMIT, ECHOCTL, 0},
+#endif
+#ifdef ECHOKE
+ {"echoke", local, SANE_SET | REV, ECHOKE, 0},
+ {"crtkill", local, REV | OMIT, ECHOKE, 0},
+#endif
+
+ {"evenp", combination, REV | OMIT, 0, 0},
+ {"parity", combination, REV | OMIT, 0, 0},
+ {"oddp", combination, REV | OMIT, 0, 0},
+ {"nl", combination, REV | OMIT, 0, 0},
+ {"ek", combination, OMIT, 0, 0},
+ {"sane", combination, OMIT, 0, 0},
+ {"cooked", combination, REV | OMIT, 0, 0},
+ {"raw", combination, REV | OMIT, 0, 0},
+ {"pass8", combination, REV | OMIT, 0, 0},
+ {"litout", combination, REV | OMIT, 0, 0},
+ {"cbreak", combination, REV | OMIT, 0, 0},
+#ifdef IXANY
+ {"decctlq", combination, REV | OMIT, 0, 0},
+#endif
+#ifdef TABDLY
+ {"tabs", combination, REV | OMIT, 0, 0},
+#endif
+#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+ {"lcase", combination, REV | OMIT, 0, 0},
+ {"LCASE", combination, REV | OMIT, 0, 0},
+#endif
+ {"crt", combination, OMIT, 0, 0},
+ {"dec", combination, OMIT, 0, 0},
+
+ {NULL, control, 0, 0, 0}
+};
+
+/* Control character settings. */
+struct control_info
+{
+ char *name; /* Name given on command line. */
+ unsigned char saneval; /* Value to set for `stty sane'. */
+ int offset; /* Offset in c_cc. */
+};
+
+/* Control characters. */
+
+struct control_info control_info[] =
+{
+ {"intr", CINTR, VINTR},
+ {"quit", CQUIT, VQUIT},
+ {"erase", CERASE, VERASE},
+ {"kill", CKILL, VKILL},
+ {"eof", CEOF, VEOF},
+ {"eol", CEOL, VEOL},
+#ifdef VEOL2
+ {"eol2", CEOL2, VEOL2},
+#endif
+#ifdef VSWTCH
+ {"swtch", CSWTCH, VSWTCH},
+#endif
+ {"start", CSTART, VSTART},
+ {"stop", CSTOP, VSTOP},
+ {"susp", CSUSP, VSUSP},
+#ifdef VDSUSP
+ {"dsusp", CDSUSP, VDSUSP},
+#endif
+#ifdef VREPRINT
+ {"rprnt", CRPRNT, VREPRINT},
+#endif
+#ifdef VWERASE
+ {"werase", CWERASE, VWERASE},
+#endif
+#ifdef VLNEXT
+ {"lnext", CLNEXT, VLNEXT},
+#endif
+
+ /* These must be last because of the display routines. */
+ {"min", 1, VMIN},
+ {"time", 0, VTIME},
+ {NULL, 0, 0}
+};
+
+/* The width of the screen, for output wrapping. */
+int max_col;
+
+/* Current position, to know when to wrap. */
+int current_col;
+
+struct option longopts[] =
+{
+ {"all", 0, NULL, 'a'},
+ {"save", 0, NULL, 'g'},
+ {NULL, 0, NULL, 0}
+};
+
+/* The name this program was run with. */
+char *program_name;
+
+/* Print format string MESSAGE and optional args.
+ Wrap to next line first if it won't fit.
+ Print a space first unless MESSAGE will start a new line. */
+
+/* VARARGS */
+void
+#ifdef __STDC__
+wrapf (char *message, ...)
+#else
+wrapf (message, va_alist)
+ char *message;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[1024]; /* Plenty long for our needs. */
+ int buflen;
+
+ VA_START (args, message);
+ vsprintf (buf, message, args);
+ va_end (args);
+ buflen = strlen (buf);
+ if (current_col + buflen >= max_col)
+ {
+ putchar ('\n');
+ current_col = 0;
+ }
+ if (current_col > 0)
+ {
+ putchar (' ');
+ current_col++;
+ }
+ fputs (buf, stdout);
+ current_col += buflen;
+}
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ struct termios mode;
+ enum output_type output_type = changed;
+ int optc;
+
+ program_name = argv[0];
+ opterr = 0;
+
+ while ((optc = getopt_long (argc, argv, "ag", longopts, (int *) 0)) != EOF)
+ {
+ if (optc == 'a')
+ output_type = all;
+ else if (optc == 'g')
+ output_type = recoverable;
+ else
+ break;
+ }
+
+ if (tcgetattr (0, &mode))
+ error (1, errno, "standard input");
+
+ max_col = screen_columns ();
+ current_col = 0;
+
+ if (optind == argc)
+ {
+ if (optc == '?')
+ error (1, 0, "invalid argument `%s'", argv[--optind]);
+ display_settings (output_type, &mode);
+ exit (0);
+ }
+
+ while (optind < argc)
+ {
+ int match_found = 0;
+ int reversed = 0;
+ int i;
+
+ if (argv[optind][0] == '-')
+ {
+ ++argv[optind];
+ reversed = 1;
+ }
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (!strcmp (argv[optind], mode_info[i].name))
+ {
+ match_found = set_mode (&mode_info[i], reversed, &mode);
+ break;
+ }
+ }
+ if (match_found == 0 && reversed)
+ error (1, 0, "invalid argument `%s'", --argv[optind]);
+ if (match_found == 0)
+ {
+ for (i = 0; control_info[i].name != NULL; ++i)
+ {
+ if (!strcmp (argv[optind], control_info[i].name))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ match_found = 1;
+ ++optind;
+ set_control_char (&control_info[i], argv[optind], &mode);
+ break;
+ }
+ }
+ }
+ if (match_found == 0)
+ {
+ if (!strcmp (argv[optind], "ispeed"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_speed (input_speed, argv[optind], &mode);
+ }
+ else if (!strcmp (argv[optind], "ospeed"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_speed (output_speed, argv[optind], &mode);
+ }
+#ifdef TIOCGWINSZ
+ else if (!strcmp (argv[optind], "rows"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_window_size ((int) integer_arg (argv[optind]), -1);
+ }
+ else if (!strcmp (argv[optind], "cols")
+ || !strcmp (argv[optind], "columns"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ set_window_size (-1, (int) integer_arg (argv[optind]));
+ }
+ else if (!strcmp (argv[optind], "size"))
+ display_window_size (0);
+#endif
+#ifdef HAVE_C_LINE
+ else if (!strcmp (argv[optind], "line"))
+ {
+ if (optind == argc - 1)
+ error (1, 0, "missing argument to `%s'", argv[optind]);
+ ++optind;
+ mode.c_line = integer_arg (argv[optind]);
+ }
+#endif
+ else if (!strcmp (argv[optind], "speed"))
+ display_speed (&mode, 0);
+ else if (string_to_baud (argv[optind]) != (speed_t) -1)
+ set_speed (both_speeds, argv[optind], &mode);
+ else if (recover_mode (argv[optind], &mode) == 0)
+ error (1, 0, "invalid argument `%s'", argv[optind]);
+ }
+ optind++;
+ }
+
+ if (tcsetattr (0, TCSADRAIN, &mode))
+ error (1, errno, "standard input");
+
+ exit (0);
+}
+
+/* Return 0 if not applied because not reversible; otherwise return 1. */
+
+int
+set_mode (info, reversed, mode)
+ struct mode_info *info;
+ int reversed;
+ struct termios *mode;
+{
+ tcflag_t *bitsp;
+
+ if (reversed && (info->flags & REV) == 0)
+ return 0;
+
+ bitsp = mode_type_flag (info->type, mode);
+
+ if (bitsp == NULL)
+ {
+ /* Combination mode. */
+ if (!strcmp (info->name, "evenp") || !strcmp (info->name, "parity"))
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+ }
+ else if (!strcmp (info->name, "oddp"))
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+ }
+ else if (!strcmp (info->name, "nl"))
+ {
+ if (reversed)
+ {
+ mode->c_iflag = mode->c_iflag | ICRNL & ~INLCR & ~IGNCR;
+ mode->c_oflag = mode->c_oflag
+#ifdef ONLCR
+ | ONLCR
+#endif
+#ifdef OCRNL
+ & ~OCRNL
+#endif
+#ifdef ONLRET
+ & ~ONLRET
+#endif
+ ;
+ }
+ else
+ {
+ mode->c_iflag = mode->c_iflag & ~ICRNL;
+#ifdef ONLCR
+ mode->c_oflag = mode->c_oflag & ~ONLCR;
+#endif
+ }
+ }
+ else if (!strcmp (info->name, "ek"))
+ {
+ mode->c_cc[VERASE] = CERASE;
+ mode->c_cc[VKILL] = CKILL;
+ }
+ else if (!strcmp (info->name, "sane"))
+ sane_mode (mode);
+ else if (!strcmp (info->name, "cbreak"))
+ {
+ if (reversed)
+ mode->c_lflag |= ICANON;
+ else
+ mode->c_lflag &= ~ICANON;
+ }
+ else if (!strcmp (info->name, "pass8"))
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ }
+ }
+ else if (!strcmp (info->name, "litout"))
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ mode->c_oflag |= OPOST;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ mode->c_oflag &= ~OPOST;
+ }
+ }
+ else if (!strcmp (info->name, "raw") || !strcmp (info->name, "cooked"))
+ {
+ if ((info->name[0] == 'r' && reversed)
+ || (info->name[0] == 'c' && !reversed))
+ {
+ /* Cooked mode. */
+ mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+ mode->c_oflag |= OPOST;
+ mode->c_lflag |= ISIG | ICANON;
+#if VMIN == VEOF
+ mode->c_cc[VEOF] = CEOF;
+#endif
+#if VTIME == VEOL
+ mode->c_cc[VEOL] = CEOL;
+#endif
+ }
+ else
+ {
+ /* Raw mode. */
+ mode->c_iflag = 0;
+ mode->c_oflag &= ~OPOST;
+ mode->c_lflag &= ~(ISIG | ICANON
+#ifdef XCASE
+ | XCASE
+#endif
+ );
+ mode->c_cc[VMIN] = 1;
+ mode->c_cc[VTIME] = 0;
+ }
+ }
+#ifdef IXANY
+ else if (!strcmp (info->name, "decctlq"))
+ {
+ if (reversed)
+ mode->c_iflag |= IXANY;
+ else
+ mode->c_iflag &= ~IXANY;
+ }
+#endif
+#ifdef TABDLY
+ else if (!strcmp (info->name, "tabs"))
+ {
+ if (reversed)
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+ else
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+ }
+#endif
+#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
+ else if (!strcmp (info->name, "lcase")
+ || !strcmp (info->name, "LCASE"))
+ {
+ if (reversed)
+ {
+ mode->c_lflag &= ~XCASE;
+ mode->c_iflag &= ~IUCLC;
+ mode->c_oflag &= ~OLCUC;
+ }
+ else
+ {
+ mode->c_lflag |= XCASE;
+ mode->c_iflag |= IUCLC;
+ mode->c_oflag |= OLCUC;
+ }
+ }
+#endif
+ else if (!strcmp (info->name, "crt"))
+ mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ ;
+ else if (!strcmp (info->name, "dec"))
+ {
+ mode->c_cc[VINTR] = 3; /* ^C */
+ mode->c_cc[VERASE] = 127; /* DEL */
+ mode->c_cc[VKILL] = 21; /* ^U */
+ mode->c_lflag |= ECHOE
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ ;
+#ifdef IXANY
+ mode->c_iflag &= ~IXANY;
+#endif
+ }
+ }
+ else if (reversed)
+ *bitsp = *bitsp & ~info->mask & ~info->bits;
+ else
+ *bitsp = (*bitsp & ~info->mask) | info->bits;
+
+ return 1;
+}
+
+void
+set_control_char (info, arg, mode)
+ struct control_info *info;
+ char *arg;
+ struct termios *mode;
+{
+ unsigned char value;
+
+ if (!strcmp (info->name, "min") || !strcmp (info->name, "time"))
+ value = integer_arg (arg);
+ else if (arg[0] == '\0' || arg[1] == '\0')
+ value = arg[0];
+ else if (!strcmp (arg, "^-") || !strcmp (arg, "undef"))
+ value = _POSIX_VDISABLE;
+ else if (arg[0] == '^' && arg[1] != '\0') /* Ignore any trailing junk. */
+ {
+ if (arg[1] == '?')
+ value = 127;
+ else
+ value = arg[1] & ~0140; /* Non-letters get weird results. */
+ }
+ else
+ value = integer_arg (arg);
+ mode->c_cc[info->offset] = value;
+}
+
+void
+set_speed (type, arg, mode)
+ enum speed_setting type;
+ char *arg;
+ struct termios *mode;
+{
+ speed_t baud;
+
+ baud = string_to_baud (arg);
+ if (type == input_speed || type == both_speeds)
+ cfsetispeed (mode, baud);
+ if (type == output_speed || type == both_speeds)
+ cfsetospeed (mode, baud);
+}
+
+#ifdef TIOCGWINSZ
+void
+set_window_size (rows, cols)
+ int rows, cols;
+{
+ struct winsize win;
+
+ if (ioctl (0, TIOCGWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+ if (rows >= 0)
+ win.ws_row = rows;
+ if (cols >= 0)
+ win.ws_col = cols;
+ if (ioctl (0, TIOCSWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+}
+
+void
+display_window_size (fancy)
+ int fancy;
+{
+ struct winsize win;
+
+ if (ioctl (0, TIOCGWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+ wrapf (fancy ? "rows %d; columns %d;" : "%d %d\n", win.ws_row, win.ws_col);
+ if (!fancy)
+ current_col = 0;
+}
+#endif
+
+int
+screen_columns ()
+{
+#ifdef TIOCGWINSZ
+ struct winsize win;
+
+ if (ioctl (0, TIOCGWINSZ, (char *) &win))
+ error (1, errno, "standard input");
+ if (win.ws_col > 0)
+ return win.ws_col;
+#endif
+ if (getenv ("COLUMNS"))
+ return atoi (getenv ("COLUMNS"));
+ return 80;
+}
+
+tcflag_t *
+mode_type_flag (type, mode)
+ enum mode_type type;
+ struct termios *mode;
+{
+ switch (type)
+ {
+ case control:
+ return &mode->c_cflag;
+
+ case input:
+ return &mode->c_iflag;
+
+ case output:
+ return &mode->c_oflag;
+
+ case local:
+ return &mode->c_lflag;
+
+ case combination:
+ return NULL;
+ }
+}
+
+void
+display_settings (output_type, mode)
+ enum output_type output_type;
+ struct termios *mode;
+{
+ switch (output_type)
+ {
+ case changed:
+ display_changed (mode);
+ break;
+
+ case all:
+ display_all (mode);
+ break;
+
+ case recoverable:
+ display_recoverable (mode);
+ break;
+ }
+}
+
+void
+display_changed (mode)
+ struct termios *mode;
+{
+ int i;
+ int empty_line;
+ tcflag_t *bitsp;
+ unsigned long mask;
+ enum mode_type prev_type = control;
+
+ display_speed (mode, 1);
+#ifdef HAVE_C_LINE
+ wrapf ("line = %d;", mode->c_line);
+#endif
+ putchar ('\n');
+ current_col = 0;
+
+ empty_line = 1;
+ for (i = 0; strcmp (control_info[i].name, "min"); ++i)
+ {
+ if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
+ continue;
+ empty_line = 0;
+ wrapf ("%s = %s;", control_info[i].name,
+ visible (mode->c_cc[control_info[i].offset]));
+ }
+ if ((mode->c_lflag & ICANON) == 0)
+ {
+ wrapf ("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
+ (int) mode->c_cc[VTIME]);
+ }
+ else if (empty_line == 0)
+ putchar ('\n');
+ current_col = 0;
+
+ empty_line = 1;
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & OMIT)
+ continue;
+ if (mode_info[i].type != prev_type)
+ {
+ if (empty_line == 0)
+ {
+ putchar ('\n');
+ current_col = 0;
+ empty_line = 1;
+ }
+ prev_type = mode_info[i].type;
+ }
+
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ if ((*bitsp & mask) == mode_info[i].bits)
+ {
+ if (mode_info[i].flags & SANE_UNSET)
+ {
+ wrapf ("%s", mode_info[i].name);
+ empty_line = 0;
+ }
+ }
+ else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
+ {
+ wrapf ("-%s", mode_info[i].name);
+ empty_line = 0;
+ }
+ }
+ if (empty_line == 0)
+ putchar ('\n');
+ current_col = 0;
+}
+
+void
+display_all (mode)
+ struct termios *mode;
+{
+ int i;
+ tcflag_t *bitsp;
+ unsigned long mask;
+ enum mode_type prev_type = control;
+
+ display_speed (mode, 1);
+#ifdef TIOCGWINSZ
+ display_window_size (1);
+#endif
+#ifdef HAVE_C_LINE
+ wrapf ("line = %d;", mode->c_line);
+#endif
+ putchar ('\n');
+ current_col = 0;
+
+ for (i = 0; strcmp (control_info[i].name, "min"); ++i)
+ {
+ wrapf ("%s = %s;", control_info[i].name,
+ visible (mode->c_cc[control_info[i].offset]));
+ }
+ wrapf ("min = %d; time = %d;\n", mode->c_cc[VMIN], mode->c_cc[VTIME]);
+ current_col = 0;
+
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & OMIT)
+ continue;
+ if (mode_info[i].type != prev_type)
+ {
+ putchar ('\n');
+ current_col = 0;
+ prev_type = mode_info[i].type;
+ }
+
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ if ((*bitsp & mask) == mode_info[i].bits)
+ wrapf ("%s", mode_info[i].name);
+ else if (mode_info[i].flags & REV)
+ wrapf ("-%s", mode_info[i].name);
+ }
+ putchar ('\n');
+ current_col = 0;
+}
+
+void
+display_speed (mode, fancy)
+ struct termios *mode;
+ int fancy;
+{
+ if (cfgetispeed (mode) == 0 || cfgetispeed (mode) == cfgetospeed (mode))
+ wrapf (fancy ? "speed %lu baud;" : "%lu\n",
+ baud_to_value (cfgetospeed (mode)));
+ else
+ wrapf (fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
+ baud_to_value (cfgetispeed (mode)),
+ baud_to_value (cfgetospeed (mode)));
+ if (!fancy)
+ current_col = 0;
+}
+
+void
+display_recoverable (mode)
+ struct termios *mode;
+{
+ int i;
+
+ printf ("%lx:%lx:%lx:%lx",
+ (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
+ (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
+ for (i = 0; i < NCCS; ++i)
+ printf (":%x", (unsigned int) mode->c_cc[i]);
+ putchar ('\n');
+}
+
+int
+recover_mode (arg, mode)
+ char *arg;
+ struct termios *mode;
+{
+ int i, n;
+ unsigned int chr;
+ unsigned long iflag, oflag, cflag, lflag;
+
+ /* Scan into temporaries since it is too much trouble to figure out
+ the right format for `tcflag_t'. */
+ if (sscanf (arg, "%lx:%lx:%lx:%lx%n",
+ &iflag, &oflag, &cflag, &lflag, &n) != 4)
+ return 0;
+ mode->c_iflag = iflag;
+ mode->c_oflag = oflag;
+ mode->c_cflag = cflag;
+ mode->c_lflag = lflag;
+ arg += n;
+ for (i = 0; i < NCCS; ++i)
+ {
+ if (sscanf (arg, ":%x%n", &chr, &n) != 1)
+ return 0;
+ mode->c_cc[i] = chr;
+ arg += n;
+ }
+ return 1;
+}
+
+struct speed_map
+{
+ char *string; /* ASCII representation. */
+ speed_t speed; /* Internal form. */
+ unsigned long value; /* Numeric value. */
+};
+
+struct speed_map speeds[] =
+{
+ {"0", B0, 0},
+ {"50", B50, 50},
+ {"75", B75, 75},
+ {"110", B110, 110},
+ {"134", B134, 134},
+ {"134.5", B134, 134},
+ {"150", B150, 150},
+ {"200", B200, 200},
+ {"300", B300, 300},
+ {"600", B600, 600},
+ {"1200", B1200, 1200},
+ {"1800", B1800, 1800},
+ {"2400", B2400, 2400},
+ {"4800", B4800, 4800},
+ {"9600", B9600, 9600},
+ {"19200", B19200, 19200},
+ {"38400", B38400, 38400},
+ {"exta", B19200, 19200},
+ {"extb", B38400, 38400},
+#ifdef B57600
+ {"57600", B57600, 57600},
+#endif
+#ifdef B115200
+ {"115200", B115200, 115200},
+#endif
+ {NULL, 0, 0}
+};
+
+speed_t
+string_to_baud (arg)
+ char *arg;
+{
+ int i;
+
+ for (i = 0; speeds[i].string != NULL; ++i)
+ if (!strcmp (arg, speeds[i].string))
+ return speeds[i].speed;
+ return (speed_t) -1;
+}
+
+unsigned long
+baud_to_value (speed)
+ speed_t speed;
+{
+ int i;
+
+ for (i = 0; speeds[i].string != NULL; ++i)
+ if (speed == speeds[i].speed)
+ return speeds[i].value;
+ return 0;
+}
+
+void
+sane_mode (mode)
+ struct termios *mode;
+{
+ int i;
+ tcflag_t *bitsp;
+
+ for (i = 0; control_info[i].name; ++i)
+ {
+#if VMIN == VEOF
+ if (!strcmp (control_info[i].name, "min"))
+ break;
+#endif
+ mode->c_cc[control_info[i].offset] = control_info[i].saneval;
+ }
+
+ for (i = 0; mode_info[i].name != NULL; ++i)
+ {
+ if (mode_info[i].flags & SANE_SET)
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
+ }
+ else if (mode_info[i].flags & SANE_UNSET)
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
+ }
+ }
+}
+
+/* Return a string that is the printable representation of character CH. */
+/* Adapted from `cat' by Torbjorn Granlund. */
+
+char *
+visible (ch)
+ unsigned char ch;
+{
+ static char buf[10];
+ char *bpout = buf;
+
+ if (ch == _POSIX_VDISABLE)
+ return "<undef>";
+
+ if (ch >= 32)
+ {
+ if (ch < 127)
+ *bpout++ = ch;
+ else if (ch == 127)
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ else
+ {
+ *bpout++ = 'M',
+ *bpout++ = '-';
+ if (ch >= 128 + 32)
+ {
+ if (ch < 128 + 127)
+ *bpout++ = ch - 128;
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch - 128 + 64;
+ }
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch + 64;
+ }
+ *bpout = '\0';
+ return buf;
+}
+
+/* Parse string S as an integer, using decimal radix by default,
+ but allowing octal and hex numbers as in C. */
+/* From `od' by Richard Stallman. */
+
+long
+integer_arg (s)
+ char *s;
+{
+ long value;
+ int radix = 10;
+ char *p = s;
+ int c;
+
+ if (*p != '0')
+ radix = 10;
+ else if (*++p == 'x')
+ {
+ radix = 16;
+ p++;
+ }
+ else
+ radix = 8;
+
+ value = 0;
+ while (((c = *p++) >= '0' && c <= '9')
+ || (radix == 16 && (c & ~40) >= 'A' && (c & ~40) <= 'Z'))
+ {
+ value *= radix;
+ if (c >= '0' && c <= '9')
+ value += c - '0';
+ else
+ value += (c & ~40) - 'A';
+ }
+
+ if (c == 'b')
+ value *= 512;
+ else if (c == 'B')
+ value *= 1024;
+ else
+ p--;
+
+ if (*p)
+ error (1, 0, "invalid integer argument `%s'", s);
+ return value;
+}
diff --git a/src/su.c b/src/su.c
new file mode 100644
index 000000000..dc29063cc
--- /dev/null
+++ b/src/su.c
@@ -0,0 +1,519 @@
+/* su for GNU. Run a shell with substitute user and group IDs.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Run a shell with the real and effective UID and GID and groups
+ of USER, default `root'.
+
+ The shell run is taken from USER's password entry, /bin/sh if
+ none is specified there. If the account has a password, su
+ prompts for a password unless run by a user with real UID 0.
+
+ Does not change the current directory.
+ Sets `HOME' and `SHELL' from the password entry for USER, and if
+ USER is not root, sets `USER' and `LOGNAME' to USER.
+ The subshell is not a login shell.
+
+ If one or more ARGs are given, they are passed as additional
+ arguments to the subshell.
+
+ Does not handle /bin/sh or other shells specially
+ (setting argv[0] to "-su", passing -c only to certain shells, etc.).
+ I don't see the point in doing that, and it's ugly.
+
+ This program intentionally does not support a "wheel group" that
+ restricts who can su to UID 0 accounts. RMS considers that to
+ be fascist.
+
+ Options:
+ -, -l, --login Make the subshell a login shell.
+ Unset all environment variables except
+ TERM, HOME and SHELL (set as above), and USER
+ and LOGNAME (set unconditionally as above), and
+ set PATH to a default value.
+ Change to USER's home directory.
+ Prepend "-" to the shell's name.
+ -c, --commmand=COMMAND
+ Pass COMMAND to the subshell with a -c option
+ instead of starting an interactive shell.
+ -f, --fast Pass the -f option to the subshell.
+ -m, -p, --preserve-environment
+ Do not change HOME, USER, LOGNAME, SHELL.
+ Run $SHELL instead of USER's shell from /etc/passwd
+ unless not the superuser and USER's shell is
+ restricted.
+ Overridden by --login and --shell.
+ -s, --shell=shell Run SHELL instead of USER's shell from /etc/passwd
+ unless not the superuser and USER's shell is
+ restricted.
+
+ Compile-time options:
+ -DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog.
+ -DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog.
+
+ -DSYSLOG_NON_ROOT Log all su's, not just those to root (UID 0).
+ Never logs attempted su's to nonexistent accounts.
+
+ Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "system.h"
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+void log_su ();
+#else
+#ifdef SYSLOG_SUCCESS
+#undef SYSLOG_SUCCESS
+#endif
+#ifdef SYSLOG_FAILURE
+#undef SYSLOG_FAILURE
+#endif
+#ifdef SYSLOG_NON_ROOT
+#undef SYSLOG_NON_ROOT
+#endif
+#endif
+
+#ifdef _POSIX_VERSION
+#include <limits.h>
+#ifdef NGROUPS_MAX
+#undef NGROUPS_MAX
+#endif
+#define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
+#else /* not _POSIX_VERSION */
+struct passwd *getpwuid ();
+struct group *getgrgid ();
+uid_t getuid ();
+#include <sys/param.h>
+#if !defined(NGROUPS_MAX) && defined(NGROUPS)
+#define NGROUPS_MAX NGROUPS
+#endif
+#endif /* not _POSIX_VERSION */
+
+#ifdef _POSIX_SOURCE
+#define endgrent()
+#define endpwent()
+#endif
+
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+/* The default PATH for simulated logins to non-superuser accounts. */
+#define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
+
+/* The default PATH for simulated logins to superuser accounts. */
+#define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
+
+/* The shell to run if none is given in the user's passwd entry. */
+#define DEFAULT_SHELL "/bin/sh"
+
+/* The user to become if none is specified. */
+#define DEFAULT_USER "root"
+
+char *crypt ();
+char *getpass ();
+char *getusershell ();
+void endusershell ();
+void setusershell ();
+
+char *basename ();
+char *concat ();
+char *xmalloc ();
+char *xrealloc ();
+int correct_password ();
+int elements ();
+int restricted_shell ();
+void change_identity ();
+void error ();
+void modify_environment ();
+void run_shell ();
+void usage ();
+void xputenv ();
+
+extern char **environ;
+
+/* The name this program was run with. */
+char *program_name;
+
+/* If nonzero, pass the `-f' option to the subshell. */
+int fast_startup;
+
+/* If nonzero, simulate a login instead of just starting a shell. */
+int simulate_login;
+
+/* If nonzero, change some environment vars to indicate the user su'd to. */
+int change_environment;
+
+struct option longopts[] =
+{
+ {"command", 1, 0, 'c'},
+ {"fast", 0, &fast_startup, 1},
+ {"login", 0, &simulate_login, 1},
+ {"preserve-environment", 0, &change_environment, 0},
+ {"shell", 1, 0, 's'},
+ {0, 0, 0, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc;
+ char *new_user = DEFAULT_USER;
+ char *command = 0;
+ char **additional_args = 0;
+ char *shell = 0;
+ struct passwd *pw;
+
+ program_name = argv[0];
+ fast_startup = 0;
+ simulate_login = 0;
+ change_environment = 1;
+
+ while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, (int *) 0))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 0:
+ break;
+ case 'c':
+ command = optarg;
+ break;
+ case 'f':
+ fast_startup = 1;
+ break;
+ case 'l':
+ simulate_login = 1;
+ break;
+ case 'm':
+ case 'p':
+ change_environment = 0;
+ break;
+ case 's':
+ shell = optarg;
+ break;
+ default:
+ usage ();
+ }
+ }
+ if (optind < argc && !strcmp (argv[optind], "-"))
+ {
+ simulate_login = 1;
+ ++optind;
+ }
+ if (optind < argc)
+ new_user = argv[optind++];
+ if (optind < argc)
+ additional_args = argv + optind;
+
+ pw = getpwnam (new_user);
+ if (pw == 0)
+ error (1, 0, "user %s does not exist", new_user);
+ endpwent ();
+ if (!correct_password (pw))
+ {
+#ifdef SYSLOG_FAILURE
+ log_su (pw, 0);
+#endif
+ error (1, 0, "incorrect password");
+ }
+#ifdef SYSLOG_SUCCESS
+ else
+ {
+ log_su (pw, 1);
+ }
+#endif
+
+ if (pw->pw_shell == 0 || pw->pw_shell[0] == 0)
+ pw->pw_shell = DEFAULT_SHELL;
+ if (shell == 0 && change_environment == 0)
+ shell = getenv ("SHELL");
+ if (shell != 0 && getuid () && restricted_shell (pw->pw_shell))
+ {
+ /* The user being su'd to has a nonstandard shell, and so is
+ probably a uucp account or has restricted access. Don't
+ compromise the account by allowing access with a standard
+ shell. */
+ error (0, 0, "using restricted shell %s", pw->pw_shell);
+ shell = 0;
+ }
+ if (shell == 0)
+ shell = pw->pw_shell;
+ shell = strcpy (xmalloc (strlen (shell) + 1), shell);
+ modify_environment (pw, shell);
+
+ change_identity (pw);
+ if (simulate_login && chdir (pw->pw_dir))
+ error (0, errno, "warning: cannot change directory to %s", pw->pw_dir);
+ run_shell (shell, command, additional_args);
+}
+
+/* Ask the user for a password.
+ Return 1 if the user gives the correct password for entry PW,
+ 0 if not. Return 1 without asking for a password if run by UID 0
+ or if PW has an empty password. */
+
+int
+correct_password (pw)
+ struct passwd *pw;
+{
+ char *unencrypted, *encrypted, *correct;
+#ifdef HAVE_SHADOW_H
+ /* Shadow passwd stuff for SVR3 and maybe other systems. */
+ struct spwd *sp = getspnam (pw->pw_name);
+
+ endspent ();
+ if (sp)
+ correct = sp->sp_pwdp;
+ else
+#endif
+ correct = pw->pw_passwd;
+
+ if (getuid () == 0 || correct == 0 || correct[0] == '\0')
+ return 1;
+
+ unencrypted = getpass ("Password:");
+ encrypted = crypt (unencrypted, correct);
+ bzero (unencrypted, strlen (unencrypted));
+ return strcmp (encrypted, correct) == 0;
+}
+
+/* Update `environ' for the new shell based on PW, with SHELL being
+ the value for the SHELL environment variable. */
+
+void
+modify_environment (pw, shell)
+ struct passwd *pw;
+ char *shell;
+{
+ char *term;
+
+ if (simulate_login)
+ {
+ /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
+ Unset all other environment variables. */
+ term = getenv ("TERM");
+ environ = (char **) xmalloc (2 * sizeof (char *));
+ environ[0] = 0;
+ if (term)
+ xputenv (concat ("TERM", "=", term));
+ xputenv (concat ("HOME", "=", pw->pw_dir));
+ xputenv (concat ("SHELL", "=", shell));
+ xputenv (concat ("USER", "=", pw->pw_name));
+ xputenv (concat ("LOGNAME", "=", pw->pw_name));
+ xputenv (concat ("PATH", "=", pw->pw_uid
+ ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH));
+ }
+ else
+ {
+ /* Set HOME, SHELL, and if not becoming a super-user,
+ USER and LOGNAME. */
+ if (change_environment)
+ {
+ xputenv (concat ("HOME", "=", pw->pw_dir));
+ xputenv (concat ("SHELL", "=", shell));
+ if (pw->pw_uid)
+ {
+ xputenv (concat ("USER", "=", pw->pw_name));
+ xputenv (concat ("LOGNAME", "=", pw->pw_name));
+ }
+ }
+ }
+}
+
+/* Become the user and group(s) specified by PW. */
+
+void
+change_identity (pw)
+ struct passwd *pw;
+{
+#ifdef NGROUPS_MAX
+ errno = 0;
+ if (initgroups (pw->pw_name, pw->pw_gid) == -1)
+ error (1, errno, "cannot set groups");
+ endgrent ();
+#endif
+ if (setgid (pw->pw_gid))
+ error (1, errno, "cannot set group id");
+ if (setuid (pw->pw_uid))
+ error (1, errno, "cannot set user id");
+}
+
+/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
+ If COMMAND is nonzero, pass it to the shell with the -c option.
+ If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
+ arguments. */
+
+void
+run_shell (shell, command, additional_args)
+ char *shell;
+ char *command;
+ char **additional_args;
+{
+ char **args;
+ int argno = 1;
+
+ if (additional_args)
+ args = (char **) xmalloc (sizeof (char *)
+ * (10 + elements (additional_args)));
+ else
+ args = (char **) xmalloc (sizeof (char *) * 10);
+ if (simulate_login)
+ {
+ args[0] = xmalloc (strlen (shell) + 2);
+ args[0][0] = '-';
+ strcpy (args[0] + 1, basename (shell));
+ }
+ else
+ args[0] = basename (shell);
+ if (fast_startup)
+ args[argno++] = "-f";
+ if (command)
+ {
+ args[argno++] = "-c";
+ args[argno++] = command;
+ }
+ if (additional_args)
+ for (; *additional_args; ++additional_args)
+ args[argno++] = *additional_args;
+ args[argno] = 0;
+ execv (shell, args);
+ error (1, errno, "cannot run %s", shell);
+}
+
+#if defined (SYSLOG_SUCCESS) || defined (SYSLOG_FAILURE)
+/* Log the fact that someone has run su to the user given by PW;
+ if SUCCESSFUL is nonzero, they gave the correct password, etc. */
+
+void
+log_su (pw, successful)
+ struct passwd *pw;
+ int successful;
+{
+ char *new_user, *old_user, *tty;
+
+#ifndef SYSLOG_NON_ROOT
+ if (pw->pw_uid)
+ return;
+#endif
+ new_user = pw->pw_name;
+ /* The utmp entry (via getlogin) is probably the best way to identify
+ the user, especially if someone su's from a su-shell. */
+ old_user = getlogin ();
+ if (old_user == 0)
+ old_user = "";
+ tty = ttyname (2);
+ if (tty == 0)
+ tty = "";
+ /* 4.2BSD openlog doesn't have the third parameter. */
+ openlog (basename (program_name), 0
+#ifdef LOG_AUTH
+ , LOG_AUTH
+#endif
+ );
+ syslog (LOG_NOTICE,
+#ifdef SYSLOG_NON_ROOT
+ "%s(to %s) %s on %s",
+#else
+ "%s%s on %s",
+#endif
+ successful ? "" : "FAILED SU ",
+#ifdef SYSLOG_NON_ROOT
+ new_user,
+#endif
+ old_user, tty);
+ closelog ();
+}
+#endif
+
+/* Return 1 if SHELL is a restricted shell (one not returned by
+ getusershell), else 0, meaning it is a standard shell. */
+
+int
+restricted_shell (shell)
+ char *shell;
+{
+ char *line;
+
+ setusershell ();
+ while (line = getusershell ())
+ {
+ if (*line != '#' && strcmp (line, shell) == 0)
+ {
+ endusershell ();
+ return 0;
+ }
+ }
+ endusershell ();
+ return 1;
+}
+
+/* Return the number of elements in ARR, a null-terminated array. */
+
+int
+elements (arr)
+ char **arr;
+{
+ int n = 0;
+
+ for (n = 0; *arr; ++arr)
+ ++n;
+ return n;
+}
+
+/* Add VAL to the environment, checking for out of memory errors. */
+
+void
+xputenv (val)
+ char *val;
+{
+ if (putenv (val))
+ error (1, 0, "virtual memory exhausted");
+}
+
+/* Return a newly-allocated string whose contents concatenate
+ those of S1, S2, S3. */
+
+char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+ char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
+
+ strcpy (result, s1);
+ strcpy (result + len1, s2);
+ strcpy (result + len1 + len2, s3);
+ result[len1 + len2 + len3] = 0;
+
+ return result;
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-flmp] [-c command] [-s shell] [--login] [--fast]\n\
+ [--preserve-environment] [--command=command] [--shell=shell] [-]\n\
+ [user [arg...]]\n", program_name);
+ exit (1);
+}
diff --git a/src/tee.c b/src/tee.c
new file mode 100644
index 000000000..721014b5d
--- /dev/null
+++ b/src/tee.c
@@ -0,0 +1,155 @@
+/* tee - read from standard input and write to standard output and files.
+ Copyright (C) 1985, 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Mike Parker, Richard M. Stallman, and David MacKenzie */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <getopt.h>
+#include "system.h"
+
+char *xmalloc ();
+int tee ();
+void error ();
+void xwrite ();
+
+/* If nonzero, append to output files rather than truncating them. */
+int append;
+
+/* If nonzero, ignore interrupts. */
+int ignore_interrupts;
+
+/* The name that this program was run with. */
+char *program_name;
+
+struct option long_options[] =
+{
+ {"append", 0, NULL, 'a'},
+ {"ignore-interrupts", 0, NULL, 'i'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int errs;
+ int optc;
+
+ program_name = argv[0];
+ append = 0;
+ ignore_interrupts = 0;
+
+ while ((optc = getopt_long (argc, argv, "ai", long_options, (int *) 0))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 'a':
+ append = 1;
+ break;
+ case 'i':
+ ignore_interrupts = 1;
+ break;
+ default:
+ fprintf (stderr, "\
+Usage: %s [-ai] [--append] [--ignore-interrupts] [file...]\n",
+ program_name);
+ exit (1);
+ }
+ }
+
+ if (ignore_interrupts)
+#ifdef _POSIX_VERSION
+ {
+ struct sigaction sigact;
+
+ sigact.sa_handler = SIG_IGN;
+ sigemptyset (&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction (SIGINT, &sigact, NULL);
+ }
+#else /* !_POSIX_VERSION */
+ signal (SIGINT, SIG_IGN);
+#endif /* _POSIX_VERSION */
+
+ errs = tee (argc - optind, &argv[optind]);
+ if (close (0) == -1)
+ error (1, errno, "standard input");
+ if (close (1) == -1)
+ error (1, errno, "standard output");
+ exit (errs);
+}
+
+/* Copy the standard input into each of the NFILES files in FILES
+ and into the standard output.
+ Return 0 if successful, 1 if any errors occur. */
+
+int
+tee (nfiles, files)
+ int nfiles;
+ char **files;
+{
+ int *descriptors;
+ char buffer[BUFSIZ];
+ register int bytes_read, i, ret = 0, mode;
+
+ if (nfiles)
+ descriptors = (int *) xmalloc (nfiles * sizeof (int));
+ mode = O_WRONLY | O_CREAT;
+ if (append)
+ mode |= O_APPEND;
+ else
+ mode |= O_TRUNC;
+
+ for (i = 0; i < nfiles; i++)
+ {
+ descriptors[i] = open (files[i], mode, 0666);
+ if (descriptors[i] == -1)
+ {
+ error (0, errno, "%s", files[i]);
+ ret = 1;
+ }
+ }
+
+ while ((bytes_read = read (0, buffer, sizeof buffer)) > 0)
+ {
+ xwrite (1, buffer, bytes_read);
+ for (i = 0; i < nfiles; i++)
+ if (descriptors[i] != -1)
+ xwrite (descriptors[i], buffer, bytes_read);
+ }
+ if (bytes_read == -1)
+ {
+ error (0, errno, "read error");
+ ret = 1;
+ }
+
+ for (i = 0; i < nfiles; i++)
+ if (descriptors[i] != -1 && close (descriptors[i]) == -1)
+ {
+ error (0, errno, "%s", files[i]);
+ ret = 1;
+ }
+
+ if (nfiles)
+ free (descriptors);
+
+ return ret;
+}
diff --git a/src/test.c b/src/test.c
new file mode 100644
index 000000000..a78af0968
--- /dev/null
+++ b/src/test.c
@@ -0,0 +1,1054 @@
+/* GNU test program (ksb and mjb) */
+
+/* Modified to run with the GNU shell by bfox. */
+
+/* Copyright (C) 1987, 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get
+ the shell builtin version. */
+/* #define TEST_STANDALONE */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#if !defined (TEST_STANDALONE)
+# include "shell.h"
+# include "posixstat.h"
+# include "filecntl.h"
+#else /* TEST_STANDALONE */
+# include "system.h"
+# if !defined (S_IXUGO)
+# define S_IXUGO 0111
+# endif /* S_IXUGO */
+# if defined (_POSIX_VERSION)
+# include <limits.h>
+# else /* !_POSIX_VERSION */
+# include <sys/param.h>
+# endif /* _POSIX_VERSION */
+# if defined (NGROUPS_MAX) || defined (_SC_NGROUPS_MAX) || defined (NGROUPS)
+# define HAVE_GETGROUPS
+# endif /* NGROUPS_MAX || _SC_NGROUPS_MAX || NGROUPS */
+#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
+#define digit(c) ((c) >= '0' && (c) <= '9')
+#define digit_value(c) ((c) - '0')
+char *program_name;
+#endif /* TEST_STANDALONE */
+
+#if !defined (_POSIX_VERSION)
+# include <sys/file.h>
+#endif /* !_POSIX_VERSION */
+
+#include <errno.h>
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+#if !defined (STREQ)
+# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
+#endif /* !STREQ */
+
+#if !defined (member)
+# define member(c, s) (int)((c) ? index ((s), (c)) : 0)
+#endif /* !member */
+
+#if defined (_POSIX_VERSION)
+
+/* Even though SunOS 4, Ultrix 4, and 386BSD are mostly POSIX.1 compliant,
+ their getgroups system call (except in the `System V' environment, which
+ is troublesome in other ways) fills in an array of int, not gid_t
+ (which is `short' on those systems). Kludge, kludge. */
+
+#if !defined(sun) && !defined(ultrix) && !defined(__386BSD__)
+#define GETGROUPS_T gid_t
+#else
+#define GETGROUPS_T int
+#endif
+#else /* !_POSIX_VERSION */
+#define GETGROUPS_T int
+#endif /* !_POSIX_VERSION */
+
+extern gid_t getgid (), getegid ();
+extern uid_t geteuid ();
+
+#if !defined (R_OK)
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#define F_OK 0
+#endif /* R_OK */
+
+/* The following few defines control the truth and false output of each stage.
+ TRUE and FALSE are what we use to compute the final output value.
+ SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
+ TRUTH_OR is how to do logical or with TRUE and FALSE.
+ TRUTH_AND is how to do logical and with TRUE and FALSE..
+ Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
+ SHELL_BOOLEAN = (!value). */
+#define TRUE 1
+#define FALSE 0
+#define SHELL_BOOLEAN(value) (!(value))
+#define TRUTH_OR(a, b) ((a) | (b))
+#define TRUTH_AND(a, b) ((a) & (b))
+
+#if defined (TEST_STANDALONE)
+# define test_exit(val) exit (val)
+#else
+ static jmp_buf test_exit_buf;
+ static int test_error_return = 0;
+# define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
+#endif /* !TEST_STANDALONE */
+
+char *xrealloc ();
+
+static int pos; /* The offset of the current argument in ARGV. */
+static int argc; /* The number of arguments present in ARGV. */
+static char **argv; /* The argument list. */
+
+static int unop ();
+static int binop ();
+static int unary_operator ();
+static int binary_operator ();
+static int two_arguments ();
+static int three_arguments ();
+static int posixtest ();
+
+static int expr ();
+static int term ();
+static int and ();
+static int or ();
+
+static void
+test_syntax_error (format, arg)
+ char *format, *arg;
+{
+ fprintf (stderr, "%s: ", argv[0]);
+ fprintf (stderr, format, arg);
+ fflush (stderr);
+ test_exit (SHELL_BOOLEAN (FALSE));
+}
+
+/* A wrapper for stat () which disallows pathnames that are empty strings. */
+static int
+test_stat (path, finfo)
+ char *path;
+ struct stat *finfo;
+{
+ if (*path == '\0')
+ {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (stat (path, finfo));
+}
+
+/* Do the same thing access(2) does, but use the effective uid and gid,
+ and don't make the mistake of telling root that any file is
+ executable. */
+static int
+eaccess (path, mode)
+ char *path;
+ int mode;
+{
+ extern int group_member ();
+ struct stat st;
+ static int euid = -1;
+
+ if (test_stat (path, &st) < 0)
+ return (-1);
+
+ if (euid == -1)
+ euid = geteuid ();
+
+ if (euid == 0)
+ {
+ /* Root can read or write any file. */
+ if (mode != X_OK)
+ return (0);
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return (0);
+ }
+
+ if (st.st_uid == euid) /* owner */
+ mode <<= 6;
+ else if (group_member (st.st_gid))
+ mode <<= 3;
+
+ if (st.st_mode & mode)
+ return (0);
+
+ return (-1);
+}
+
+#if defined (HAVE_GETGROUPS)
+/* The number of groups that this user is a member of. */
+static int ngroups = 0;
+static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL;
+static int default_group_array_size = 0;
+#endif /* HAVE_GETGROUPS */
+
+/* Return non-zero if GID is one that we have in our groups list. */
+int
+group_member (gid)
+ gid_t gid;
+{
+#if !defined (HAVE_GETGROUPS)
+ return ((gid == getgid ()) || (gid == getegid ()));
+#else
+ register int i;
+
+ /* getgroups () returns the number of elements that it was able to
+ place into the array. We simply continue to call getgroups ()
+ until the number of elements placed into the array is smaller than
+ the physical size of the array. */
+
+ while (ngroups == default_group_array_size)
+ {
+ default_group_array_size += 64;
+
+ group_array = (GETGROUPS_T *)
+ xrealloc (group_array,
+ default_group_array_size * sizeof (GETGROUPS_T));
+
+ ngroups = getgroups (default_group_array_size, group_array);
+ }
+
+ /* In case of error, the user loses. */
+ if (ngroups < 0)
+ return (0);
+
+ /* Search through the list looking for GID. */
+ for (i = 0; i < ngroups; i++)
+ if (gid == group_array[i])
+ return (1);
+
+ return (0);
+#endif /* HAVE_GETGROUPS */
+}
+
+/* Increment our position in the argument list. Check that we're not
+ past the end of the argument list. This check is supressed if the
+ argument is FALSE. Made a macro for efficiency. */
+#if !defined (lint)
+#define advance(f) (++pos, f && (pos < argc ? 0 : beyond()))
+#endif
+
+#if !defined (advance)
+static int
+advance (f)
+ int f;
+{
+ ++pos;
+
+ if (f && pos >= argc)
+ beyond ();
+}
+#endif /* advance */
+
+#define unary_advance() (advance (1),++pos)
+
+/*
+ * beyond - call when we're beyond the end of the argument list (an
+ * error condition)
+ */
+static int
+beyond ()
+{
+ test_syntax_error ("argument expected\n", (char *)NULL);
+}
+
+/* Syntax error for when an integer argument was expected, but
+ something else was found. */
+static void
+integer_expected_error (pch)
+ char *pch;
+{
+ test_syntax_error ("integer expression expected %s\n", pch);
+}
+
+/* Return non-zero if the characters pointed to by STRING constitute a
+ valid number. Stuff the converted number into RESULT if RESULT is
+ a non-null pointer to a long. */
+static int
+isint (string, result)
+ register char *string;
+ long *result;
+{
+ int sign;
+ long value;
+
+ sign = 1;
+ value = 0;
+
+ if (result)
+ *result = 0;
+
+ /* Skip leading whitespace characters. */
+ while (whitespace (*string))
+ string++;
+
+ if (!*string)
+ return (0);
+
+ /* We allow leading `-' or `+'. */
+ if (*string == '-' || *string == '+')
+ {
+ if (!digit (string[1]))
+ return (0);
+
+ if (*string == '-')
+ sign = -1;
+
+ string++;
+ }
+
+ while (digit (*string))
+ {
+ if (result)
+ value = (value * 10) + digit_value (*string);
+ string++;
+ }
+
+ /* Skip trailing whitespace, if any. */
+ while (whitespace (*string))
+ string++;
+
+ /* Error if not at end of string. */
+ if (*string)
+ return (0);
+
+ if (result)
+ {
+ value *= sign;
+ *result = value;
+ }
+
+ return (1);
+}
+
+/* Find the modification time of FILE, and stuff it into AGE, a pointer
+ to a long. Return non-zero if successful, else zero. */
+static int
+age_of (filename, age)
+ char *filename;
+ long *age;
+{
+ struct stat finfo;
+
+ if (test_stat (filename, &finfo) < 0)
+ return (0);
+
+ if (age)
+ *age = finfo.st_mtime;
+
+ return (1);
+}
+
+/*
+ * term - parse a term and return 1 or 0 depending on whether the term
+ * evaluates to true or false, respectively.
+ *
+ * term ::=
+ * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
+ * '-'('L'|'x') filename
+ * '-t' [ int ]
+ * '-'('z'|'n') string
+ * string
+ * string ('!='|'=') string
+ * <int> '-'(eq|ne|le|lt|ge|gt) <int>
+ * file '-'(nt|ot|ef) file
+ * '(' <expr> ')'
+ * int ::=
+ * '-l' string
+ * positive and negative integers
+ */
+static int
+term ()
+{
+ int value;
+
+ if (pos >= argc)
+ beyond ();
+
+ /* Deal with leading "not"'s. */
+ if ('!' == argv[pos][0] && '\000' == argv[pos][1])
+ {
+ value = FALSE;
+ while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
+ {
+ advance (1);
+ value ^= (TRUE);
+ }
+
+ return (value ^ (term ()));
+ }
+
+ /* A paren-bracketed argument. */
+ if (argv[pos][0] == '(' && !argv[pos][1])
+ {
+ advance (1);
+ value = expr ();
+ if (argv[pos][0] != ')' || argv[pos][1])
+ test_syntax_error ("')' expected, found %s\n", argv[pos]);
+ advance (0);
+ return (TRUE == (value));
+ }
+
+ /* are there enough arguments left that this could be dyadic? */
+ if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
+ ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
+ value = binary_operator ();
+
+ /* Might be a switch type argument */
+ else if ('-' == argv[pos][0] && 0 == argv[pos][2])
+ {
+ if (unop (argv[pos][1]))
+ value = unary_operator ();
+ else
+ test_syntax_error ("%s: unary operator expected\n", argv[pos]);
+ }
+ else
+ {
+ value = (argv[pos][0] != '\0');
+ advance (0);
+ }
+
+ return (value);
+}
+
+static int
+binary_operator ()
+{
+ register int op;
+ struct stat stat_buf, stat_spare;
+ long int l, r, value;
+ /* Are the left and right integer expressions of the form '-l string'? */
+ int l_is_l, r_is_l;
+
+ if (strcmp (argv[pos], "-l") == 0)
+ {
+ l_is_l = 1;
+ op = pos + 2;
+
+ /* Make sure that OP is still a valid binary operator. */
+ if ((op >= argc - 1) || (binop (argv[op]) == 0))
+ test_syntax_error ("%s: binary operator expected\n", argv[op]);
+
+ advance (0);
+ }
+ else
+ {
+ l_is_l = 0;
+ op = pos + 1;
+ }
+
+ if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
+ {
+ r_is_l = 1;
+ advance (0);
+ }
+ else
+ r_is_l = 0;
+
+ if (argv[op][0] == '-')
+ {
+ /* check for eq, nt, and stuff */
+ switch (argv[op][1])
+ {
+ default:
+ break;
+
+ case 'l':
+ if (argv[op][2] == 't' && !argv[op][3])
+ {
+ /* lt */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -lt");
+ }
+
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -lt");
+ }
+ pos += 3;
+ return (TRUE == (l < r));
+ }
+
+ if (argv[op][2] == 'e' && !argv[op][3])
+ {
+ /* le */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -le");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -le");
+ }
+ pos += 3;
+ return (TRUE == (l <= r));
+ }
+ break;
+
+ case 'g':
+ if (argv[op][2] == 't' && !argv[op][3])
+ {
+ /* gt integer greater than */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -gt");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -gt");
+ }
+ pos += 3;
+ return (TRUE == (l > r));
+ }
+
+ if (argv[op][2] == 'e' && !argv[op][3])
+ {
+ /* ge - integer greater than or equal to */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -ge");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -ge");
+ }
+ pos += 3;
+ return (TRUE == (l >= r));
+ }
+ break;
+
+ case 'n':
+ if (argv[op][2] == 't' && !argv[op][3])
+ {
+ /* nt - newer than */
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
+ if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
+ return (TRUE == (l > r));
+ else
+ return (FALSE);
+ }
+
+ if (argv[op][2] == 'e' && !argv[op][3])
+ {
+ /* ne - integer not equal */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -ne");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -ne");
+ }
+ pos += 3;
+ return (TRUE == (l != r));
+ }
+ break;
+
+ case 'e':
+ if (argv[op][2] == 'q' && !argv[op][3])
+ {
+ /* eq - integer equal */
+ if (l_is_l)
+ l = strlen (argv[op - 1]);
+ else
+ {
+ if (!isint (argv[op - 1], &l))
+ integer_expected_error ("before -eq");
+ }
+ if (r_is_l)
+ r = strlen (argv[op + 2]);
+ else
+ {
+ if (!isint (argv[op + 1], &r))
+ integer_expected_error ("after -eq");
+ }
+ pos += 3;
+ return (TRUE == (l == r));
+ }
+
+ if (argv[op][2] == 'f' && !argv[op][3])
+ {
+ /* ef - hard link? */
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error ("-ef does not accept -l\n", (char *)NULL);
+ if (stat (argv[op - 1], &stat_buf) < 0)
+ return (FALSE);
+ if (stat (argv[op + 1], &stat_spare) < 0)
+ return (FALSE);
+ return (TRUE ==
+ (stat_buf.st_dev == stat_spare.st_dev &&
+ stat_buf.st_ino == stat_spare.st_ino));
+ }
+ break;
+
+ case 'o':
+ if ('t' == argv[op][2] && '\000' == argv[op][3])
+ {
+ /* ot - older than */
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
+ if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
+ return (TRUE == (l < r));
+ return (FALSE);
+ }
+ break;
+ }
+ test_syntax_error ("unknown binary operator", argv[op]);
+ }
+
+ if (argv[op][0] == '=' && !argv[op][1])
+ {
+ value = (strcmp (argv[pos], argv[pos + 2]) == 0);
+ pos += 3;
+ return (TRUE == value);
+ }
+
+ if (strcmp (argv[op], "!=") == 0)
+ {
+ value = (strcmp (argv[pos], argv[pos + 2]) != 0);
+ pos += 3;
+ return (TRUE == value);
+ }
+}
+
+static int
+unary_operator ()
+{
+ long r, value;
+ struct stat stat_buf;
+
+ switch (argv[pos][1])
+ {
+ default:
+ return (FALSE);
+
+ /* All of the following unary operators use unary_advance (), which
+ checks to make sure that there is an argument, and then advances
+ pos right past it. This means that pos - 1 is the location of the
+ argument. */
+
+ case 'a': /* file exists in the file system? */
+ case 'e':
+ unary_advance ();
+ value = -1 != test_stat (argv[pos - 1], &stat_buf);
+ return (TRUE == value);
+
+ case 'r': /* file is readable? */
+ unary_advance ();
+ value = -1 != eaccess (argv[pos - 1], R_OK);
+ return (TRUE == value);
+
+ case 'w': /* File is writeable? */
+ unary_advance ();
+ value = -1 != eaccess (argv[pos - 1], W_OK);
+ return (TRUE == value);
+
+ case 'x': /* File is executable? */
+ unary_advance ();
+ value = -1 != eaccess (argv[pos - 1], X_OK);
+ return (TRUE == value);
+
+ case 'O': /* File is owned by you? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (geteuid () == stat_buf.st_uid));
+
+ case 'G': /* File is owned by your group? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (getegid () == stat_buf.st_gid));
+
+ case 'f': /* File is a file? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ /* Under POSIX, -f is true if the given file exists
+ and is a regular file. */
+ return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
+ (0 == (stat_buf.st_mode & S_IFMT))));
+
+ case 'd': /* File is a directory? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISDIR (stat_buf.st_mode)));
+
+ case 's': /* File has something in it? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (stat_buf.st_size > (off_t) 0));
+
+ case 'S': /* File is a socket? */
+#if !defined (S_ISSOCK)
+ return (FALSE);
+#else
+ unary_advance ();
+
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
+#endif /* S_ISSOCK */
+
+ case 'c': /* File is character special? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISCHR (stat_buf.st_mode)));
+
+ case 'b': /* File is block special? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (S_ISBLK (stat_buf.st_mode)));
+
+ case 'p': /* File is a named pipe? */
+ unary_advance ();
+#ifndef S_ISFIFO
+ return (FALSE);
+#else
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+ return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
+#endif /* S_ISFIFO */
+
+ case 'L': /* Same as -h */
+ /*FALLTHROUGH*/
+
+ case 'h': /* File is a symbolic link? */
+ unary_advance ();
+#ifndef S_ISLNK
+ return (FALSE);
+#else
+ /* An empty filename is not a valid pathname. */
+ if ((argv[pos - 1][0] == '\0') ||
+ (lstat (argv[pos - 1], &stat_buf) < 0))
+ return (FALSE);
+
+ return (TRUE == (S_ISLNK (stat_buf.st_mode)));
+#endif /* S_IFLNK */
+
+ case 'u': /* File is setuid? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
+
+ case 'g': /* File is setgid? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+
+ return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
+
+ case 'k': /* File has sticky bit set? */
+ unary_advance ();
+ if (test_stat (argv[pos - 1], &stat_buf) < 0)
+ return (FALSE);
+#if !defined (S_ISVTX)
+ /* This is not Posix, and is not defined on some Posix systems. */
+ return (FALSE);
+#else
+ return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
+#endif
+
+ case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
+ advance (0);
+ if (pos < argc && isint (argv[pos], &r))
+ {
+ advance (0);
+ return (TRUE == (isatty ((int) r)));
+ }
+ return (TRUE == (isatty (1)));
+
+ case 'n': /* True if arg has some length. */
+ unary_advance ();
+ return (TRUE == (argv[pos - 1][0] != 0));
+
+ case 'z': /* True if arg has no length. */
+ unary_advance ();
+ return (TRUE == (argv[pos - 1][0] == '\0'));
+ }
+}
+
+/*
+ * and:
+ * term
+ * term '-a' and
+ */
+static int
+and ()
+{
+ int value;
+
+ value = term ();
+ while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
+ {
+ advance (0);
+ value = TRUTH_AND (value, and ());
+ }
+ return (TRUE == value);
+}
+
+/*
+ * or:
+ * and
+ * and '-o' or
+ */
+static int
+or ()
+{
+ int value;
+
+ value = and ();
+
+ while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
+ {
+ advance (0);
+ value = TRUTH_OR (value, or ());
+ }
+
+ return (TRUE == value);
+}
+
+/*
+ * expr:
+ * or
+ */
+static int
+expr ()
+{
+ if (pos >= argc)
+ beyond ();
+
+ return (FALSE ^ (or ())); /* Same with this. */
+}
+
+/* Return TRUE if S is one of the test command's binary operators. */
+static int
+binop (s)
+ char *s;
+{
+ return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
+ (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
+ (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
+ (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
+}
+
+/* Return non-zero if OP is one of the test command's unary operators. */
+static int
+unop (op)
+ int op;
+{
+ return (member (op, "abcdefgkLhprsStuwxOGnz"));
+}
+
+static int
+two_arguments ()
+{
+ int value;
+
+ if (STREQ (argv[pos], "!"))
+ value = strlen (argv[pos+1]) == 0;
+ else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
+ {
+ if (unop (argv[pos][1]))
+ value = unary_operator ();
+ else
+ test_syntax_error ("%s: unary operator expected\n", argv[pos]);
+ }
+ else
+ beyond ();
+ return (value);
+}
+
+static int
+three_arguments ()
+{
+ int value;
+
+ if (STREQ (argv[pos], "!"))
+ {
+ advance (1);
+ value = !two_arguments ();
+ }
+ else if (binop (argv[pos+1]))
+ {
+ value = binary_operator ();
+ pos = argc;
+ }
+ else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
+ (argv[pos][0] == '('))
+ value = expr ();
+ else
+ test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
+ return (value);
+}
+
+/* This is an implementation of a Posix.2 proposal by David Korn. */
+static int
+posixtest ()
+{
+ int value;
+
+ switch (argc - 1) /* one extra passed in */
+ {
+ case 0:
+ value = FALSE;
+ pos = argc;
+ break;
+
+ case 1:
+ value = strlen (argv[1]) != 0;
+ pos = argc;
+ break;
+
+ case 2:
+ value = two_arguments ();
+ pos = argc;
+ break;
+
+ case 3:
+ value = three_arguments ();
+ break;
+
+ case 4:
+ if (STREQ (argv[pos], "!"))
+ {
+ advance (1);
+ value = !three_arguments ();
+ break;
+ }
+ /* FALLTHROUGH */
+ case 5:
+ default:
+ value = expr ();
+ }
+
+ return (value);
+}
+
+/*
+ * [:
+ * '[' expr ']'
+ * test:
+ * test expr
+ */
+int
+#if defined (TEST_STANDALONE)
+main (margc, margv)
+#else
+test_command (margc, margv)
+#endif /* !TEST_STANDALONE */
+ int margc;
+ char **margv;
+{
+ auto int value;
+ int expr ();
+
+#if !defined (TEST_STANDALONE)
+ int code;
+
+ code = setjmp (test_exit_buf);
+
+ if (code)
+ return (test_error_return);
+#else /* TEST_STANDALONE */
+ program_name = margv[0];
+#endif /* TEST_STANDALONE */
+
+ argv = margv;
+
+ if (margv[0] && strcmp (margv[0], "[") == 0)
+ {
+ --margc;
+
+ if (margc < 2)
+ test_exit (SHELL_BOOLEAN (FALSE));
+
+ if (margv[margc] && strcmp (margv[margc], "]") != 0)
+ test_syntax_error ("missing `]'\n", (char *)NULL);
+ }
+
+ argc = margc;
+ pos = 1;
+
+ if (pos >= argc)
+ test_exit (SHELL_BOOLEAN (FALSE));
+
+ value = posixtest ();
+
+ if (pos != argc)
+ test_syntax_error ("too many arguments\n", (char *)NULL);
+
+ test_exit (SHELL_BOOLEAN (value));
+}
diff --git a/src/tty.c b/src/tty.c
new file mode 100644
index 000000000..056c7da8d
--- /dev/null
+++ b/src/tty.c
@@ -0,0 +1,88 @@
+/* tty -- print the path of the terminal connected to standard input
+ Copyright (C) 1990, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Displays "not a tty" if stdin is not a terminal.
+ Displays nothing if -s option is given.
+ Exit status 0 if stdin is a tty, 1 if not, 2 if usage error.
+
+ Written by David MacKenzie (djm@ai.mit.edu). */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include "system.h"
+
+void usage ();
+
+/* The name under which this program was run. */
+char *program_name;
+
+/* If nonzero, return an exit status but produce no output. */
+int silent;
+
+struct option longopts[] =
+{
+ {"silent", 0, NULL, 's'},
+ {"quiet", 0, NULL, 's'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ char *tty;
+ int optc;
+
+ program_name = argv[0];
+ silent = 0;
+
+ while ((optc = getopt_long (argc, argv, "s", longopts, (int *) 0)) != EOF)
+ {
+ switch (optc)
+ {
+ case 's':
+ silent = 1;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind != argc)
+ usage ();
+
+ tty = ttyname (0);
+ if (!silent)
+ {
+ if (tty)
+ puts (tty);
+ else
+ puts ("not a tty");
+ }
+
+ exit (tty == NULL);
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-s] [--silent] [--quiet]\n", program_name);
+ exit (2);
+}
diff --git a/src/uname.c b/src/uname.c
new file mode 100644
index 000000000..77eaae677
--- /dev/null
+++ b/src/uname.c
@@ -0,0 +1,155 @@
+/* uname -- print system information
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Option Example
+
+ -s, --sysname SunOS
+ -n, --nodename rocky8
+ -r, --release 4.0
+ -v, --version
+ -m, --machine sun
+ -a, --all SunOS rocky8 4.0 sun
+
+ The default behavior is equivalent to `-s'.
+
+ David MacKenzie <djm@ai.mit.edu> */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <getopt.h>
+#include "system.h"
+
+void error ();
+void print_element ();
+void usage ();
+
+/* Values that are bitwise or'd into `toprint'. */
+/* Operating system name. */
+#define PRINT_SYSNAME 1
+
+/* Node name on a communications network. */
+#define PRINT_NODENAME 2
+
+/* Operating system release. */
+#define PRINT_RELEASE 4
+
+/* Operating system version. */
+#define PRINT_VERSION 8
+
+/* Machine hardware name. */
+#define PRINT_MACHINE 16
+
+/* Mask indicating which elements of the name to print. */
+unsigned char toprint;
+
+/* The name this program was run with, for error messages. */
+char *program_name;
+
+struct option long_options[] =
+{
+ {"sysname", 0, NULL, 's'},
+ {"nodename", 0, NULL, 'n'},
+ {"release", 0, NULL, 'r'},
+ {"version", 0, NULL, 'v'},
+ {"machine", 0, NULL, 'm'},
+ {"all", 0, NULL, 'a'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ struct utsname name;
+ int c;
+
+ program_name = argv[0];
+ toprint = 0;
+
+ while ((c = getopt_long (argc, argv, "snrvma", long_options, (int *) 0))
+ != EOF)
+ {
+ switch (c)
+ {
+ case 's':
+ toprint |= PRINT_SYSNAME;
+ break;
+ case 'n':
+ toprint |= PRINT_NODENAME;
+ break;
+ case 'r':
+ toprint |= PRINT_RELEASE;
+ break;
+ case 'v':
+ toprint |= PRINT_VERSION;
+ break;
+ case 'm':
+ toprint |= PRINT_MACHINE;
+ break;
+ case 'a':
+ toprint = PRINT_SYSNAME | PRINT_NODENAME | PRINT_RELEASE |
+ PRINT_VERSION | PRINT_MACHINE;
+ break;
+ default:
+ usage ();
+ }
+ }
+
+ if (optind != argc)
+ usage ();
+
+ if (toprint == 0)
+ toprint = PRINT_SYSNAME;
+
+ if (uname (&name) == -1)
+ error (1, errno, "cannot get system name");
+
+ print_element (PRINT_SYSNAME, name.sysname);
+ print_element (PRINT_NODENAME, name.nodename);
+ print_element (PRINT_RELEASE, name.release);
+ print_element (PRINT_VERSION, name.version);
+ print_element (PRINT_MACHINE, name.machine);
+
+ exit (0);
+}
+
+/* If the name element set in MASK is selected for printing in `toprint',
+ print ELEMENT; then print a space unless it is the last element to
+ be printed, in which case print a newline. */
+
+void
+print_element (mask, element)
+ unsigned char mask;
+ char *element;
+{
+ if (toprint & mask)
+ {
+ toprint &= ~mask;
+ printf ("%s%c", element, toprint ? ' ' : '\n');
+ }
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-snrvma] [--sysname] [--nodename] [--release] [--version]\n\
+ [--machine] [--all]\n", program_name);
+ exit (1);
+}
diff --git a/src/who.c b/src/who.c
new file mode 100644
index 000000000..d5f40dc55
--- /dev/null
+++ b/src/who.c
@@ -0,0 +1,434 @@
+/* GNU's who.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by jla; revised by djm */
+
+/* Output format:
+ name [state] line time [idle] host
+ state: -T
+ name, line, time: not -q
+ idle: -u
+
+ Options:
+ -m Same as 'who am i', for POSIX.
+ -q Only user names and # logged on; overrides all other options.
+ -s Name, line, time (default).
+ -i, -u Idle hours and minutes; '.' means active in last minute;
+ 'old' means idle for >24 hours.
+ -H Print column headings at top.
+ -w, -T -s plus mesg (+ or -, or ? if bad line). */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <utmp.h>
+#include <time.h>
+#include <getopt.h>
+#ifndef _POSIX_SOURCE
+#include <sys/param.h>
+#endif
+#include "system.h"
+
+#ifndef UTMP_FILE
+#ifdef _PATH_UTMP /* 4.4BSD. */
+#define UTMP_FILE _PATH_UTMP
+#else /* !_PATH_UTMP */
+#define UTMP_FILE "/etc/utmp"
+#endif /* !_PATH_UTMP */
+#endif /* !UTMP_FILE */
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define MESG_BIT 020 /* Group write bit. */
+
+char *ttyname ();
+
+char *idle_string ();
+char *xmalloc ();
+struct utmp *search_entries ();
+void error ();
+void list_entries ();
+void print_entry ();
+void print_heading ();
+void scan_entries ();
+void usage ();
+void who ();
+void who_am_i ();
+
+/* The name this program was run with. */
+char *program_name;
+
+/* If nonzero, display only a list of usernames and count of
+ the users logged on.
+ Ignored for `who am i'. */
+int short_list;
+
+/* If nonzero, display the hours:minutes since each user has touched
+ the keyboard, or "." if within the last minute, or "old" if
+ not within the last day. */
+int include_idle;
+
+/* If nonzero, display a line at the top describing each field. */
+int include_heading;
+
+/* If nonzero, display a `+' for each user if mesg y, a `-' if mesg n,
+ or a `?' if their tty cannot be statted. */
+int include_mesg;
+
+struct option longopts[] =
+{
+ {"count", 0, NULL, 'q'},
+ {"idle", 0, NULL, 'u'},
+ {"heading", 0, NULL, 'H'},
+ {"message", 0, NULL, 'T'},
+ {"mesg", 0, NULL, 'T'},
+ {"writable", 0, NULL, 'T'},
+ {NULL, 0, NULL, 0}
+};
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int optc, longind;
+ int my_line_only = 0;
+
+ program_name = argv[0];
+
+ while ((optc = getopt_long (argc, argv, "imqsuwHT", longopts, &longind))
+ != EOF)
+ {
+ switch (optc)
+ {
+ case 'm':
+ my_line_only = 1;
+ break;
+
+ case 'q':
+ short_list = 1;
+ break;
+
+ case 's':
+ break;
+
+ case 'i':
+ case 'u':
+ include_idle = 1;
+ break;
+
+ case 'H':
+ include_heading = 1;
+ break;
+
+ case 'w':
+ case 'T':
+ include_mesg = 1;
+ break;
+
+ default:
+ usage ();
+ }
+ }
+
+ if (chdir ("/dev"))
+ error (1, errno, "cannot change directory to /dev");
+
+ switch (argc - optind)
+ {
+ case 0: /* who */
+ if (my_line_only)
+ who_am_i (UTMP_FILE);
+ else
+ who (UTMP_FILE);
+ break;
+
+ case 1: /* who <utmp file> */
+ if (my_line_only)
+ who_am_i (argv[optind]);
+ else
+ who (argv[optind]);
+ break;
+
+ case 2: /* who <blurf> <glop> */
+ who_am_i (UTMP_FILE);
+ break;
+
+ default: /* lose */
+ usage ();
+ }
+
+ exit (0);
+}
+
+static struct utmp *utmp_contents;
+
+/* Display a list of who is on the system, according to utmp file FILENAME. */
+
+void
+who (filename)
+ char *filename;
+{
+ int users;
+
+ users = read_utmp (filename);
+ if (short_list)
+ list_entries (users);
+ else
+ scan_entries (users);
+}
+
+/* Read the utmp file FILENAME into UTMP_CONTENTS and return the
+ number of entries it contains. */
+
+int
+read_utmp (filename)
+ char *filename;
+{
+ register int desc;
+ struct stat file_stats;
+
+ desc = open (filename, O_RDONLY, 0);
+ if (desc < 0)
+ error (1, errno, "%s", filename);
+
+ fstat (desc, &file_stats);
+ if (file_stats.st_size > 0)
+ utmp_contents = (struct utmp *) xmalloc ((unsigned) file_stats.st_size);
+ else
+ {
+ close (desc);
+ return 0;
+ }
+
+ /* Use < instead of != in case the utmp just grew. */
+ if (read (desc, utmp_contents, file_stats.st_size) < file_stats.st_size)
+ error (1, errno, "%s", filename);
+
+ if (close (desc))
+ error (1, errno, "%s", filename);
+
+ return file_stats.st_size / sizeof (struct utmp);
+}
+
+/* Display a line of information about entry THIS. */
+
+void
+print_entry (this)
+ struct utmp *this;
+{
+ struct stat stats;
+ time_t last_change;
+ char mesg;
+ char line[sizeof (this->ut_line) + 1];
+
+ strncpy (line, this->ut_line, sizeof (this->ut_line));
+ line[sizeof (this->ut_line)] = 0;
+ if (stat (line, &stats) == 0)
+ {
+ mesg = (stats.st_mode & MESG_BIT) ? '+' : '-';
+ last_change = stats.st_atime;
+ }
+ else
+ {
+ mesg = '?';
+ last_change = 0;
+ }
+
+ printf ("%-*.*s",
+ sizeof (this->ut_name), sizeof (this->ut_name),
+ this->ut_name);
+ if (include_mesg)
+ printf (" %c ", mesg);
+ printf (" %-*.*s",
+ sizeof (this->ut_line), sizeof (this->ut_line),
+ this->ut_line);
+ printf (" %-12.12s", ctime (&this->ut_time) + 4);
+ if (include_idle)
+ {
+ if (last_change)
+ printf (" %s", idle_string (last_change));
+ else
+ printf (" . ");
+ }
+#ifdef HAVE_UT_HOST
+ if (this->ut_host[0])
+ printf (" (%-.*s)", sizeof (this->ut_host), this->ut_host);
+#endif
+
+ putchar ('\n');
+}
+
+/* Print the username of each valid entry and the number of valid entries
+ in `utmp_contents', which should have N elements. */
+
+void
+list_entries (n)
+ int n;
+{
+ register struct utmp *this = utmp_contents;
+ register int entries = 0;
+
+ while (n--)
+ {
+ if (this->ut_name[0]
+#ifdef USER_PROCESS
+ && this->ut_type == USER_PROCESS
+#endif
+ )
+ {
+ printf ("%s ", this->ut_name);
+ entries++;
+ }
+ this++;
+ }
+ printf ("\n# users=%u\n", entries);
+}
+
+void
+print_heading ()
+{
+ struct utmp *ut;
+
+ printf ("%-*s ", sizeof (ut->ut_name), "USER");
+ if (include_mesg)
+ printf ("MESG ");
+ printf ("%-*s ", sizeof (ut->ut_line), "LINE");
+ printf ("LOGIN-TIME ");
+ if (include_idle)
+ printf ("IDLE ");
+ printf ("FROM\n");
+}
+
+/* Display `utmp_contents', which should have N entries. */
+
+void
+scan_entries (n)
+ int n;
+{
+ register struct utmp *this = utmp_contents;
+
+ if (include_heading)
+ print_heading ();
+
+ while (n--)
+ {
+ if (this->ut_name[0]
+#ifdef USER_PROCESS
+ && this->ut_type == USER_PROCESS
+#endif
+ )
+ print_entry (this);
+ this++;
+ }
+}
+
+/* Search `utmp_contents', which should have N entries, for
+ an entry with a `ut_line' field identical to LINE.
+ Return the first matching entry found, or NULL if there
+ is no matching entry. */
+
+struct utmp *
+search_entries (n, line)
+ int n;
+ char *line;
+{
+ register struct utmp *this = utmp_contents;
+
+ while (n--)
+ {
+ if (this->ut_name[0]
+#ifdef USER_PROCESS
+ && this->ut_type == USER_PROCESS
+#endif
+ && !strncmp (line, this->ut_line, sizeof (this->ut_line)))
+ return this;
+ this++;
+ }
+ return NULL;
+}
+
+/* Display the entry in utmp file FILENAME for this tty on standard input,
+ or nothing if there is no entry for it. */
+
+void
+who_am_i (filename)
+ char *filename;
+{
+ register struct utmp *utmp_entry;
+ char hostname[MAXHOSTNAMELEN + 1];
+ char *tty;
+
+ if (gethostname (hostname, MAXHOSTNAMELEN + 1))
+ *hostname = 0;
+
+ if (include_heading)
+ {
+ printf ("%*s ", strlen (hostname), " ");
+ print_heading ();
+ }
+
+ tty = ttyname (0);
+ if (tty == NULL)
+ return;
+ tty += 5; /* Remove "/dev/". */
+
+ utmp_entry = search_entries (read_utmp (filename), tty);
+ if (utmp_entry == NULL)
+ return;
+
+ printf ("%s!", hostname);
+ print_entry (utmp_entry);
+}
+
+/* Return a string representing the time between WHEN and the time
+ that this function is first run. */
+
+char *
+idle_string (when)
+ time_t when;
+{
+ static time_t now = 0;
+ static char idle[10];
+ time_t seconds_idle;
+
+ if (now == 0)
+ time (&now);
+
+ seconds_idle = now - when;
+ if (seconds_idle < 60) /* One minute. */
+ return " . ";
+ if (seconds_idle < (24 * 60 * 60)) /* One day. */
+ {
+ sprintf (idle, "%02d:%02d",
+ seconds_idle / (60 * 60),
+ (seconds_idle % (60 * 60)) / 60);
+ return idle;
+ }
+ return " old ";
+}
+
+void
+usage ()
+{
+ fprintf (stderr, "\
+Usage: %s [-imqsuwHT] [--count] [--idle] [--heading] [--message] [--mesg]\n\
+ [--writable] [file] [am i]\n",
+ program_name);
+ exit (1);
+}
diff --git a/src/whoami.c b/src/whoami.c
new file mode 100644
index 000000000..4a12406f1
--- /dev/null
+++ b/src/whoami.c
@@ -0,0 +1,49 @@
+/* whoami -- print effective userid
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Equivalent to `id -un'. */
+/* Written by Richard Mlynarik. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "system.h"
+
+void
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct passwd *pw;
+ register uid_t uid;
+
+ if (argc != 1)
+ {
+ fprintf (stderr, "Usage: %s\n", argv[0]);
+ exit (1);
+ }
+
+ uid = geteuid ();
+ pw = getpwuid (uid);
+ if (pw)
+ {
+ puts (pw->pw_name);
+ exit (0);
+ }
+ fprintf (stderr,"%s: cannot find username for UID %u\n", argv[0], uid);
+ exit (1);
+}
diff --git a/src/yes.c b/src/yes.c
new file mode 100644
index 000000000..71eabb020
--- /dev/null
+++ b/src/yes.c
@@ -0,0 +1,39 @@
+/* yes - output a string repeatedly until killed
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* David MacKenzie <djm@ai.mit.edu> */
+
+#include <stdio.h>
+
+void
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+
+ if (argc == 1)
+ while (1)
+ puts ("y");
+
+ while (1)
+ for (i = 1; i < argc; i++)
+ {
+ fputs (argv[i], stdout);
+ putchar (i == argc - 1 ? '\n' : ' ');
+ }
+}