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