cechew@bruce.OZ (Earl Chew) (10/09/89)
#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 2 (of 2)." # Contents: ls.c # Wrapped by cechew@bruce on Mon Oct 9 19:57:51 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'ls.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ls.c'\" else echo shar: Extracting \"'ls.c'\" \(36040 characters\) sed "s/^X//" >'ls.c' <<'END_OF_FILE' X/* X * List Directory Entries X * X * (C) Copyright C E Chew X * X * Feel free to copy and use this software provided: X * X * 1. you do not pretend that you wrote it X * 2. you leave this copyright notice intact. X * X * This is an implementation of a BSD style ls(1) for Minix. This X * implementation is faster than the original ls(1) for Minix. There X * are no restrictions with regard to the size of the directory file X * since memory is allocated dynamically. X * X * Patchlevel 2.0 X * X * Edit History: X * 21-Sep-1989 Changed names to _BSD and _MINIX. Use #ifdef for X * portability. X * 19-Sep-1989 Sizes in kb rather than `blocks'. X * 14-Sep-1989 No longer need to define MINIX. Get rid of itoa(). X * Pyramid BSD coercions. Symbolic links and sockets. X * 27-Aug-1989 Added declaration of errno for old errno.h and X * char *malloc() for old include files. Added X * support for named FIFO's. Changed user and group X * name format to %-6.6s so that long names won't X * muck up columns. Later will have to add X * symbolic link support for -l and -L. X * 16-May-1989 Option -n shouldn't look in /etc/passwd. Use new X * strerror in string.h, add prototype for itoa(). X * 30-Apr-1989 Changed stat failure message to not found. Include X * dirent.h after stdio.h so that NULL is defined. X * 22-Mar-1989 Incompatible options processing, long sizes instead X * of int and removed TURBOC conditional. X * 02-Mar-1989 Sort command line arguments. X * 22-Feb-1989 Fixed up bug regarding device number printing. X * 10-Sep-1988 Cleaned up for lint. X * 05-Sep-1988 Tidied up for network release. X */ X X#ifndef _SYSV X# ifndef _BSD X# ifndef _MINIX X# define _MINIX X# endif X# endif X#endif X X#include <sys/types.h> X#include <sys/stat.h> X#include <errno.h> X#include <stdio.h> X#ifdef _BSD X# include <sys/dir.h> X# define dirent direct X# include <sys/param.h> X# define STD_BLK DEV_BSIZE X# define PATH_MAX MAXPATHLEN X# include <strings.h> X# define strchr index X# define strerror(n) ((n) < 1 || (n) > sys_nerr \ X ? "Unknown" : sys_errlist[n]) X# include <sys/time.h> X#else X# include <limits.h> X# include <dirent.h> X# include <string.h> X# include <time.h> X#endif X#include <ctype.h> X#include <grp.h> X#include <pwd.h> X X#ifdef _MINIX X# include <minix/const.h> X# include <minix/type.h> X# include <fs/const.h> X# include <fs/type.h> X# undef printf X# undef T X# undef D X# undef P X#endif X X#ifndef SUPER_USER X# define SUPER_USER (0) X#endif X X#ifdef __STDC__ X# define T(x,y) x X# define D(x) X# define P(x) x X#else X# define T(x,y) y X# define D(x) x; X# define P(x) () X#endif X X#define DEFAULTDIR "." /* default for ls without args */ X#define PARENT ".." /* cd back to parent */ X#define ENVNAME "LSOPTS" /* ls options */ X#define COLUMNS 80 /* columns on terminal */ X#define INTERSPACE 2 /* spacing between columns */ X#define INODEWIDTH 5 /* width of field for inode */ X#define BLOCKWIDTH 4 /* width of field for blocks */ X X#define HALFYEAR ((long) 60*60*24*365/2) /* half year in seconds */ X#define BASEYEAR 1900 /* struct tm.tm_year base */ X X#define NIL (0) /* nil pointer */ X X/* X * Flags are maintained in a bitmap. Access should be fast X * because of the inline nature. X */ X#define BPC 8 /* bits per character */ X#define BITTEST(m,b) (m[b/BPC] & (1<<(b%BPC))) X#define BITSET(m,b) (m[b/BPC] |= (1<<(b%BPC))) X#define BITCLEAR(m,b) (m[b/BPC] &= ~(1<<(b%BPC))) X#define BITCHARS(b) ((b+BPC-1)/BPC) X X#define TEST(b) BITTEST(flags,b) X#define SET(b) BITSET(flags,b) X#define CLEAR(b) BITCLEAR(flags,b) X X/* X * These macros permit the shortens the short circuiting of X * complicated boolean expressions. This is particularly X * useful when working with the flags since these are X * read-only. X */ X#define BOOL(b) static char b = 0 X#define EVAL(b,a) ((b ? b : (b = (a)+1)) > 1) X X/* X * A descriptor is kept for each file in the directory. The X * descriptors are linked together in a linked list. X */ Xstruct direntry { X struct direntry *next; /* link list */ X char *name; /* name of entry */ X char *suffix; /* entry suffix */ X int length; /* length of name and suffix */ X struct direntry *parent; /* pointer to parent */ X struct stat *status; /* pointer to status */ X}; X Xtypedef struct direntry DIRENTRY; /* entry */ Xtypedef DIRENTRY *DIRLIST[2]; /* list of entries */ X X#define EMPTY_dl(d) (d[0]=d[1]=(DIRENTRY *)NIL) X#define APPEND_dl(d,p) if (d[0]) d[1]->next=p,d[1]=p;else d[0]=d[1]=p X#define TIE_dl(d,p) APPEND_dl(d,p); while (d[1]->next) d[1]=d[1]->next X#define HEAD_dl(d) d[0] X X#define LINKOFFSET ((char *)&((DIRENTRY *)NIL)->next - \ X (char *)((DIRENTRY *)NIL)) X X/* X * External Functions. X */ X Xtime_t time P((time_t *)); /* get system time */ Xchar *getenv P((char *)); /* get environment variable */ Xint stat P((char *, struct stat *)); /* get status of file */ Xstruct passwd *getpwuid P((uid_t)); /* get password from uid */ Xstruct group *getgrgid P((gid_t)); /* get group from gid */ Xint getuid P((void)); /* get uid of this process */ Xint getopt P((int, char **, char *)); /* parse the options */ Xvoid free P((void *)); /* free memory */ Xint isatty P((int)); /* stdout is a terminal */ Xvoid exit P((int)); /* terminate */ Xchar *malloc P((unsigned)); /* memory allocator */ Xstruct tm *localtime P((time_t *)); /* convert to local time */ XDIR *opendir P((char *)); /* open directory */ Xstruct dirent *readdir P((DIR *)); /* read directory */ X X#ifdef _BSD Xvoid closedir P((DIR *)); /* close directory */ X#else Xint closedir P((DIR *)); /* close directory */ X#endif X X#ifdef S_IFLNK X int lstat P((char *, struct stat *)); /* stat() with opaque links */ X int readlink P((char *, char *, int));/* read symbolic link */ X# define STAT lstat X#else X# define STAT stat X#endif X X/* X * External Variables. X */ X Xextern int optind; /* next argument */ Xextern int errno; /* error code */ Xextern int sys_nerr; /* errors */ Xextern char *sys_errlist[]; /* errors by name */ X X/* X * Forward declarations. X */ X Xvoid acrosspage P((DIRENTRY *, DIRLIST, int)); /* list across the page */ Xvoid downpage P((DIRENTRY *, DIRLIST, int, int)); /* list down the page */ Xvoid streamoutput P((DIRENTRY *, DIRLIST)); /* stream output */ Xvoid dolsopts P((void)); /* do environment variable */ Xvoid parse P((int, char **)); /* parse argument list */ Xvoid setflag P((int)); /* set flag */ Xvoid checkflag P((int, char *)); /* check flag for incompatibilies */ Xvoid init P((void)); /* initialise globals */ Xint stringlength P((char *)); /* length of string with feeling */ Xvoid printstring P((char *)); /* print string with feeling */ Xunsigned long bytestokb P((struct stat *)); /* convert bytes to kb */ Xvoid date P((time_t)); /* make date readable */ Xvoid makepath P((DIRENTRY *)); /* form the path */ Xvoid longprint P((struct stat *)); /* long format */ Xvoid printentry P((DIRENTRY *, DIRENTRY *, int, int)); /* print this entry */ XDIRENTRY *newentry P((char *)); /* malloc a new entry */ Xvoid freelist P((DIRLIST)); /* free entries */ Xvoid freeentry P((DIRENTRY *)); /* free an entry */ Xint alphacmp P((DIRENTRY *, DIRENTRY *)); /* alphabetic comparison */ Xint mtimecmp P((DIRENTRY *, DIRENTRY *)); /* compare by modification time */ Xvoid list P((DIRENTRY *, DIRLIST, int, int)); /* file listing routine */ Xvoid suffix P((DIRENTRY *)); /* do suffixes */ Xint filestat P((int, X int (*)(char *, struct stat *), X char *, struct stat *)); /* get status of file */ Xvoid *safemalloc P((unsigned int)); /* malloc with feeling */ XDIRENTRY *makelist P((char *, int *, int *, X unsigned long *)); /* create list of entries */ Xvoid *lsort P((void *, int, X int (*)(void *, void *)));/* linked list sort */ X X#ifdef S_IFLNK Xchar *followlink P((char *)); /* follow symbolic link */ X#endif X X/* X * Unitialised Globals. X */ X XDIRLIST rlist; /* temporary recursive list */ XDIRLIST dlist; /* list of directories */ Xtime_t today; /* today's date */ X X/* X * Initialised Globals. X */ X Xunsigned char flags[BITCHARS(128)] = {0}; Xchar pathname[PATH_MAX+1] = {0}; /* current path */ Xchar *endpath = pathname; /* end of path */ Xint (*compare)() = alphacmp; /* sort criteria */ Xint (*status)() = STAT; /* opaque status */ Xint extrawidth = INTERSPACE; /* implied extra space to add */ Xchar *incompatible[] = { /* mutually exclusive flags */ X "aA", "mx1C", "glno", "bq", "pF", (char *) NIL X}; X Xint main(argc, argv) X Xint argc; /* number of arguments */ Xchar *argv[]; /* list of arguments */ X X{ X DIRENTRY *dp; /* directory scanner */ X DIRLIST nlist; /* list of files */ X int showdir = 0; /* print directory name */ X BOOL(dototal); /* show totals */ X BOOL(dostat); /* need to do a stat */ X BOOL(dodir); /* check for directory */ X X/* Initialise by emptying the directory lists */ X EMPTY_dl(dlist), EMPTY_dl(nlist); X X/* Parse the command line */ X dolsopts(); X parse(argc, argv); X init(); X X/* No arguments defaults to a list of the current directory */ X if (optind >= argc) { X dp = newentry(DEFAULTDIR); X APPEND_dl(dlist, dp); X } X X/* Insert arguments into the current list */ X else { X int number, width; /* width and number */ X X for (width=number=0; optind < argc; optind++) { X X dp = newentry(argv[optind]); X X if (EVAL(dostat, TEST('t') || TEST('g') || X TEST('o') || TEST('s') || X TEST('F') || TEST('p') || X TEST('R') || TEST('i') || X ! TEST('f') && ! TEST('d')) && X dp->status == (struct stat *) NIL && X filestat(0, stat, dp->name, X dp->status = X (struct stat *) safemalloc(sizeof(*dp->status)))) { X freeentry(dp); X continue; X } X X if (EVAL(dodir, TEST('f') || ! TEST('d')) && X (dp->status->st_mode & S_IFMT) == S_IFDIR) { X X free(dp->status); X dp->status = (struct stat *) NIL; X X if (HEAD_dl(dlist)) X showdir = 1; X APPEND_dl(dlist, dp); X } X else { X suffix(dp); X number++; X if (width < dp->length) X width = dp->length; X X APPEND_dl(nlist, dp); X } X } X X if (number) X showdir = 1; X X HEAD_dl(nlist) = (DIRENTRY *) lsort((void *) HEAD_dl(nlist), X LINKOFFSET, compare); X list((DIRENTRY *) NIL, nlist, width, number); X } X X if (! TEST('f')) X HEAD_dl(dlist) = (DIRENTRY *) lsort((void *) HEAD_dl(dlist), X LINKOFFSET, compare); X X for (dp = HEAD_dl(dlist); dp; showdir = 1, dp = dp->next) { X int width, number; /* width and number */ X unsigned long kb; /* kb in total */ X X freelist(nlist); X makepath(dp); X X if (showdir) X (void) printf("\n%.*s:\n", endpath-pathname-1, pathname); X X HEAD_dl(nlist) = makelist(pathname, &width, &number, &kb); X X if (EVAL(dototal, TEST('g') || TEST('o') || TEST('s'))) X (void) printf("total %lu\n", kb); X X list(dp, nlist, width, number); X } X X return 0; X} X X/* X * Scan the options from the environment variable. This is X * done in a similar fashion to the command line option X * scan but no errors are flagged. X */ X Xvoid dolsopts() X X{ X char *env = getenv(ENVNAME); /* environment options */ X X/* Output to tty allows environment settable options */ X if (isatty(fileno(stdout)) && env) { X while (*env && isascii(*env)) { X setflag(*env); X env++; X } X } X} X X/* X * Parse the command line arguments. This function will set X * the switches in the global variable flags. No interpretation X * of the switches is performed at this point. X */ X Xvoid parse(argc, argv) X Xint argc; /* number of arguments */ Xchar *argv[]; /* argument vector */ X X{ X int swch; /* switch character */ X X while ((swch = getopt(argc, argv, "abdfgilmnopqrstx1ACFLR")) != EOF) { X if (swch != '?') X setflag(swch); X else { X (void) fputs("Unrecognised option\n", stderr); X exit(1); X } X } X} X X/* X * Set the specified option switch. This function knows about X * mutually exclusive options and will turn off options which X * are not compatible with the current one. X */ X Xvoid setflag(ch) X Xint ch; /* option to set */ X X{ X char **p; /* scanner */ X X for (p = incompatible; *p != (char *) NIL; p++) X checkflag(ch, *p); X SET(ch); X} X X/* X * Check the specified flag against the list of mutually exclusive X * flags. If the flag appears, then all other flags in the list are X * turned off. If not, then nothing is done. X */ X Xvoid checkflag(ch, p) X Xint ch; /* flag */ Xchar *p; /* strings */ X X{ X if (strchr(p, ch) != (char *) NIL) { X while (*p != 0) { X if (*p++ != ch) X CLEAR(p[-1]); X } X } X} X X/* X * Scan the switches and initialise globals. This function will X * do some preliminary work on the switches to determine which X * globals will be needed and also which switches need to be X * set or cleared in addition to the current settings. X */ X Xvoid init() X X{ X/* Get today's date */ X today = time((long *) 0); X X/* Turn on -A if we're the super user */ X if (! TEST('a') && getuid() == SUPER_USER) X SET('A'); X X/* Force raw directory listing */ X if (TEST('f')) { X CLEAR('l'); CLEAR('n'); CLEAR('o'); CLEAR('g'); X CLEAR('t'); X CLEAR('s'); X CLEAR('r'); X CLEAR('F'); CLEAR('p'); X CLEAR('A'); X SET('a'); X } X X#ifdef S_IFLNK X/* File status */ X if (TEST('L')) X status = stat; X#endif X X/* Sort criterion */ X if (TEST('t')) X compare = mtimecmp; X X/* Open the password and group files if required */ X if (TEST('l') || TEST('n')) { X SET('o'); X SET('g'); X } X X/* Long format forces output to be single column */ X if (TEST('g') || TEST('o')) { X CLEAR('m'); X CLEAR('x'); X CLEAR('C'); X } X X/* Accumulate extra width if required */ X if (TEST('i')) X extrawidth += INODEWIDTH+1; X if (TEST('s')) X extrawidth += BLOCKWIDTH+1; X} X X/* X * Make a linked list of entries using specified directory. The X * directory is rewound before being scanned. The function X * returns a pointer to the head of the list of entries. The X * function gathers two important statistics as the list X * is created. It will return the width required to print X * the files, and also the number of files in the list. X * X * The list will be sorted according to the current sorting X * criteria. X */ X XDIRENTRY *makelist(dirname, width, number, kb) X Xchar *dirname; /* name of directory to scan */ Xint *width, *number; /* width and number */ Xunsigned long *kb; /* number of kb */ X X{ X DIR *dirp; /* directory to scan */ X struct dirent *cp; /* current entry */ X DIRLIST nlist; /* files in directory */ X DIRENTRY *p; /* new entry */ X BOOL(dostat); /* perform a stat */ X BOOL(doblock); /* check block sizes */ X X EMPTY_dl(nlist); X *width = 0; X *number = 0; X *kb = 0; X X if ((dirp = opendir(dirname)) == (DIR *) NIL) { X (void) fprintf(stderr, "Cannot access %s: %s\n", pathname, strerror(errno)); X return (DIRENTRY *) NIL; X } X X while ((cp = readdir(dirp)) != (struct dirent *) NIL) { X if (cp->d_name[0] != '.' || TEST('a') || X cp->d_name[1] != 0 && (cp->d_name[1] != '.' || cp->d_name[2] != 0) X && TEST('A')) { X X p = newentry(cp->d_name); X X if (EVAL(dostat, TEST('t') || TEST('g') || X TEST('o') || TEST('s') || X TEST('F') || TEST('p') || X TEST('R') || TEST('i')) && X filestat(0, status, p->name, X p->status = X (struct stat *) safemalloc(sizeof(*p->status)))) { X freeentry(p); X continue; X } X X suffix(p); X X if (*width < p->length) X *width = p->length; X (*number)++; X X if (EVAL(doblock, TEST('g') || TEST('o') || TEST('s'))) X *kb += bytestokb(p->status); X X APPEND_dl(nlist, p); X } X } X X#ifndef _MINIX X (void) closedir(dirp); X#else X if (closedir(dirp)) { X (void) fprintf(stderr, "Cannot close directory: %s\n", strerror(errno)); X exit(1); X } X#endif X X return TEST('f') ? HEAD_dl(nlist) X : (DIRENTRY *) lsort((void *) HEAD_dl(nlist), X LINKOFFSET, compare); X} X X/* X * This function performs does the formatting and output for ls(1). X * The function is passed a list of files (not necessarily sorted) X * to list. All files will be listed. . and .. removal should have X * been done BEFORE calling this function. X */ X Xvoid list(parent, lp, width, number) X XDIRENTRY *parent; /* parent to this entry */ XDIRLIST lp; /* files to list */ Xint width, number; /* width and number */ X X{ X if (number) { X X/* Empty recursive directory list */ X EMPTY_dl(rlist); X X/* Select the correct output format */ X if (TEST('m')) X streamoutput(parent, lp); X else if (! TEST('C')) X acrosspage(parent, lp, width); X else X downpage(parent, lp, width, number); X X (void) putchar('\n'); X X/* Check recursive list */ X if (HEAD_dl(rlist)) { X if (! TEST('f')) X HEAD_dl(rlist) = (DIRENTRY *) lsort((void *) HEAD_dl(rlist), X LINKOFFSET, compare); X TIE_dl(dlist, HEAD_dl(rlist)); X } X } X} X X/* X * List the entries across the page. Single column output is X * treated as a special case of this. This is the easiest X * since the list of files can be scanned and dumped. X */ X Xvoid acrosspage(parent, lp, width) X XDIRENTRY *parent; /* parent to this entry */ XDIRLIST lp; /* files to list */ Xint width; /* maximum width */ X X{ X DIRENTRY *p; /* entries to print */ X int columns; /* columns to print in */ X int colno; /* which column we're printing */ X X columns = TEST('x') ? COLUMNS/(width+extrawidth) : 1; X X for (colno = 0, p = HEAD_dl(lp); p; p = p->next) { X if (++colno > columns) { X colno = 1; X (void) putchar('\n'); X } X printentry(parent, p, width, colno != columns); X } X} X X/* X * Print the entries down the page. This is the default format X * for multicolumn listings. It's unfortunate that this is X * the most acceptable to the user, but it causes the program X * a little grief since the list of files is not in the X * most convenient order. Most of this code is taken up X * with rearranging the list to suit the output format. X */ X Xvoid downpage(parent, lp, width, number) X XDIRENTRY *parent; /* parent to this entry */ XDIRLIST lp; /* files to list */ Xint width, number; /* width and number */ X X{ X DIRENTRY *c[(COLUMNS+INTERSPACE-1)/INTERSPACE]; /* column pointers */ X DIRENTRY *p; /* list scanner */ X int columns; /* columns to print in */ X int rows; /* number of rows per column */ X int i, j; /* general counters */ X X/* Nominal number of columns - targeted number of rows */ X columns = COLUMNS/(width+extrawidth); X rows = (number+columns-1)/columns; X X/* Scan and fill in pointers adjusting columns */ X for (columns = 0, p = HEAD_dl(lp); p; ) { X X c[columns++] = p; X for(i = rows; i-- && p; p = p->next) X ; X } X X/* Scan and print the listing */ X for (i = 0; i < rows; i++) { X if (i) X (void) putchar('\n'); X X for (j = 0; j < columns; j++) { X if (c[j]) { X printentry(parent, c[j], width, j != columns-1); X c[j] = c[j]->next; X } X } X } X} X X/* X * List the files using stream format. This code looks a bit X * kludgy because it is necessary to KNOW how wide the next X * entry is going to be. If the width would case the printout X * to exceed the width of the display, the entry must be printed X * on the next line. X */ X Xvoid streamoutput(parent, lp) X XDIRENTRY *parent; /* parent to this entry */ XDIRLIST lp; /* files to list */ X X{ X DIRENTRY *p; /* entries to print */ X int colno; /* current column */ X int midline; /* in middle of line */ X int tw; /* width of this entry */ X int x; /* inode calculation */ X BOOL(dopretty); /* pretty print */ X unsigned long kb; /* size in kb */ X X for (midline = colno = 0, p =HEAD_dl(lp); p; p = p->next) { X X/* Nominal length of the name */ X tw = p->length; X X/* Pretty printing adds an extra character */ X if (EVAL(dopretty, TEST('F') || TEST('p'))) { X if ((p->status->st_mode & S_IFMT) == S_IFDIR) tw++; X else if (TEST('F') && p->status->st_mode & 0111) tw++; X } X X/* Size will add to the length */ X if (TEST('s')) { X kb = bytestokb(p->status); X do { X tw++; X } while ((kb /= 10) != 0); X tw++; X } X X/* Inode number adds to the length (plus the separating space) */ X if (TEST('i')) { X x = p->status->st_ino; X tw++; X do X tw++; X while ((x /= 10) != 0); X } X X/* There will be a separating comma */ X if (p->next) X tw++; X X/* There will be a separating space */ X if (midline) X tw++; X X/* Begin a new line if necessary in which case there is no separating space */ X if (colno + tw >= COLUMNS) { X (void) putchar('\n'); X midline = 0; X colno = 0; X tw--; X } X X/* Separate entries if required */ X if (midline) X (void) putchar(' '); X X/* Now the entry proper */ X printentry(parent, p, 0, 0); X X/* Now the separating comma */ X if (p->next) X (void) putchar(','); X X midline = 1; X colno += tw; X } X} X X/* X * Print this entry on stdout. No newline is appended to the X * output. This function localises all the transformations X * which have to be carried out to make the output readable. X * Columnar magic is done elsewhere. X */ X Xvoid printentry(parent, p, w, padout) X XDIRENTRY *parent; /* parent to this entry */ XDIRENTRY *p; /* entry to print */ Xint w; /* width of field */ Xint padout; /* pad this entry */ X X{ X int pad; /* pad count */ X BOOL(dolong); /* long print */ X X if (TEST('i')) { X (void) printf(sizeof(p->status->st_ino) == sizeof(unsigned int) X ? "%*u " : "%*lu ", X w ? INODEWIDTH : 0, X p->status->st_ino); X } X X if (TEST('s')) X (void) printf("%*lu ", w ? BLOCKWIDTH : 0, bytestokb(p->status)); X X if (EVAL(dolong, TEST('o') || TEST('g'))) X longprint(p->status); X X/* Print the name of this file */ X printstring(p->name); X printstring(p->suffix); X X/* Only pad it if the caller requires it */ X if (padout) X for (pad = w - p->length + INTERSPACE; pad-- > 0; (void) putchar(' ')) X ; X X/* If recursion is required, add to list if it's a directory */ X if (TEST('R') && (p->status->st_mode & S_IFMT) == S_IFDIR && X (p->name[0] != '.' || X p->name[1] != 0 && (p->name[1] != '.' || p->name[2] != 0))) { X DIRENTRY *np = newentry(p->name); X np->parent = parent; X APPEND_dl(rlist, np); X } X} X X/* X * Format and print out the information for a long listing. X * This function handles the conversion of the mode bits X * owner, etc. It assumes that the status has been obtained. X */ X Xvoid longprint(sp) X Xstruct stat *sp; /* pointer to status */ X X{ X unsigned int filetype; /* type of file */ X char filecode; /* code for type of file */ X unsigned int mode; /* current mode */ X char *prot[3]; /* protection ogu */ X static struct passwd *pwent = (struct passwd *) NIL; /* password entry */ X static struct group *grent = (struct group *) NIL; /* group entry */ X static char *protmode[16] = { /* protection modes */ X "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx", X "--S", "--s", "-wS", "-ws", "r-S", "r-s", "rwS", "rws" X }; X static unsigned int setbits[3] = { /* set on execution bits */ X 0, S_ISGID, S_ISUID X }; X int i; /* general counter */ X X filetype = sp->st_mode & S_IFMT; X if (filetype == S_IFDIR) filecode = 'd'; X else if (filetype == S_IFBLK) filecode = 'b'; X else if (filetype == S_IFCHR) filecode = 'c'; X#ifdef S_IFIFO X else if (filetype == S_IFIFO) filecode = 'p'; X#endif X#ifdef S_IFLNK X else if (filetype == S_IFLNK) filecode = 'l'; X#endif X#ifdef S_IFSOCK X else if (filetype == S_IFSOCK) filecode = 's'; X#endif X else filecode = '-'; X X for (i = 0, mode = sp->st_mode; i < 3; mode >>= 3, i++) X prot[i] = protmode[(mode & 07) + (sp->st_mode & setbits[i] ? 8 : 0)]; X X (void) printf("%c%s%s%s %2d ", X filecode, prot[2], prot[1], prot[0], sp->st_nlink); X X if (TEST('o')) { X if (! TEST('n') && ((pwent && pwent->pw_uid == sp->st_uid) || X (pwent = getpwuid(sp->st_uid)) != (struct passwd *) NIL)) X (void) printf("%-8.8s ", pwent->pw_name); X else X (void) printf("%-8d ", sp->st_uid); X } X X if (TEST('g')) { X if (! TEST('n') && ((grent && grent->gr_gid == sp->st_gid) || X (grent = getgrgid(sp->st_gid)) != (struct group *) NIL)) X (void) printf("%-9.9s ", grent->gr_name); X else X (void) printf("%-9d ", sp->st_gid); X } X X/* Now show how big the file is */ X if (filetype != S_IFCHR && filetype != S_IFBLK) X (void) printf("%6ld ", sp->st_size); X else X (void) printf("%2d,%3d ", (sp->st_rdev >> 8) & 0377, sp->st_rdev & 0377); X X/* Now the date */ X date(sp->st_mtime); X} X X/* X * Given the time convert it into human readable form. The month and X * day are always printed. If the time is within about the last half year, X * the hour and minute are printed, otherwise the year. X */ X Xvoid date(t) X Xtime_t t; /* time to print */ X X{ X struct tm *tmbuf; /* output time */ X X tmbuf = localtime(&t); X (void) printf("%.3s %2d ", X "JanFebMarAprMayJunJulAugSepOctNovDec"+tmbuf->tm_mon*3, X tmbuf->tm_mday); X if (t < today && (today-t) <= HALFYEAR) X (void) printf("%2d:%02d ", tmbuf->tm_hour, tmbuf->tm_min); X else X (void) printf("%5d ", tmbuf->tm_year+BASEYEAR); X} X X/* X * Convert the size of a file (in bytes) to the number of X * kilobytes of storage used. This figure will include the X * number of indirect blocks used to store the file. X * The Minix code was lifted from the original Minix ls. X */ X Xunsigned long bytestokb(sp) X Xstruct stat *sp; /* status buffer */ X X{ X#ifdef _BSD X return sp->st_blocks * STD_BLK / 1024; X#endif X X#ifdef _MINIX X unsigned long blocks; /* accumulated blocks */ X unsigned long fileb; /* size of file in blocks */ X X/* Compute raw file size and checking for direct zones */ X fileb = (sp->st_size + (long) STD_BLK - 1)/STD_BLK; X blocks = fileb; X if (fileb > NR_DZONE_NUM) { X X/* Single indirect zones */ X blocks++; X fileb -= NR_DZONE_NUM; X if (fileb > NR_INDIRECTS) { X X/* Doubly indirect zones */ X blocks++; X fileb -= NR_INDIRECTS; X blocks += (fileb + NR_INDIRECTS - 1)/NR_INDIRECTS; X } X } X return blocks * STD_BLK / 1024; X#endif X} X X/* X * Chase the parent pointers and make a path. This path is X * used later to stat the files in each directory. X */ X Xvoid makepath(p) X XDIRENTRY *p; /* directory */ X X{ X char tmppath[PATH_MAX+1]; /* build it here */ X char *cp; /* pointer to tmppath */ X char *cq; /* pointer into name */ X char *cr; /* temporary pointer */ X X/* Work your way back up to the root */ X for (cp = tmppath, *cp++ = 0; p; p = p->parent) { X for (cq = p->name; *cq; *cp++ = *cq++) X ; X *cp++ = 0; X } X X/* Now flip the order */ X for (cq = pathname, --cp; cp != tmppath; ) { X while (*--cp) X ; X for (cr = cp+1; *cr; *cq++ = *cr++) X ; X if (cq[-1] != '/') X *cq++ = '/'; X } X *(endpath = cq) = 0; X} X X/* X * Allocate memory for a new entry. Memory is allocated and X * filled in. The next, parent and status pointers are set X * to null. The function returns a pointer to the new entry. X */ X XDIRENTRY *newentry(name) X Xchar *name; /* name of entry */ X X{ X DIRENTRY *p; /* pointer to entry */ X X p = (DIRENTRY *) safemalloc(sizeof(*p)); X (void) strcpy(p->name = (char *) safemalloc((unsigned int) strlen(name)+1), X name); X p->suffix = (char *) NIL; X p->next = (DIRENTRY *) NIL; X p->parent = (DIRENTRY *) NIL; X p->status = (struct stat *) NIL; X return p; X} X X/* X * Free the memory associated with list of elements. The function X * assumes all memory has been allocated using malloc(), so that X * free() will work without suffering a heart attack. The list X * header is set to null before returning. X */ X Xvoid freelist(lp) X XDIRLIST lp; /* list to free */ X X{ X while (lp[0]) { X lp[1] = lp[0]->next; X freeentry(lp[0]); X lp[0] = lp[1]; X } X EMPTY_dl(lp); X} X X/* X * Free the memory associated with a directory entry. Remember that X * all the memory should have been allocated using malloc(). X */ X Xvoid freeentry(p) X XDIRENTRY *p; /* pointer to entry */ X X{ X if (p) { X if (p->name) X free(p->name); X if (p->suffix && p->suffix[0] != 0 && p->suffix[1] != 0) X free(p->suffix); X if (p->status) X free(p->status); X free(p); X } X} X X/* X * Compare entries in the file list. Pointers to two entries are X * expected as arguments (non null pointers). Compare the entries X * and return -1, 0 or 1 depending on whether the first argument X * is less than, equal to or greater than the second. X */ X Xint alphacmp(p, q) X XDIRENTRY *p, *q; X X{ X register int rv = strcmp(p->name, q->name); X X return TEST('r') ? -rv : rv; X} X X/* X * Compare entries on the basis of modification time. Pointers to X * two entries are expected as arguments. It is assumed that the status X * has been obtained. The routine will return -1, 0 or 1 depending X * on whether the first argument is later than, equal to or earlier X * than the second. X */ X Xint mtimecmp(p, q) X XDIRENTRY *p, *q; /* entries to compare */ X X{ X register int rv; /* comparison result */ X long delta = p->status->st_mtime - q->status->st_mtime; X X rv = delta > 0 ? -1 : delta ? 1 : 0; X X return TEST('r') ? -rv : rv; X} X X/* X * Append file name suffix. The suffix can be a simple file type X * indicator or can be the full path name if it is a symbolic X * link. X */ X Xvoid suffix(p) X XDIRENTRY *p; /* directory entry */ X X{ X struct stat lsb; /* link stat buffer */ X char *type; /* file type */ X BOOL(showtype); /* show file type */ X#ifdef S_IFLNK X BOOL(dolink); /* follow link */ X char *link; /* link to */ X#endif X X p->length = 0; X X#ifdef S_IFLNK X if (EVAL(dolink, TEST('o') || TEST('g')) && X (p->status->st_mode & S_IFMT) == S_IFLNK) { X if (! filestat(1, stat, link = followlink(p->name), &lsb)) { X *p->status = lsb; X p->length += 4 + stringlength(link); X *(p->suffix = (char *) safemalloc((unsigned int) p->length+2)) = 0; X (void) strcat(p->suffix, " -> "); X (void) strcat(p->suffix, link); X } X } X#endif X X type = ""; X if (EVAL(showtype, TEST('F') || TEST('p'))) { X if ((p->status->st_mode & S_IFMT) == S_IFDIR) type = "/"; X else if (TEST('F')) { X if ((p->status->st_mode & 0111)) type = "*"; X#ifdef S_IFLNK X if ((p->status->st_mode & S_IFMT) == S_IFLNK) type = "@"; X#endif X#ifdef S_IFSOCK X if ((p->status->st_mode & S_IFMT) == S_IFSOCK) type = "="; X#endif X } X } X X p->length += stringlength(p->name) + strlen(type); X X if (p->suffix == NIL) X p->suffix = type; X else if (*type != 0) X (void) strcat(p->suffix, type); X} X X/* X * Follow a symbolic link and return the name of the file X * to which it points. The function will return a pointer X * to a static area. X */ X X#ifdef S_IFLNK Xchar *followlink(name) X Xchar *name; /* name of link to follow */ X X{ X static char linkto[PATH_MAX+1]; /* link to name */ X int c; /* size of link name */ X X (void) strcpy(endpath, name); X if ((c = readlink(pathname, linkto, sizeof(linkto)-1)) < 0) { X (void) fprintf(stderr, "%s readlink failure: %s\n", X pathname, strerror(errno)); X exit(1); X } X linkto[c] = 0; X return linkto; X} X#endif X X/* X * Get the status of a file prepending the path before calling X * stat. The function pointer should indicate which status function X * to call. Return 0 if successful, non-zero if the name cannot X * be found. X */ X Xint filestat(T(int silent, silent), X T(int (*status)(char *, struct stat *), status), X T(char *name, name), X T(struct stat *statbuf, statbuf)) X XD(int silent) /* say no evil */ XD(int (*status)()) /* file status */ XD(char *name) /* name of file */ XD(struct stat *statbuf) /* stat buffer */ X X{ X if (*name != '/') { X (void) strcpy(endpath, name); X name = pathname; X } X X if ((*status)(name, statbuf) X#ifdef S_IFLNK X && (status == lstat || X errno != ENOENT || X lstat(name, statbuf)) X#endif X ) { X if (errno == ENOENT) { X if (! silent) X (void) fprintf(stderr, "%s not found\n", name); X return 1; X } X else { X (void) fprintf(stderr, "%s stat failure: %s\n", name, strerror(errno)); X exit(1); X } X } X return *endpath = 0; X} X X/* X * Compute the length of the string taking into account the X * form of output. String lengths will increase if the X * visible output flag has been specified. X */ X Xint stringlength(s) X Xchar *s; /* file name */ X X{ X register int i = 0; /* length */ X register int c; /* character */ X X while ((c = *s++) != 0) X if (TEST('b') && !isprint(c)) X i += 4; X else X i++; X return i; X} X X/* X * Print a string converting invisible characters into visible sequences X * if necessary. X */ X Xvoid printstring(s) X Xchar *s; /* string to print */ X X{ X int c; /* temporary */ X X for (; *s; ) { X if (isprint(c = *s++)) X (void) putchar(c); X else if (TEST('s')) X (void) putchar('?'); X else if (TEST('b')) X (void) printf("\\%03d", c & 0377); X else X (void) putchar(c); X } X} X X/* X * This function does a malloc() with feeling. If malloc() returns X * null, indicating no more memory, the function will fail with X * an error message. The function will return a pointer to the X * allocated memory. X */ X Xvoid *safemalloc(n) X Xunsigned int n; /* amount to malloc */ X X{ X void *p; /* malloc'ed region */ X X if ((p = (void *) malloc(n)) == (void *) NIL) { X (void) fputs("Insufficient memory to complete operation\n", stderr); X exit(1); X } X return p; X} X X/* X * Sort a linked list using a natural merge sort with automatic X * run merging. Logical run boundaries between runs are not maintained X * allowing runs to naturally coalesce. X * X * This function accepts three arguments. The first is a pointer to X * the first element of the list. The second is the number of elements X * in the list. If this is given as -1, the list will be scanned first X * to find the number of elements (a null pointer indicating the final X * element). The third argument is the byte offset from the element X * pointer to find the pointer to the next element in the list. X */ X Xtypedef void *LIST[2]; X X#define EMPTYLIST(t) (t[0] = t[1] = (void *) 0) X#define NEXT(p) (* (void **) ((char *) p + offset)) X#define HEAD(t) (t[0]) X#define APPEND(a,p) if (a[0]) NEXT(a[1])=p,a[1]=p; else a[0]=a[1]=p X#define MOVE(a,p,q) APPEND(a,p); NEXT(p) = (void *) 0; p = q; X Xvoid *lsort(T(void *head, head), X T(int offset, offset), X T(int (*cmp)(void *, void *), cmp)) X XD(void *head) /* head of list */ XD(int offset) /* byte offset to link */ XD(int (*cmp)()) /* comparison */ X X{ X LIST pile[2]; /* distributions */ X void *p, *q; /* tape head */ X void *ap, *aq; /* first tape head */ X void *bp, *bq; /* second tape head */ X int tapeno; /* tape number */ X int eora, eorb; /* end of run */ X X/* Distribute the runs from the initial list */ X EMPTYLIST(pile[0]), EMPTYLIST(pile[1]); X X for (p = head, tapeno = 0; p; tapeno ^= 1) { X do { X eora = (q = NEXT(p)) != (void *) 0 ? (*cmp)(p,q) > 0 : 1; X MOVE(pile[tapeno], p, q); X } while (!eora); X } X X/* Now merge the runs from the two piles - distribute as we go */ X while (HEAD(pile[1])) { X X ap = HEAD(pile[0]); aq = NEXT(ap); X bp = HEAD(pile[1]); bq = NEXT(bp); X X EMPTYLIST(pile[0]), EMPTYLIST(pile[1]); X X for (tapeno = 0; X eora = (ap == 0), eorb = (bp == 0), !eora || !eorb; X tapeno ^= 1) { X X while (!eora && !eorb) { X if ((*cmp)(ap,bp) < 0) { X eora = aq == (void *) 0 || (*cmp)(ap,aq) > 0; X MOVE(pile[tapeno], ap, aq); X if (ap) X aq = NEXT(ap); X } X else { X eorb = bq == (void *) 0 || (*cmp)(bp,bq) > 0; X MOVE(pile[tapeno], bp, bq); X if (bp) X bq = NEXT(bp); X } X } X X/* At least one of the lists has finished - copy out the other */ X while (!eora) { X eora = aq == (void *) 0 || (*cmp)(ap,aq) > 0; X MOVE(pile[tapeno], ap, aq); X if (ap) X aq = NEXT(ap); X } X X while (!eorb) { X eorb = bq == (void *) 0 || (*cmp)(bp,bq) > 0; X MOVE(pile[tapeno], bp, bq); X if (bp) X bq = NEXT(bp); X } X } X } X X return HEAD(pile[0]); X} END_OF_FILE if test 36040 -ne `wc -c <'ls.c'`; then echo shar: \"'ls.c'\" unpacked with wrong size! fi # end of 'ls.c' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Earl Chew, Dept of Computer Science, Monash University, Australia 3168 ARPA: cechew%bruce.cs.monash.oz.au@uunet.uu.net ACS : cechew@bruce.oz ----------------------------------------------------------------------