chris@attron.ruhr.sub.org (Christian Schlichtherle) (01/28/91)
This is a shell script. It prints a documented listing on it's standard output. The original idea to write this was to have a better documented UUCP file list (/usr/spool/uucppublic/files). I was unlucky with the output of ls(C), because it gives no information about the contents of the files. So I wrote this. Of course, it works on every directory. Chris #!/bin/sh # This is a shell archive (shar 3.24) # made 01/27/1991 17:30 UTC by chris@attron.ruhr.sub.org # Source directory /u/chris/src/list # # existing files WILL be overwritten # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 202 -rw-r--r-- .list # 476 -r--r--r-- Makefile # 1775 -r--r--r-- README # 1581 -r--r--r-- list.sh # if touch 2>&1 | fgrep '[-amc]' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= .list ============== echo "x - extracting .list (Text)" sed 's/^X//' << 'SHAR_EOF' > .list && X.list Demonstration file for list(C) XMakefile Makefile for list(C) XREADME Documentation file for list(C) Xlist.sh Prints documented listing Xtestline This line will vanish when executing 'list -u' ;-) SHAR_EOF $TOUCH -am 0127181391 .list && chmod 0644 .list || echo "restore of .list failed" set `wc -c .list`;Wc_c=$1 if test "$Wc_c" != "202"; then echo original size 202, current size $Wc_c fi # ============= Makefile ============== echo "x - extracting Makefile (Text)" sed 's/^X//' << 'SHAR_EOF' > Makefile && X# X# @(#) Makefile 1.1 91/01/27 X# X# Written 1991 by Christian Schlichtherle X# (chris@attron.ruhr.sub.org) X# No copyright until you remove this header. The author X# disclaims any warranty. X# X# Makefile - Makefile for list(C). X# X XOWNER=chris XGROUP=freak XMODE=0755 XBIN=/u/bin X Xlist: list.sh X cp $? $@ X chmod $(MODE) $@ X Xinstall: $(BIN)/list X X$(BIN)/list: list X cp $? $@ X chown $(OWNER) $@ X chgrp $(GROUP) $@ X chmod $(MODE) $@ X Xuninstall: X rm -f $(BIN)/list X Xclean: X rm -f list SHAR_EOF $TOUCH -am 0127182891 Makefile && chmod 0444 Makefile || echo "restore of Makefile failed" set `wc -c Makefile`;Wc_c=$1 if test "$Wc_c" != "476"; then echo original size 476, current size $Wc_c fi # ============= README ============== echo "x - extracting README (Text)" sed 's/^X//' << 'SHAR_EOF' > README && X X @(#) README 1.1 91/01/27 X X Written 1991 by Christian Schlichtherle X (chris@attron.ruhr.sub.org) X No copyright unless you remove this header. The author X disclaims any warranty. X X README - Documentation file for list(C). X XNAME X X list - Prints documented listings. X XSYNTAX X X list [-A] [<pathname> ...] X list -u [<directory> ...] X XDESCRIPTION X X list(C) prints a documented listing on standard output. The X output is formated. The first field contains the filename, X the second field the size of the file in bytes. The rest of X the line contains the description for the file. The lines X are sorted in increasing ASCII collating sequence (like X ls(C)). The "-A" option indicates that files beginning with X a period are listed for non-super users too (like ls(C)). X X The description for the files is held in the file ".list" X in the directory where the file reside. This file is X formatted as follows: The first field contains the filename X and the rest of the line contains the description for the X file. The lines are sorted in increasing ASCII collating X sequence. X X The second form of the command 'list -u [<directory> ...]' X updates the ".list" file. The lines are sorted, descriptions X for nonexistent files are thrown away and the output is X formatted. X XFILES X X .list Description file for listing X XSEE ALSO X X ls(C), sort(C), join(C), awk(C) X XNOTES X X Whitespace in the description field of the ".list" file is X replaced by a single space character. X X Filenames must not contain escape sequences for printf(). X X If your awk bails out you will have to substitute the line X "print substr($0, i + 2);" with "print substr($0, i + 2, X 255);" or "print substr($0, i + 2, length($1) - i - 1);". I X used this because it shows better performance (need not X process length($1)). SHAR_EOF $TOUCH -am 0127182891 README && chmod 0444 README || echo "restore of README failed" set `wc -c README`;Wc_c=$1 if test "$Wc_c" != "1775"; then echo original size 1775, current size $Wc_c fi # ============= list.sh ============== echo "x - extracting list.sh (Text)" sed 's/^X//' << 'SHAR_EOF' > list.sh && X: X# X# @(#) list.sh 1.1 91/01/27 X# X# Written 1991 by Christian Schlichtherle X# (chris@attron.ruhr.sub.org) X# No copyright unless you remove this header. The author X# disclaims any warranty. X# X# list.sh - Bourne shell script for list(C). X# X XPATH=/bin:/usr/bin XLISTFILE=.list XTMP=/tmp/upd$$ XUSAGE="usage:\t$0 [-A] [<pathname> ...]\n\t$0 -u [<directory> ...]" X Xset -h X Xusage() { X echo $USAGE 1>&2 X exit 1 X} X Xlist() { X if [ $# -gt 1 ] X then X ECHO=ECHO X else X ECHO=: X fi X for FILE in $@ X do X if [ -d $FILE ] X then X $ECHO "\n$FILE:" X LISTPATH=$FILE/$LISTFILE X else X LISTPATH=`dirname $FILE`/$LISTFILE X fi X if [ -r $LISTPATH ] X then X ls -n $LISTOPT $FILE | join -a1 -j1 9 - $LISTPATH | \ X awk 'NF >= 9 { X printf "%-14s %7s", $1, $6; X if (NF > 9) X for (i = 10; i <= NF; i++) X printf " %s", $i; X printf "\n"; X } ' - X else X ls -n $FILE | awk '{ printf "%-14s %7s\n", $9, $5; }' - X fi X done X} X Xupdate() { X for FILE in $@ X do X [ ! -d $FILE ] && { X echo Not a directory: $FILE X continue X } X trap : 1 2 3 13 15 # save the listfile X sort $FILE/$LISTFILE > $TMP && X ls -A $FILE | join - $TMP | \ X awk ' { X printf $1; X if ((i = length($1)) < 8) X printf "\t\t"; X else X printf "\t"; X print substr($0, i + 2); X } ' - > $FILE/$LISTFILE X rm -f $TMP X trap '' 1 2 3 13 15 X done X} X XMODE=list Xfor i in $@ Xdo X case $i in X -A) [ $MODE = update ] && usage X LISTOPT="$LISTOPT -A" X ;; X -u) [ "$LISTOPT" ] && usage X MODE=update X ;; X -*) usage X ;; X *) break X ;; X esac X shift Xdone X Xset -- ${@:-.} X$MODE $* SHAR_EOF $TOUCH -am 0127182891 list.sh && chmod 0444 list.sh || echo "restore of list.sh failed" set `wc -c list.sh`;Wc_c=$1 if test "$Wc_c" != "1581"; then echo original size 1581, current size $Wc_c fi exit 0 -- Snail: Christian Schlichtherle, Elbscheweg 20, 5802 Wetter 4, Germany Email: chris@attron.ruhr.sub.org Tel.: +49 2335 7550 Politiker an die Front!
chris@attron.ruhr.sub.org (Christian Schlichtherle) (02/05/91)
This is a quick and dirty rehack of my "list" shellscript I posted before. The source code is not elegant at all, but the program is very _fast_! The bigger the directory, the faster it is in relation to "ls". Yes, it is even faster! Also, this version is more verbose than the old shell script. You can print the owner, group and last modification date of the file now. You can even use one description list for several directories... Chris P.S.: Enjoy it! #!/bin/sh # This is a shell archive (shar 3.24) # made 02/04/1991 21:15 UTC by chris@attron.ruhr.sub.org # Source directory /u/chris/src/list # # existing files WILL be overwritten # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 209 -rw-r----- .list # 539 -r--r----- Makefile # 2447 -r--r----- README # 379 -r--r----- config.h # 19228 -r--r----- list.c # if touch 2>&1 | fgrep '[-amc]' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= .list ============== echo "x - extracting .list (Text)" sed 's/^X//' << 'SHAR_EOF' > .list && X. This directory X.list Description list for this directory XMakefile Makefile for list(C) XREADME Documentation file for list(C) Xconfig.h Some configurable parameters for list(C) Xlist.c C Source for list(C) SHAR_EOF $TOUCH -am 0204210891 .list && chmod 0640 .list || echo "restore of .list failed" set `wc -c .list`;Wc_c=$1 if test "$Wc_c" != "209"; then echo original size 209, current size $Wc_c fi # ============= Makefile ============== echo "x - extracting Makefile (Text)" sed 's/^X//' << 'SHAR_EOF' > Makefile && X# X# @(#) Makefile 1.1 91/02/04 X# X# Author: Christian Schlichtherle, 1991 X# (chris@attron.ruhr.sub.org) X# X# Makefile - Makefile for list(C). X# X XBIN = /u/bin XOWNER = chris XGROUP = freak XMODE = 0711 X XCC = cc XCFLAGS = -Ml2 -i XLDFLAGS = -s X XSHELL = /bin/sh X XOBJS = list.o X Xlist: list.o X $(CC) $(CFLAGS) $(LDFLAGS) -o $@ list.o X Xlist.o: config.h X Xinstall: $(BIN)/list X X$(BIN)/list: list X cp $? $@ X chown $(OWNER) $@ X chgrp $(GROUP) $@ X chmod $(MODE) $@ X Xuninstall: X rm -f $(BIN)/list X Xclean: X rm -f a.out core *.o X Xclobber: clean X rm -f list X SHAR_EOF $TOUCH -am 0204221191 Makefile && chmod 0440 Makefile || echo "restore of Makefile failed" set `wc -c Makefile`;Wc_c=$1 if test "$Wc_c" != "539"; then echo original size 539, current size $Wc_c fi # ============= README ============== echo "x - extracting README (Text)" sed 's/^X//' << 'SHAR_EOF' > README && XThis is a quick and dirty rehack of my "list" shellscript I posted before. XThe source code is not elegant at all, but the program is very _fast_! XThe bigger the directory, the faster it is in relation to "ls". Yes, it is Xeven faster! XAlso, this version is more verbose than the old shell script. You can Xprint the owner, group and last modification date of the file now. You Xcan even use one description list for several directories... X X X @(#) README 1.1 91/02/04 X X Author: Christian Schlichtherle, 1991 X (chris@attron.ruhr.sub.org) X X README - Documentation file for list(C). X X XNAME X X list - Prints documented listings. X X XSYNTAX X X list [-s] [-a|-A] [-ugd] [-l <listpath>] [<pathname>] ... X list -U [-k] [-l <listpath>] [<directory>] ... X X XDESCRIPTION X X list(C) prints a documented listing on standard output. The X output is formatted. The first field contains the size of the X file in bytes and the second field the filename. The rest of X the line contains the description for the file. The lines X are sorted in increasing ASCII collating sequence (like X ls(C)). X The description for the files is held in the file ".list" X in the directory where the files reside. This file is X formatted as follows: The first field contains the filename X and the rest of the line contains the description for the X file. The lines are sorted in increasing ASCII collating X sequence. X X Options: X X -s Sorts the description lines found in ".list" before X listing the files. This is useful if your ".list" X file isn't sorted already. X -A Lists filenames beginning with a period for non X superusers, too. X -a Lists all valid entries found in the directory. X -u Prints the owner (user) of the file, too. X -g Prints the group of the file, too. X -d Prints the date of last modification to the file, X too. X -l name Overrides the default filename of the description X list ".list". You can use any valid pathname. X X The second form of the command 'list -U [<directory> ...]' X updates the ".list" file. The lines are sorted, descriptions X for nonexistent files are thrown away and the output is X formatted. X X Options: X X -k Keeps the description file if it is empty after X updating. Normally the file is removed if it is X empty after updating. X -l name Overrides the default filename of the description X list ".list". You can use any valid pathname. X The update is made to this file instead of the X default ".list". X X XFILES X X .list Description file for listing SHAR_EOF $TOUCH -am 0204221191 README && chmod 0440 README || echo "restore of README failed" set `wc -c README`;Wc_c=$1 if test "$Wc_c" != "2447"; then echo original size 2447, current size $Wc_c fi # ============= config.h ============== echo "x - extracting config.h (Text)" sed 's/^X//' << 'SHAR_EOF' > config.h && X/* X * @(#) config.h 1.1 91/02/04 X * X * Author: Christian Schlichtherle, 1991 X * (chris@attron.ruhr.sub.org) X * X * config.h - C include file for list(C). X */ X X /* Default name of the description file */ X#define LISTFILE ".list" X X /* Things you normally won't change */ X#define TABLEN 8 X#define PATHLEN 255 X#define LINELEN 255 X#define MEMBLKSIZ 1024 SHAR_EOF $TOUCH -am 0204221191 config.h && chmod 0440 config.h || echo "restore of config.h failed" set `wc -c config.h`;Wc_c=$1 if test "$Wc_c" != "379"; then echo original size 379, current size $Wc_c fi # ============= list.c ============== echo "x - extracting list.c (Text)" sed 's/^X//' << 'SHAR_EOF' > list.c && X/* X * @(#) list.c 1.1 91/02/04 X * X * Author: Christian Schlichtherle, 1991 X * (chris@attron.ruhr.sub.org) X * X * list.c - C module for list(C). X */ X X#if !defined(lint) && !defined(library) Xstatic char sccsid[] = "@(#) list.c 1.1 91/02/04 "; X#endif /* not lint and not library */ X X#include <stdio.h> X#include <ctype.h> X#include <string.h> X#include <time.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/dir.h> X#include <malloc.h> X#include <errno.h> X#include <pwd.h> X#include <grp.h> X#include <search.h> X#include "config.h" X Xchar *month[] = { X "Jan", X "Feb", X "Mar", X "Apr", X "May", X "Jun", X "Jul", X "Aug", X "Sep", X "Oct", X "Nov", X "Dec" X}; Xchar *usage_txt[] = { X "usage:\t%s [-s] [-a|-A] [-ugd] [-l <listpath>] [<pathname>] ...\n", X "\t%s -U [-k] [-l <listpath>] [<directory>] ...\n" X}; X Xextern void perror(); Xextern int getopt(); Xextern void exit(); Xextern void qsort(); Xextern char *bsearch(); Xextern struct passwd *getpwent(); Xextern struct group *getgrent(); Xextern long time(); X Xchar *progname; Xint update = 0; Xchar *multicast = NULL; Xenum { simple, all, allbutdot } listmode = simple; Xint KeepEmptyList = 0; Xint SortBeforeList = 0; Xint PrintOwner = 0; Xint PrintGroup = 0; Xint PrintDate = 0; X X/* X * error - Prints an error message. X * The first argument (if not NULL) is printed using perror(), X * the second argument is printed on standard error. X */ Xvoid error(err1, err2) Xchar *err1, *err2; X{ X if ((err1 || err2) && !isatty(0)) X (void) fprintf(stderr, "%s: ", progname); X if (err1) X perror(err1); X if (err2) X (void) fprintf(stderr, "%s\n", err2); X X return; X} X X/* X * usage - Prints usage and exits. X */ Xvoid usage() X{ X unsigned i; X X if (!isatty(0)) X (void) fprintf(stderr, "%s: ", progname); X for (i = 0; i < sizeof(usage_txt) / sizeof(usage_txt[0]); i++) X (void) fprintf(stderr, usage_txt[i], progname); X X exit(1); X} X X/* X * fatal - Prints an error message using error() and exits. X */ Xvoid fatal(err1, err2) Xchar *err1, *err2; X{ X error(err1, err2); X X exit(1); X return; X} X X/* X * FormatLines - Formats lines in order to update the description file. X */ Xvoid FormatLines(linesc, lines) Xunsigned linesc; Xchar **lines; X{ X char buf[LINELEN + 1]; X char *ptr; X char *lptr; X X#ifdef DEBUG X (void) fputs("Formating some lines...\n", stderr); X#endif X buf[LINELEN] = '\0'; X while (linesc--) X { X ptr = buf; X lptr = lines[linesc]; X X while (isspace(*lptr)) /* Skip any leading blanks in source */ X lptr++; X while (!isspace(*lptr)) /* Extract filename to destination */ X *ptr++ = *lptr++; X while (isspace(*lptr)) /* Skip further blanks in source */ X lptr++; X *ptr++ = '\t'; /* Append an appropriate number of tabs */ X if (ptr - buf <= TABLEN) X *ptr++ = '\t'; X /* Copy the rest of the line */ X (void) strncpy(ptr, lptr, LINELEN - (ptr - buf)); X X (void) free(lines[linesc]); X if ((lines[linesc] = strdup(buf)) == NULL) X fatal(NULL, "Out of memory"); X } X X return; X} X X/* X * skipword - Skips the next word in the string. X * Returns NULL if there is no word after the next word. X */ Xchar *skipword(s) Xchar *s; X{ X while (isspace(*s)) X s++; X while (*s && !isspace(*s)) X s++; X while (isspace(*s)) X s++; X if (*s) X return s; X X return NULL; X} X X/* X * wrdcmp - Compares the first word in each string with each other. X */ Xint wrdcmp(s1, s2) Xchar **s1, **s2; X{ X char *p1 = *s1, *p2 = *s2; X X while (isspace(*p1)) X p1++; X while (isspace(*p2)) X p2++; X while (*p1 && *p1 == *p2 && !isspace(*p1) && !isspace(*p2)) X p1++, p2++; X X if (isspace(*p1)) X if (isspace(*p2)) X return 0; X else X return -*p2; X else X if (isspace(*p2)) X return *p1; X else X return *p1 - *p2; X} X X/* X * SortLines - Sorts some lines. X * Only the first word is used for sorting. X */ Xvoid SortLines(linesc, lines) Xunsigned linesc; Xchar **lines; X{ X#ifdef DEBUG X (void) fputs("Sorting some lines...\n", stderr); X#endif X qsort((char *) lines, linesc, sizeof(*lines), wrdcmp); X X return; X} X X/* X * FreeLines - Frees previously allocated lines by AddMem(). X */ Xvoid FreeLines(listc, list) Xunsigned *listc; Xchar ***list; X{ X if (*listc != 0) X { X while (*listc != 0) X free((*list)[--(*listc)]); X if (*list != (char **) 0) X free((char *) *list); X *listc = 0; X *list = (char **) 0; X } X X return; X} X X/* X * isdir - Checks if "path" is a directory. X */ Xint isdir(path) Xchar *path; X{ X struct stat st; X X if (stat(path, &st) == -1) X return 0; X if (st.st_mode & 040000) X return 1; X X return 0; X} X X/* X * BuildListpath - Builds the path of the description file used for listing. X */ Xvoid BuildListpath(path, isadir, listpath) Xchar *path; Xint isadir; Xchar *listpath; X{ X extern char *multicast; X char *ptr; X X if (multicast) X (void) strcpy(listpath, multicast); X else X if (isadir) X (void) sprintf(listpath, "%s/%s", path, LISTFILE); X else X if ((ptr = strrchr(path, '/')) != NULL) X { X (void) strncpy(listpath, path, ptr - path + 1); X listpath[ptr - path + 2] = '\0'; X (void) strcat(listpath, LISTFILE); X } X else X (void) strcpy(listpath, LISTFILE); X X return; X} X X/* X * AddMem - Adds memory (if needed) to hold some data. X * Memory is added in MEMBLKSIZ blocks of bytes. X */ Xvoid AddMem(nel, ptr, width) Xunsigned nel; Xchar **ptr; Xunsigned width; X{ X unsigned blksiz = MEMBLKSIZ / width * width; X X if (!(nel * width % blksiz) || !*ptr) X if (!nel || !*ptr) X { X if ((*ptr = malloc(blksiz)) == NULL) X fatal(NULL, "Out of memory"); X } X else X if ((*ptr = realloc(*ptr, (nel + MEMBLKSIZ / width) * width)) X == NULL) X fatal(NULL, "Out of memory"); X X return; X} X X/* X * ReadFile - Reads a file using "inpfnc()". X */ Xint ReadFile(path, listc, list, inpfnc) Xchar *path; Xunsigned *listc; Xchar ***list; Xchar *(*inpfnc)(); X{ X FILE *fp; X char *line; X X if ((fp = fopen(path, "r")) == (FILE *) 0) X return -1; X while ((line = (*inpfnc)(fp)) != NULL) X { X AddMem(*listc, (char **) list, sizeof(char *)); X if (((*list)[(*listc)++] = strdup(line)) == NULL) X fatal(NULL, "Out of memory"); X } X (void) fclose(fp); X X return 0; X} X X/* X * flistentry - Reads a single line of a description file. X */ Xchar *flistentry(fp) XFILE *fp; X{ X static char buf[LINELEN + 1]; X X /* Read lines until a line contains more than the filename */ X while (fgets(buf, LINELEN + 1, fp) != NULL) X if (skipword(buf)) X return buf; X X return NULL; X} X X/* X * ReadList - Reads description file. X */ Xvoid ReadList(listpath, listc, list) Xchar *listpath; Xunsigned *listc; Xchar ***list; X{ X#ifdef DEBUG X (void) fprintf(stderr, "Reading description list \"%s\"...\n", listpath); X#endif X X (void) ReadFile(listpath, listc, list, flistentry); X return; X} X X/* X * fdirentry - Reads a single directory entry. X */ Xchar *fdirentry(fp) XFILE *fp; X{ X static char buf[DIRSIZ + 1]; X struct direct dir; X X buf[DIRSIZ] = '\0'; X X while (fread((char *) &dir, sizeof(dir), 1, fp) == 1) X { X if (dir.d_ino == 0) X continue; X (void) strncpy(buf, dir.d_name, DIRSIZ); X if (update) X return buf; X switch (listmode) X { X case simple: X if (buf[0] != '.') X return buf; X break; X case all: X return buf; X case allbutdot: X if (strcmp(buf, ".") && strcmp(buf, "..")) X return buf; X } X } X X return NULL; X} X X/* X * ReadDir - Reads a directory. X */ Xint ReadDir(dirpath, dirc, dir) Xchar *dirpath; Xunsigned *dirc; Xchar ***dir; X{ X struct stat st; X X#ifdef DEBUG X (void) fprintf(stderr, "Reading directory \"%s\"...\n", dirpath); X#endif X X if (stat(dirpath, &st) == -1) X { X error(dirpath, NULL); X return -1; X } X errno = 0; X if (!(st.st_mode & 040000)) X errno = ENOTDIR; X if (!errno) X (void) access(dirpath, 01); X if (errno) X error(dirpath, NULL); X return ReadFile(dirpath, dirc, dir, fdirentry); X} X X/* X * uidcmp - Compares user ids. X */ Xint uidcmp(pw1, pw2) Xstruct passwd *pw1, *pw2; X{ X return pw1->pw_uid - pw2->pw_uid; X} X X/* X * ReadEtcPasswd - Reads /etc/passwd file. X */ Xvoid ReadEtcPasswd(pw_c, pw_v) Xunsigned *pw_c; Xstruct passwd **pw_v; X{ X struct passwd *pw; X unsigned old_c; X X#ifdef DEBUG X (void) fputs("Reading password entries...\n", stderr); X#endif X *pw_c = 0; X *pw_v = (struct passwd *) 0; X while ((pw = getpwent()) != (struct passwd *) 0) X { X AddMem(*pw_c, (char **) pw_v, sizeof(struct passwd)); X old_c = *pw_c; X (void) lsearch((char *) pw, (char *) *pw_v, pw_c, sizeof(struct passwd), X uidcmp); X if (*pw_c > old_c) X if (((*pw_v)[*pw_c - 1].pw_name = strdup(pw->pw_name)) == NULL) X fatal(NULL, "Out of memory"); X } X qsort((char *) *pw_v, *pw_c, sizeof(struct passwd), uidcmp); X X return; X} X X/* X * gidcmp - Compares two group ids. X */ Xint gidcmp(gr1, gr2) Xstruct group *gr1, *gr2; X{ X return gr1->gr_gid - gr2->gr_gid; X} X X/* X * ReadEtcGroup - Reads /etc/group file. X */ Xvoid ReadEtcGroup(gr_c, gr_v) Xunsigned *gr_c; Xstruct group **gr_v; X{ X struct group *gr; X unsigned old_c; X X#ifdef DEBUG X (void) fputs("Reading group entries...\n", stderr); X#endif X *gr_c = 0; X *gr_v = (struct group *) 0; X while ((gr = getgrent()) != (struct group *) 0) X { X AddMem(*gr_c, (char **) gr_v, sizeof(struct group)); X old_c = *gr_c; X (void) lsearch((char *) gr, (char *) *gr_v, gr_c, sizeof(struct group), X gidcmp); X if (*gr_c > old_c) X if (((*gr_v)[*gr_c - 1].gr_name = strdup(gr->gr_name)) == NULL) X fatal(NULL, "Out of memory"); X } X qsort((char *) *gr_v, *gr_c, sizeof(struct group), gidcmp); X X return; X} X X/* X * ListFile - Prints a documented listing of a file. X * If "isadir" is a directory "path/file" is used for listing, "path" X * otherwise. X * "pw_c" is the number of struct passwd entries in "pw_v". X * "gr_c" is the number of struct group entries in "gr_v". X * "ActMonth" is the number of Months since 1900. This is used to determine X * the output format of the last modification date of the file. X * "desc" is the descriptive text for the file. It must end with a X * newline. X */ Xint ListFile(path, isadir, file, pw_c, pw_v, gr_c, gr_v, ActMonth, X desc) Xchar *path; Xint isadir; Xchar *file; Xunsigned pw_c; Xstruct passwd *pw_v; Xunsigned gr_c; Xstruct group *gr_v; Xint ActMonth; Xchar *desc; X{ X char buf[PATHLEN + 1]; X struct stat st; X struct passwd *pw, pw_dummy; X struct group *gr, gr_dummy; X long htime; X struct tm *tm; X X if (isadir) X (void) sprintf(buf, "%s/%s", path, file); X else X (void) strcpy(buf, path); X if (stat(buf, &st) == -1) X { X error(buf, NULL); X return -1; X } X if (PrintOwner && pw_c) X { X pw_dummy.pw_uid = st.st_uid; X pw = (struct passwd *) bsearch((char *) &pw_dummy, (char *) pw_v, pw_c, X sizeof(struct passwd), uidcmp); X if (pw != (struct passwd *) 0) X (void) printf("%-8s ", pw->pw_name); X } X if (PrintGroup && gr_c) X { X gr_dummy.gr_gid = st.st_gid; X gr = (struct group *) bsearch((char *) &gr_dummy, (char *) gr_v, gr_c, X sizeof(struct group), gidcmp); X if (gr != (struct group *) 0) X (void) printf("%-8s ", gr->gr_name); X } X (void) printf("%7lu ", (unsigned long) st.st_size); X if (PrintDate) X { X htime = (long) st.st_mtime; X tm = localtime(&htime); X (void) printf("%s %2d ", month[tm->tm_mon], tm->tm_mday); X if (abs(ActMonth - (12 * tm->tm_year + tm->tm_mon)) > 6) X (void) printf("%5d ", 1900 + tm->tm_year); X else X (void) printf("%02d:%02d ", tm->tm_hour, tm->tm_min); X } X (void) printf("%-14s", file); X if (desc) X (void) printf(" %s", desc); X else X (void) putchar('\n'); X X return 0; X} X X/* X * join - Joins the description list and the directory entries in X * order to print a documented listing. X * "path" is the pathname of the file or directory to list. X * "isadir" tells you, if "path" is a directory. X * "listpath" is the pathname of the description list. X * Rest is in "ListFile()". X */ Xint join(path, isadir, listpath, dirc, dir, listc, list, pw_c, pw_v, X gr_c, gr_v, ActMonth) Xchar *path; Xint isadir; Xchar *listpath; Xunsigned dirc; Xchar **dir; Xunsigned listc; Xchar **list; Xunsigned pw_c; Xstruct passwd *pw_v; Xunsigned gr_c; Xstruct group *gr_v; Xint ActMonth; X{ X FILE *fp; X unsigned i, j; X int diff; X int EmptyList = 1; X char *ptr; X X#ifdef DEBUG X if (update) X (void) fprintf(stderr, "Joining and updating \"%s\"\n", listpath); X else X (void) fprintf(stderr, "Joining and listing from \"%s\"...\n", listpath); X#endif X X if (update) X { X if ((fp = fopen(listpath, "w")) == (FILE *) 0) X { X (void) fprintf(stderr, "Cannot open \"%s\" for writing\n", listpath); X return -1; X } X } X i = 0; X j = 0; X while (i < dirc && (!update || j < listc)) X { X if (j < listc) X if (diff = wrdcmp(&dir[i], &list[j])) X ptr = NULL; X else X ptr = skipword(list[j]); X else X { X diff = -1; X ptr = NULL; X } X if (update) X { X if (!diff) X { X EmptyList = 0; X if (fputs(list[j], fp) == EOF) X { X (void) fprintf(stderr, "Can't write to file \"%s\"\n", X listpath); X break; X } X#ifdef DEBUG X (void) fputs(list[j], stderr); X#endif X } X } X else X if (diff <= 0 X && ListFile(path, isadir, dir[i], pw_c, pw_v, gr_c, gr_v, X ActMonth, ptr) == -1) X return -1; X if (diff <= 0) X i++; X if (diff >= 0) X j++; X } X if (update) X { X (void) fclose(fp); X if (EmptyList) X if (!KeepEmptyList) X { X#ifdef DEBUG X (void) fputs("Description list is empty - removing it\n", stderr); X#endif X if (unlink(listpath) == -1) { X error(listpath, "Cannot remove empty description list"); X return -1; X } X } X#ifdef DEBUG X else X (void) fputs("Description list is empty - keeping it\n", stderr); X#endif X } X X return 0; X} X X/* X * ProcessPaths - Processes (lists or updates) all paths. X */ Xint ProcessPaths(pathc, pathv) Xint pathc; Xchar **pathv; X{ X int i; X int err = 0; X unsigned listc = 0; X char **list = (char **) 0; X unsigned dirc = 0; X char **dir = (char **) 0; X char listpath[PATHLEN + 1]; X int isadir; X int sorted = 0; X long htime; X struct tm *tm; X unsigned pw_c; X struct passwd *pw_v; X unsigned gr_c; X struct group *gr_v; X int ActMonth; X X if (PrintOwner) X ReadEtcPasswd(&pw_c, &pw_v); X if (PrintGroup) X ReadEtcGroup(&gr_c, &gr_v); X if (PrintDate) X { X (void) time(&htime); X tm = localtime(&htime); X ActMonth = 12 * tm->tm_year + tm->tm_mon; X } X if (multicast) X { X#ifdef DEBUG X (void) fputc('\n', stderr); X#endif X if (update && access(multicast, 06) == -1) X { X error(multicast, "Cannot read or write description list"); X return 1; X } X ReadList(multicast, &listc, &list); X } X X for (i = 0; i < pathc; i++, pathv++) X { X isadir = isdir(*pathv); X BuildListpath(*pathv, isadir, listpath); X if (update) X { X if (!isadir) X { X err++; X errno = ENOTDIR; X error(*pathv, NULL); X continue; X } X } X else X if (isadir && pathc > 1) X (void) printf("\n%s:\n", *pathv); X if (!multicast) X { X#ifdef DEBUG X (void) fputc('\n', stderr); X#endif X if (update && access(listpath, 06) == -1) X { X err++; X error(listpath, "Cannot read or write description list"); X continue; X } X ReadList(listpath, &listc, &list); X } X if (isadir) X { X if (ReadDir(*pathv, &dirc, &dir) == -1) X { X if (update || !multicast) X { X FreeLines(&listc, &list); X FreeLines(&dirc, &dir); X } X if (update && multicast) X return 1; X continue; X } X } X else X { X AddMem(dirc, dir, sizeof(char *)); X if ((dir[dirc++] = strdup(*pathv)) == NULL) X fatal(NULL, "Out of memory"); X } X if (update) X { X if (!multicast || i == pathc - 1) X { X FormatLines(listc, list); X SortLines(listc, list); X SortLines(dirc, dir); X if (join(*pathv, isadir, listpath, dirc, dir, listc, list, pw_c, X pw_v, gr_c, gr_v, ActMonth) == -1) X err++; X FreeLines(&listc, &list); X FreeLines(&dirc, &dir); X } X } X else X { X if (SortBeforeList && (!multicast || !sorted)) X { X SortLines(listc, list); X sorted = 1; X } X SortLines(dirc, dir); X if (join(*pathv, isadir, listpath, dirc, dir, listc, list, pw_c, X pw_v, gr_c, gr_v, ActMonth) == -1) X err++; X FreeLines(&dirc, &dir); X if (!multicast) X FreeLines(&listc, &list); X } X } X if (PrintOwner) X free((char *) pw_v); X if (PrintGroup) X free((char *) gr_v); X X return err; X} X X/* X * SortPathlist - Sorts the paths. X * Filenames are sorted first, directories last. X * Filenames and directories keep their order of occurence. X */ Xvoid SortPathlist(pathc, pathv) Xunsigned pathc; Xchar **pathv; X{ X int i, j, end; X char *ptr; X X end = pathc; X for (i = 0; i < end; i++) X if (isdir(pathv[i])) X { X ptr = pathv[i]; X for (j = i; j < pathc - 1; j++) X pathv[j] = pathv[j + 1]; X pathv[j] = ptr; X end--; X } X X return; X} X X/* X * BuildPathlist - Builds a list of pathnames to list or update. X */ Xvoid BuildPathlist(optind, argc, argv) Xint optind; Xint *argc; Xchar ***argv; X{ X static char *def[] = { /* Default list of pathnames */ X "." X }; X X if (optind < *argc) X { X *argc = *argc - optind; X *argv = &(*argv)[optind]; X SortPathlist((unsigned) *argc, *argv); X } X else X { X *argc = 1; X *argv = def; X } X X return; X} X Xint main(argc, argv) Xint argc; Xchar **argv; X{ X extern char *optarg; X extern int optind; X int c; X X progname = argv[0]; X X while ((c = getopt(argc, argv, "?UaAugdksl:")) != EOF) X switch(c) X { X case 'U': X update = 1; X break; X case 'k': X if (!update) X usage(); X KeepEmptyList = 1; X break; X case 's': X if (update) X usage(); X SortBeforeList = 1; X break; X case 'a': X if (update || listmode != simple) X usage(); X listmode = all; X break; X case 'A': X if (update || listmode != simple) X usage(); X listmode = allbutdot; X break; X case 'u': X if (update) X usage(); X PrintOwner = 1; X break; X case 'g': X if (update) X usage(); X PrintGroup = 1; X break; X case 'd': X if (update) X usage(); X PrintDate = 1; X break; X case 'l': X multicast = optarg; X break; X default: X usage(); X } X if (!update && listmode == simple && !geteuid()) X listmode = allbutdot; X X BuildPathlist(optind, &argc, &argv); X#ifdef DEBUG X { X int i; X X (void) fputs("Pathnames to list or update:", stderr); X for (i = 0; i < argc; i++) X (void) fprintf(stderr, " %s", argv[i]); X (void) fputc('\n', stderr); X } X#endif /* DEBUG */ X exit(ProcessPaths(argc, argv)); X X return 1; X} SHAR_EOF $TOUCH -am 0204221191 list.c && chmod 0440 list.c || echo "restore of list.c failed" set `wc -c list.c`;Wc_c=$1 if test "$Wc_c" != "19228"; then echo original size 19228, current size $Wc_c fi exit 0 -- Snail: Christian Schlichtherle, Elbscheweg 20, 5802 Wetter 4, Germany Email: chris@attron.ruhr.sub.org Tel.: +49 2335 7550 "Der Tod ist ein Meister aus Deutschland" -- Paul Celan