[alt.sources] dls/describe part 1 of 2

timcc@csv.viccol.edu.au (Tim Cook) (03/23/91)

#!/bin/sh
# This is a shell archive (shar 3.47)
# made 22-Mar-1991 07:48 UTC by timcc@admin.viccol.edu.au
# Source directory /cs/staff/tim/work/dls/dist
#
# existing files will NOT be overwritten unless -c is specified
#
# This is part 1 of a multipart archive                                    
# do not concatenate these parts, unpack them in order with /bin/sh        
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   3622 -rw-r----- README
#   2107 -rw-r----- PORTING
#   1369 -r--r----- Makefile
#  11602 -rw-r----- dls.c
#   8873 -rw-r----- describe.c
#   3043 -rw-r----- list.c
#    669 -rw-r----- list.h
#   1412 -rw-r----- smalloc.c
#   1344 -rw-r----- vclib.h
#   1417 -rw-r----- split_pathname.c
#   1774 -rw-r----- dls.man
#   2094 -rw-r----- describe.man
#   1162 -rw-r----- strpbrk.c
#   1265 -rw-r----- perror2.c
#
if test -r _shar_seq_.tmp; then
	echo 'Must unpack archives in sequence!'
	echo Please unpack part `cat _shar_seq_.tmp` next
	exit 1
fi
# ============= README ==============
if test -f 'README' -a X"$1" != X"-c"; then
	echo 'x - skipping README (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting README (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'README' &&
Description
-----------
Have you ever wondered what on earth "xfrg2.2.tar.Z" was?  Are you
wondering whether "foo" is really a scratch file?  Does it ashame you
especially, seeing that these files were created by you?
X
I have the solution for you...  File Descriptions.
X
I'm sure some operating system or another once had the ability to set
a descriptive comment on a file, but Unix wasn't one of them.  It is
possible, however, to retro-fit onto Unix an implementation that works
quite well.
X
In this package, I have provided a utility to set/delete/list file
descriptions (describe), and a directory listing utility that shows
any file descriptions that are set next to the respective files (dls).
X
Implementation
--------------
The storage/retrieval of file-descriptions is implemented in the
following way:
X
X	All file descriptions are stored in a DBM file called .desc,
X	in  the same directory as the files described.  This in fact
X	means that you have two files, .desc.pag and .desc.dir.
X
X	Each description is indexed by the name of the file (excluding
X	any path information).  To provide a better chance of finding
X	the description after the file has been renamed (within the
X	same directory), the name is also indexed by the inode-number.
X	This means that the file's description will still be found
X	after it has been renamed (within the same directory) or
X	re-written by an editor.  To prevent the two indexes from
X	becoming mixed up, whenever a name/description pair are stored
X	with a name that is the same size as an inode-number, the name
X	is stored with a trailing null byte.
X	
X
Purpose
-------
My reason for coming up with this idea is simple.  I got tired of
logging on to some distant site's anonymous FTP area, and coming
across something like:
X
-rw-r--r--  1 root       214807 May 22  1989 3c503.tar.Z
-rw-r--r--  1 bin         27751 Aug 22 16:35 bonnie
-rw-rw-r--  1 uucp        65383 Aug 23 02:54 dynafeed.tar.Z
-rw-r--r--  1 bin         10444 Nov  3 21:58 fpipe
-rw-r--r--  1 bin         47138 Aug 22 16:41 opaque	(good name!)
-rw-r--r--  1 don        683759 May 29 23:21 psiber.tar.Z
X
You know?  You sit there and think ... "some of those sound
interesting, but it wouldn't be nice for me to download them just to
find out what they are...".
X
If there is no other demand for describe/dls, there should be as a
replacement for ls in anonymous FTP areas.  And that's how simple it
is, once you have dls installed, simply copy it to ~ftp/bin/ls, and
the FTP daemon will use it instead of standard ls for anonymous
logins.  The output of dls was actually designed with anonymous FTP
use in mind (who cares who owns the file or which group it belongs
to?).
X
Of course, the site administrator has to provide meaningful
descriptions for what has been put in the anonymous FTP area.  I don't
think it would be too hard to generate descriptions files from an
index of something like comp.sources.unix, though.  A dozen lines of
perl should do it.
X
An Example
----------
As an example of what an anonymous FTP site that uses dls looks like,
try my back yard, admin.viccol.edu.au.
X
Copyright
---------
The whole system is Copyright (c) Tim Cook 1991, apart from a courtesy
copy of strpbrk.c which is Copyright (c) 1985 Regents of the
University of California.  All together, the package can be
distributed provided no profit is made from any distribution, and all
copyright notices remain intact.
X
The Author
----------
Tim Cook, Systems Administrator, Victoria College Computer Services.
X
I am the administrator of a Sequent S27 (Symmetry),
admin.viccol.edu.au, which runs administrative systems under Oracle.
SHAR_EOF
chmod 0640 README ||
echo 'restore of README failed'
Wc_c="`wc -c < 'README'`"
test 3622 -eq "$Wc_c" ||
	echo 'README: original size 3622, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= PORTING ==============
if test -f 'PORTING' -a X"$1" != X"-c"; then
	echo 'x - skipping PORTING (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting PORTING (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'PORTING' &&
I have not made any effort to port dls/describe to any other version
of Unix (this is because I don't have access to any other versions of Unix).
It runs under DYNIX version 3.0.12, which is based on 4.2 bsd, so it should
be fairly portable.
X
I will list the things that might need consideration when porting:
X
1.   DBM.  The description records are stored using dbm or ndbm.
X     If you want to use ndbm or an ndbm-compatible library, define
X     NDBM when compiling.
X
2.   Directory reading routines.  I have used the opendir/readdir
X     calls that come with DYNIX.  There is a non-commercial version
X     of this for most versions of Unix nowdays.  I would note that
X     the DYNIX version uses a "struct direct".  Some use a
X     "struct dirent".
X
3.   Scanf.  I use scanf to read strings containing white-space.
X     There may be some simpler versions out there that cannot do this
X     (I'm really not sure, though).
X
4.   Uid/gid types.  Newer versions of Unix define uid_t and gid_t,
X     and use them in all situations.  DYNIX makes half an effort, so
X     things aren't too clean on this one.
X
5.   Include files.  I wrote up my own version of string.h ages ago
X     when I started adding some of the more recent str* routines, so I
X     always use that.  It wasn't in DYNIX when we got it, though
X     (strings.h was instead).  There may be other changes necessary.
X
6.   Strchr.  The worlds most common porting consideration.
X
7.   strpbrk.  Wasn't in DYNIX.  I have provided source for others who
X     don't already have it.
X
8.   I have made a fair bit of use of void in the source.  If your
X     compiler can't handle void (I imagine there are few such compilers
X     left in the world), you will have to make adjustments for this.
X
Of course, I am interested in seeing the results of any porting efforts,
so that I can incorporate them in later releases.  Depending on the level
of my spare time, I would even be prepared to assist you in porting this
to your particular brand of Unix.  I have a desire to see this propagate
as far as possible around the world of Unix anonymous FTP sites.
SHAR_EOF
chmod 0640 PORTING ||
echo 'restore of PORTING failed'
Wc_c="`wc -c < 'PORTING'`"
test 2107 -eq "$Wc_c" ||
	echo 'PORTING: original size 2107, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
# Makefile -	Makefile for dls
#
# Copyright (c) 1991 Tim Cook.
# Non-profit distribution allowed.  See README for details.
#
# $Header: Makefile 1.1 91/03/22 $
X
# DBM is the default if NDBM is not defined
DEFINES =	-DNDBM
X
# This may need tweaking (there is a choice between dbm and ndbm at least)
LIBS =		-lndbm
#LIBS =		-ldbm -lseq
X
# Take out strpbrk.o if you have it in a library
EXTRAS =	strpbrk.o perror2.o
X
# "root" is a local command that provides su privilege to specific users
INSTALL = root install
X
SHAR =		shar -w
X
#COPT =		-g
COPT =		-O
CFLAGS =	$(COPT) -I. $(DEFINES)
LDFLAGS =	$(COPT)
SOURCE =	dls.c describe.c list.c list.h smalloc.c vclib.h \
X		split_pathname.c dls.man describe.man strpbrk.c perror2.c
X
all : dls describe
X
doc : dls.1 describe.1
X
install :
X	$(INSTALL) -c -s -o bin -g cs -m 751 dls /usr/local/bin
X	$(INSTALL) -c -s -o bin -g cs -m 751 describe /usr/local/bin
X
shar :
X	$(SHAR) README PORTING Makefile $(SOURCE) > dls.shar
X
dls : dls.o list.o smalloc.o split_pathname.o
X	$(CC) $(LDFLAGS) -o $@ dls.o list.o smalloc.o split_pathname.o $(LIBS)
X
describe : describe.o split_pathname.o $(EXTRAS)
X	$(CC) $(LDFLAGS) -o $@ describe.o split_pathname.o $(EXTRAS) $(LIBS)
X
dls.c :	list.h
X
list.c : list.h
X
strpbrk.o : strpbrk.c
X	$(CC) -c -O $*.c
X
dls.1 :	dls.man
X	nroff -man dls.man > $@
X
describe.1 : describe.man
X	nroff -man describe.man > $@
SHAR_EOF
chmod 0440 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 1369 -eq "$Wc_c" ||
	echo 'Makefile: original size 1369, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dls.c ==============
if test -f 'dls.c' -a X"$1" != X"-c"; then
	echo 'x - skipping dls.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting dls.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'dls.c' &&
/* dls.c -	Descriptive ls
X *
X * Copyright (c) 1991 Tim Cook.
X * Non-profit distribution allowed.  See README for details.
X */
X
static char rcsid[] = "$Header: dls.c 1.0 91/03/22 $" ;
X
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#ifdef NDBM
#include <ndbm.h>
#else
#if defined(sequent) && defined(NULL)
#undef NULL
#endif
#include <dbm.h>
#endif
X
#include <vclib.h>
#include <list.h>
X
#define UID_TYPE	unsigned short
#define GID_TYPE	unsigned short
X
extern time_t time () ;
extern void *smalloc () ;
extern int getuid () ;
extern int getgroups () ;
X
extern char *optarg ;
extern int optind, opterr ;
X
#define NULL_CP	((char *) 0)
#define EOS	'\0'
X
static struct file_info {
X   char *name ;
X   ino_t inode ;
X   u_short mode ;
X   short nlink ;
X   off_t size ;
X   time_t mtime ;
X   } ;
X
/* options */
static int show_date = FALSE ;
static int sort_by_date = FALSE ;
static int even_inaccessible = FALSE ;
static int filename_columns = 14 ;
X
/* other globals */
#ifdef NDBM
static DBM *desc_database ;
#endif
static time_t six_months_ago ;
static struct file_info not_there ;	/* Symbolic value */
static int name_and_size_columns ;
static int space = FALSE ;		/* If TRUE, we need to print a blank
X					   line before anything else */
static int printed_anything = FALSE ;
static int uid ;
static int no_groups ;
static int groups[NGROUPS] ;
X
static void *get_element_from_argv () ;
static void build_and_sort_list () ;
static struct file_info *get_file_info () ;
static void list_file () ;
static void list_directory () ;
X
X
int main (argc, argv)
X   int argc ;
X   char **argv ;
{
X   int option ;
X   struct list file_list ;
X   struct file_info *file_info ;
X
X   opterr = 0 ;
X   while ((option = getopt (argc, argv, "detf:")) != EOF) {
X      switch ((char) option) {
X         case 'd' :
X	    show_date = TRUE ; break ;
X         case 'e' :
X	    even_inaccessible = TRUE ; break ;
X         case 't' :
X	    sort_by_date = TRUE ; break ;
X         case 'f' :
X	    filename_columns = atoi (optarg) ; break ;
X	 case '?' :
X            break ; } }
X
X   name_and_size_columns = filename_columns + 8 ;
X
X   if (show_date)
X      six_months_ago = time ((time_t *) 0) - (182 * 24 * 60 * 60) ;
X
X   if (! even_inaccessible) {
X      uid = (UID_TYPE) getuid () ;
X      no_groups = getgroups (NGROUPS, groups) ; }
X
X   if (optind - argc == 0) {
X      list_directory (".", FALSE) ;
#ifdef NDBM
X      dbm_close (desc_database) ;
#else
X      dbmclose () ;
#endif
X      exit (0) ; }
X
X   list_init (&file_list) ;
X   build_and_sort_list (&file_list, get_element_from_argv, (char *) argv,
X      (char *) &optind) ;
X
X   while ((file_info = get_file_info (&file_list))
X	  != (struct file_info *) NULL) {
X      if (file_info != &not_there) {
X	 if (file_info->mode & S_IFDIR)
X	    list_directory (file_info->name, TRUE) ;
X	 else {
X	    list_file (file_info, FALSE) ; } } }
X   }
X
X
static
void *get_element_from_argv (param_1, param_2)
X   char *param_1, *param_2 ;
{
X   int *argc ;
X   char **argv ;
X
X   argv = (char **) param_1 ;
X   argc = (int *) param_2 ;
X   return (void *) argv[(*argc)++] ;
X   }
X
X
static
int file_info_date_cmp (info_1, info_2)
X   struct file_info **info_1, **info_2 ;
{
X   return (int) ((*info_2)->mtime - (*info_1)->mtime) ;
X   }
X
X
static
int compare_str (str_1, str_2)
X   char **str_1, **str_2 ;
{
X   return strcmp (*str_1, *str_2) ;
X   }
X
X
static
void build_and_sort_list (file_list, get_element, param_1, param_2)
X   struct list *file_list ;
X   void * (*get_element) () ;
X   char *param_1, *param_2 ;
{
X   struct stat file_status ;
X   struct file_info *file_info ;
X   char *name ;
X
X   if (sort_by_date) {
X      while ((name = (char *) get_element (param_1, param_2)) != NULL_CP) {
X	 if (stat (name, &file_status) == -1)
X	    perror (name) ;
X	 else {
X	    file_info =
X	       (struct file_info *) smalloc (sizeof (struct file_info)) ;
X	    file_info->name = name ;
X	    file_info->inode = file_status.st_ino ;
X	    file_info->mode = file_status.st_mode ;
X	    file_info->nlink = file_status.st_nlink ;
X	    file_info->size = file_status.st_size ;
X	    file_info->mtime = file_status.st_mtime ;
X	    list_push (file_list, (void *) file_info) ; } }
X      list_sort (file_list, file_info_date_cmp) ; }
X
X   else {	/* Sort by name */
X      while ((name = (char *) get_element (param_1, param_2)) != NULL_CP) {
X	 list_push (file_list, (void *) name) ; }
X      list_sort (file_list, compare_str) ; }
X   }
X
X
static
struct file_info *get_file_info (file_list)
X   struct list *file_list ;
{
X   static struct file_info file_info ;
X   static struct stat status ;	/* Tongue twister */
X   char *name ;
X   int listable ;
X   int n ;
X
X   if (sort_by_date)
X      return (struct file_info *) list_shift (file_list) ;
X   else {
X      name = (char *) list_shift (file_list) ;
X      if (name == (char *) NULL)
X	 return (struct file_info *) NULL ;
X      else
X	if (stat (name, &status) == -1) {
X	   perror (name) ;
X	   return &not_there ; }
X	else {
X	   if (! even_inaccessible) {
X	      if (status.st_uid == (UID_TYPE) uid)
X		 listable = status.st_mode & 0500 ;
X	      else {
X		 for (n = 0 ; n < no_groups ; n++) {
X		    if (status.st_gid == (GID_TYPE) groups[n]) {
X		       listable = status.st_mode & 050 ;
X		       n = no_groups + 1 ; } }
X		 if (n == no_groups)
X		    listable = status.st_mode & 05 ; } }
X	   else
X	      listable = TRUE ;
X	   if (! listable)
X	      return &not_there ;
X	   else {
X	      file_info.name = name ;
X	      file_info.inode = status.st_ino ;
X	      file_info.mode = status.st_mode ;
X	      file_info.nlink = status.st_nlink ;
X	      file_info.size = status.st_size ;
X	      file_info.mtime = status.st_mtime ;
X	      return &file_info ; } } }
X   }
X
X
static void *get_element_from_dir () ;
X
X
static
void list_directory (name, show_directory)
X   char *name ;
X   int show_directory ;
{
X   DIR *directory ;
X   struct list file_list ;
X   struct file_info *file_info ;
X
X   space = FALSE ;
X   if (show_directory) {
X      if (printed_anything) {
X	 printc ('\n') ; }
X      printf ("%s:\n", name) ;
X      printed_anything = TRUE ; }
X
X   if ((directory = opendir (name)) == (DIR *) NULL) {
X      perror (name) ; }
X   else {
X      list_init (&file_list) ;
X      build_and_sort_list (&file_list, get_element_from_dir,
X	 (char *) directory, name) ;
X      while ((file_info = get_file_info (&file_list))
X	     != (struct file_info *) NULL) {
X	 if (file_info != &not_there)
X	    list_file (file_info, TRUE) ; }
X      list_free (&file_list) ;
X      closedir (directory) ; }
X   space = TRUE ;
X   }
X
X
static
void *get_element_from_dir (param_1, param_2)
X   char *param_1 ;
X   char *param_2 ;
{
X   char *p ;
X   struct direct *dir_entry ;
X   int n ;
X
X   for (dir_entry = readdir ((DIR *) param_1) ;
X        dir_entry != (struct direct *) NULL && dir_entry->d_name[0] == '.' ;
X	dir_entry = readdir ((DIR *) param_1)) ;
X   if (dir_entry == (struct direct *) NULL)
X      return (void *) NULL ;
X
X   n = strlen ((char *) param_2) + dir_entry->d_namlen + 2 ;
X   p = (char *) smalloc ((size_t) n) ;
X   strcpy (p, (char *) param_2) ;
X   strcat (p, "/") ;
X   strncat (p, dir_entry->d_name, dir_entry->d_namlen) ;
X   p[n] = EOS ;
X   return (void *) p ;
X   }
X
X
static
char *get_description (pathname, inode_ptr)
X   char *pathname ;
X   ino_t *inode_ptr ;
{
X   static char desc_file[MAXNAMLEN+1] = "" ;
X   static char new_desc_file[MAXNAMLEN+1] ;
X   static char file_name[MAXNAMLEN+1] ;
X   static datum key, value ;
X   static int no_desc_file = TRUE ;
X   static int desc_not_found ;
X   struct stat status ;
X   char *p ;
X
X   split_pathname (pathname, new_desc_file, file_name) ;
X
X   if (no_desc_file || strcmp (new_desc_file, desc_file) != 0) {
X      strcpy (desc_file, new_desc_file) ;
X      if (*new_desc_file != EOS)
X	 strcat (new_desc_file, "/") ;
#ifdef NDBM
X      strcat (new_desc_file, ".desc") ;
X      dbm_close (desc_database) ;
X      if ((desc_database = dbm_open (new_desc_file, O_RDONLY, 0))
X            == (DBM *) NULL)
X	 desc_not_found = TRUE ;
X      else
X	 no_desc_file = desc_not_found = FALSE ;
#else
X      strcat (new_desc_file, ".desc.pag") ;
X      dbmclose () ;
X      no_desc_file = TRUE ;
X      /* Check this ourselves to prevent dbm from complaining */
X      if (stat (new_desc_file, &status) == 0) {
X	 p = strrchr (new_desc_file, '.') ;
X	 *p = EOS ;
X	 dbminit (new_desc_file) ;
X         no_desc_file = desc_not_found = FALSE ; }
X      else {
X         desc_not_found = TRUE ; }
#endif	/* NDBM */
X      }
X   if (desc_not_found) {
X      return NULL_CP ; }
X   else {
X      /* Check for it by name */
X      key.dptr = file_name ;
X      key.dsize = strlen (file_name) ;
X      if (key.dsize == sizeof (ino_t))
X	 key.dsize++ ;
#ifdef NDBM
X      value = dbm_fetch (desc_database, key) ;
#else
X      value = fetch (key) ;
#endif
X      if (value.dptr == NULL_CP) {
X	 /* Check for it by inode */
X	 key.dptr = (char *) inode_ptr ;
X	 key.dsize = sizeof (ino_t) ;
#ifdef NDBM
X	 value = dbm_fetch (desc_database, key) ;
#else
X	 value = fetch (key) ;
#endif
X	 if (value.dptr != NULL_CP) {
X	    /* Now use the name we got using the inode */
X	    key.dptr = value.dptr ;
X	    key.dsize = value.dsize ;
#ifdef NDBM
X	    value = dbm_fetch (desc_database, key) ;
#else
X	    value = fetch (key) ;
#endif
X	    } }
X      return (char *) value.dptr ; }
X   }
X      
X
X
static
void list_file (file_info, doing_directory)
X   struct file_info *file_info ;
X   int doing_directory ;
{
X   static char month[][12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
X      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} ;
X   char *p ;
X   static char file_name[MAXNAMLEN+1] ;
X   char *desc ;
X   static char size[12] ;
X   static char date[16] ;
X   int name_len, size_len, gap ;
X   struct tm *tm_time ;
X
X   strcpy (file_name, file_info->name) ;
X   p = file_name ;
X   if (! doing_directory && p[0] == '.' && p[1] == '/' && p[2] != EOS)
X      p += 2 ;
X   desc = get_description (p, &(file_info->inode)) ;
X   if (doing_directory)
X      p = strrchr (p, '/') + 1 ;
X
X   name_len = strlen (p) ;
X   /* If it is a directory, make it obvious */
X   if (file_info->mode & S_IFDIR) {
X      strcat (p, "/") ;
X      name_len++ ;
X      size[1] = EOS ;
X      size_len = 1 ;
X      if (file_info->nlink > 2) {	/* Denotes a directory that contains */
X	 size[0] = '=' ; }		/* one or more sub-directories */
X      else {
X	 size[0] = '-' ; } }
X   else {
X      sprintf (size, "%d", file_info->size) ;
X      size_len = strlen (size) ; }
X   if (name_len + size_len < name_and_size_columns)
X      gap = name_and_size_columns - name_len ;
X   else {
X      print (p) ;
X      p[0] = '\n' ; p[1] = EOS ;
X      gap = name_and_size_columns ; }
X   if (show_date) {
X      tm_time = localtime (&file_info->mtime) ;
X      if (six_months_ago > file_info->mtime) {
X	 /* The bloody thing is more than 6 months old! */
#ifdef US_DATE_FORMAT
X	 sprintf (date, " %s %2d  %d ", month[tm_time->tm_mon],
X	    tm_time->tm_mday, tm_time->tm_year + 1900) ;
#else
X	 sprintf (date, " %2d %s  %d ", tm_time->tm_mday,
X	    month[tm_time->tm_mon], tm_time->tm_year + 1900) ;
#endif
X         }
X      else {
#ifdef US_DATE_FORMAT
X	 sprintf (date " %s %2d %02d:%02d ", month[tm_time->tm_mon],
X	    tm_time->tm_mday, tm_time->tm_hour, tm_time->tm_min) ;
#else
X	 sprintf (date, " %2d %s %02d:%02d ", tm_time->tm_mday,
X	     month[tm_time->tm_mon], tm_time->tm_hour, tm_time->tm_min) ;
#endif
X      } }
X   else {
X      date[0] = ' ' ;
X      date[1] = EOS ; }
X
X   if (space) {
X      printc ('\n') ;
X      space = FALSE ; }
X   printf ("%s %*s%s %s\n", p, gap, size, date, desc) ;
X   printed_anything = TRUE ;
X   }
SHAR_EOF
chmod 0640 dls.c ||
echo 'restore of dls.c failed'
Wc_c="`wc -c < 'dls.c'`"
test 11602 -eq "$Wc_c" ||
	echo 'dls.c: original size 11602, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= describe.c ==============
if test -f 'describe.c' -a X"$1" != X"-c"; then
	echo 'x - skipping describe.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting describe.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'describe.c' &&
/* describe.c -	Set or list a descriptive comment for a file
X *
X * Copyright (c) 1991 Tim Cook.
X * Non-profit distribution allowed.  See README for details.
X */
X
static char rcsid[] = "$Header: describe.c 1.0 91/03/22 $" ;
X
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <sys/stat.h>
#ifdef NDBM
#include <ndbm.h>
#else
#if defined(sequent) && defined(NULL)
#undef NULL	/* On the Sequent, dbm.h #define's NULL */
#endif
#include <dbm.h>
#endif
#include <vclib.h>
X
#ifdef sequent
#define strrchr	rindex
#endif
X
#define NULL_CP	((char *) 0)
#define EOS	'\0'
X
static char *program_name ;
#ifdef NDBM
static DBM *desc_database ;
#endif
X
static void usage () ;
static int init_desc_file () ;
static void set_description () ;
static void set_from_file () ;
static void delete_description () ;
static void list_descriptions () ;
X
X
int main (argc, argv)
X   int argc ;
X   char **argv ;
{
X   extern char *optarg ;
X   extern int optind, opterr ;
X   char directory[MAXNAMLEN+1] ;
X   char file_name[MAXNAMLEN+1] ;
X   int delete_desc = FALSE ;
X   struct stat status ;
X   int option ;
X   char *p ;
X
X   program_name = argv[0] ;
X   while ((option = getopt (argc, argv, "dls:")) != EOF) {
X      switch (option) {
X	 case 's':	/* Set descriptions from file */
X	    if (delete_desc)
X	       usage () ;
X	    if (optind >= argc)
X	       p = "." ;
X	    else
X	       p = argv[optind] ;
X	    init_desc_file (p, FALSE) ;
X	    set_from_file (optarg, p) ;
#ifdef NDBM
X	    dbm_close (desc_database) ;
#else
X	    dbmclose () ;
#endif
X	    exit (0) ;
X	    break ;
X         case 'l':	/* List directory */
X	    if (delete_desc)
X	       usage () ;
X	    if (optind >= argc)
X	       p = "." ;
X	    else
X	       p = argv[optind] ;
X	    if (! init_desc_file (p, TRUE)) {
X	       fprintf (stderr, "%s: no descriptions for directory %s\n",
X		  program_name, p) ;
X	       exit (2) ; }
X	    list_descriptions () ;
#ifdef NDBM
X	    dbm_close (desc_database) ;
#else
X	    dbmclose () ;
#endif
X	    exit (0) ;
X	    break ;
X         case 'd':	/* Delete description(s) */
X	    delete_desc = TRUE ;
X	    break ;
X         default:
X	    usage () ; } }
X
X   if (delete_desc) {
X      if (optind >= argc)
X	 usage () ;
X      for ( ; optind < argc ; optind++) {
X	 split_pathname (argv[optind], directory, file_name) ;
X	 if (stat (argv[optind], &status) == -1) {
X	    perror2 (program_name, argv[optind]) ; }
X	 else {
X	    init_desc_file (directory, FALSE) ;
X	    delete_description (file_name, status.st_ino) ; } } }
X   else {
X      if (argc != 3)
X	 usage () ;
X
X      /* Set description of specified file */
X      split_pathname (argv[1], directory, file_name) ;
X      if (stat (argv[1], &status) == -1) {
X	 perror2 (program_name, argv[1]) ;
X	 exit (2) ; }
X      init_desc_file (directory, FALSE) ;
X      set_description (file_name, status.st_ino, argv[2]) ; }
X
#ifdef NDBM
X   dbm_close (desc_database) ;
#else
X   dbmclose () ;
#endif
X   }
X
X
static
void usage ()
{
X   fprintf (stderr, "\
usage: %s <file> <description>\n\
X   or: %s -s <descriptions-file> [<directory>]\n\
X   or: %s -d <file> ...\n\
X   or: %s -l [<directory>]\n",
X      program_name, program_name, program_name, program_name) ;
X   exit (1) ;
X   }
X
X
static
void set_from_file (file, directory)
X   char *file ;
X   char *directory ;
{
X   FILE *descriptions ;
X   char file_name[MAXNAMLEN+1] ;
X   char buffer[1024] ;
X   char description[256] ;
X   struct stat status ;
X   int n ;
X
X   if (strcmp (file, "-") == 0)
X      descriptions = stdin ;
X   else {
X      if ((descriptions = fopen (file, "r")) == (FILE *) NULL) {
SHAR_EOF
true || echo 'restore of describe.c failed'
fi
echo 'End of  part 1'
echo 'File describe.c is continued in part 2'
echo 2 > _shar_seq_.tmp
exit 0