Article 13402 of alt.sources:
Archive-name: els-1.29
Submitted-by: markb@elen.utah.edu

ELS is an Enhanced "ls" look-alike with many additional features.  Besides
supporting most of what /bin/ls does, ELS also allows you to specify your own
output format for directory listings and it supports many different time
formats (e.g. Ascii, ISO-3307, Absolute/relative time in days, months, years,
etc.).  ELS allows you to specify absolute file paths and names so that the
output can be further manipulated by /bin/sort, etc.  ELS has a built-in
checksumming ability so that files can be listed along with their checksums.
ELS has a "untouch" facility which will allow you to recover the modification
dates following a "touch".  Another use for ELS is that if linked statically
(i.e. no shared libraries), it can be used in place of /bin/ls for anonymous
FTP.

ELS will even correctly display the dates of files created after Jan 19
03:14:08 2038 GMT (most Unixes' /bin/ls will croak when listing files
created beyond this date).

NEW: Included in this release for the first time is 'edate' which allows
you to display the current time, set the system clock, or convert any
date format to any other format.  As expected, 'edate' will allow you to
set your system clock beyond the year 2000 (and if you're really warped
you can use it to set your system clock beyond the year 2038).  'edate'
can also be used for converting between ISO-3307, verbose-style, and raw
unix-clock values.

Perhaps the best reason I can give for recommending els is that it can
list files using ISO-3307 dates along with full path names.  What you
could do with such output is to pipe it through "sort" and then you would
have a listing from the oldest to the most recent files throughout your
entire directory.  This can be done using "els" by typing:

	els +MR /home/markb | sort | more

If you prefer seeing access times instead of creation dates (which is
a handy way of finding files that no one is using), try doing this:

	els +MMR /home/markb | sort | more

Keep in mind that with "els" you can make your directory listings
appear in any format you desire (you can change the order of the
permissions, owners, sizes, change the time format, change the file
name format, etc.).

#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 05/23/1996 05:03 UTC by markb@orpheus
# Source directory /tmp_mnt/home/chaos1/markb/src/els/release-1.29
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#  18920 -rw-rw-r-- els/README
#   4048 -rw-rw-r-- els/chdate.1
#   5123 -rw-rw-r-- els/edate.1
#   7309 -rw-rw-r-- els/els.1
#   1554 -rwxrwxr-x els/untouch
#   8093 -rw-rw-r-- els/chdate.c
#  18009 -rw-rw-r-- els/edate.c
#  66869 -rw-rw-r-- els/els.c
#   1551 -rw-rw-r-- els/defs.h
#  14356 -rw-rw-r-- els/getdate32.c
#   1385 -rw-rw-r-- els/getdate32.h
#   5212 -rw-rw-r-- els/time32.c
#   1283 -rw-rw-r-- els/time32.h
#   2162 -rw-rw-r-- els/Makefile
#
# ============= els/README ==============
if test ! -d 'els'; then
    echo 'x - creating directory els'
    mkdir 'els'
fi
if test -f 'els/README' -a X"$1" != X"-c"; then
	echo 'x - skipping els/README (File already exists)'
else
echo 'x - extracting els/README (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/README' &&
File: els/README
Last updated: May 17, 1996
X
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X
ELS is an Enhanced LS look-alike with many additional features.  Besides
supporting most of what /bin/ls does, ELS also allows you to specify your own
output format for directory listings and it supports many different time
formats (e.g. Ascii, ISO-3307, Absolute/relative time in days, months, years,
etc.).  ELS allows you to specify absolute file paths and names so that the
output can be further manipulated by /bin/sort, etc.  ELS has a built-in
checksumming ability so that files can be listed along with their checksums.
ELS has a "untouch" facility which will allow you to recover the modification
dates following a "touch".  Another use for ELS is that if linked statically
(i.e. no shared libraries), it can be used in place of /bin/ls for anonymous
FTP.
X
ELS will even correctly display the dates of files created after Jan 19
03:14:08 2038 GMT (most Unixes' /bin/ls will croak when listing files
created beyond this date).
X
X	"This is a truly unique program that everyone must have."
X	"Two thumbs-up", say Siskel and Ebert.
X
X
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X
SUPPORTED ARCHITECTURES/OS:
X
This program is known to compile and execute under Linux, SunOS 4.x, SunOS 5.x
(aka Solaris 2), Ultrix, HPUX 9.x, RS6000/AIX, Dynix, and ISC 3.x.  ELS will
probably compile under most other Unixes as well; however, if your Unix is
ancient then you may need to compile with NO_ST_BLOCKS defined as older Unixes
usually do not have the st_blocks element defined in their <sys/stat.h> file.
X
If your Unix supports the timelocal() and timegm() functions then compiling
with HAVE_TIMELOCAL defined is helpful but not required.  Likewise, if your
Unix has the tm_zone element defined in <time.h> file then compiling with
HAVE_TM_ZONE defined is recommended.
X
Note: The most recent version of this program may be obtained from
ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X
Usage: els [-aAcdFgilLnoprRstu] [+HB5D] [+GTN format] [+F char] \
X	[+IEie pattern] [+S char] [+U] files
X
ls-like options:
X
X    -a: List all files including . and ..
X    -A: List all files except . and ..
X    -c: List time of last status change(BSD & SYS5) and sort(BSD)
X    -d: List directories as files, but don't list their contents
X    -F: Mark files
X    -g: List group(BSD), long listing omitting UID(SYS5)
X    -i: List inode number
X    -l: Long listing using BSD or SYS5 semantics
X    -L: List actual file rather than symbolic link
X    -n: Long listing using numeric UIDs and GIDs(SYS5)
X    -o: Long listing omitting GIDs(SYS5)
X    -p: Mark directories
X    -r: Reverse the sort
X    -R: Recursively list contents of all directories
X    -s: List size in blocks(SYS5) or in KBytes(BSD)
X    -t: Sort files according to time
X    -u: List time of last access
X
enhanced ls options:
X
X    +H: Give HELP
X    +B: Use BSD semantics
X    +5: Use SYS5 semantics
X    +D: Don't list directories as files, but list their contents (has the
X	opposite effect of -d)
X
X    +G: GENERAL format, e.g.:  els +G~tp~lusmnL (same as els -l)
X	i:  inode number
X      t,T:  type of file in alpha, symbolic
X      p,P:  permission in alpha, numeric
X      l,L:  link count, name
X      u,U:  UID in alpha, numeric
X      g,G:  GID in alpha, numeric
X      s,S:  size in bytes, blocks
X    m,a,c:  time modified, accessed, status changed
X      n,N:  name of file, path/file
X	C:  16 bit checksum (performed on regular files only and
X	    access times are preserved)
X
X    +T: TIME format, e.g.:  els -la +TRD (give age of file in days)
X	N:  Modifier for Numeric dates instead of alpha
X	A:  Modifier for Absolute time since the epoch
X	R:  Modifier for Relative time from now
X	G:  Modifier for GMT dates instead of Local
X	L:  Modifier for Local dates instead of GMT
X	I:  Iso3307 style (same as +TN~YMD.hms~)
X	e:  els style (default, same as +TMDY2)
X	l:  ls style (same as +TMDy)
X	v:  verbose style (same as +TWMD3ZY)
X  Y,M,D,W:  year, month, day, weekday
X    h,m,s:  hour, minutes, seconds
X      2,3:  time as h:m, h:m:s
X	y:  year or time depending on age of file
X	Z:  zone name
X
X    +N: NAME format, e.g.:  els -laR +NF
X	F:  Full file name (same as +Nd/f)
X	d:  Directory name
X	f:  File name
X
X    G, T, and N format options also recognize the following modifiers:
X	\:  Output following character verbatim
X	~:  Toggle spacing off/on
X       %n:  Output a right justified field 'n' characters wide
X      %-n:  Output a left justified field 'n' characters wide
X    "",'':  Output contents of quoted string verbatim
X	Other characters not recognized by the format are output verbatim
X
X    +F: Field separator character, e.g.:  els -la +F: +TI
X
X    +I: Include specified files, e.g.:  els -lagR +I"*.c"
X    +E: Exclude specified files, e.g.:  els -lagR +E"*.[ao]"
X    +i: Include specified directories, e.g.:  els -lagR +i"[a-m]*"
X    +e: Exclude specified directories, e.g.:  els -lagR +e"SCCS"
X
X    +S: STAMP: The output from this option is used for recording the state
X	of a hierarchy for future comparison to determine any changes.
X
X	C:  Stamp a source code hierarchy excluding *.[ao], *.ln, *~,
X	    and SCCS/RCS files, e.g.:  els +SC -R /home/myfiles/src
X	c:  Same as +SC, except nothing is excluded
X
X	O:  Stamp an operating system hierarchy (no files are excluded and
X	    more detail given), e.g.:  els +SO / -R /etc /dev /usr /var
X	o:  Same as +SO, except checksumming is also performed
X
X    +U: UNTOUCH: Create a script which can be saved into a file for later
X	recovery of modification dates following a /bin/touch, e.g.:
X	    els +U -R * > untouch  # Create untouch script file
X	    /bin/touch *           # Clobber modification dates
X	    sh untouch             # Recover modification dates
X
X
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X
INTRODUCTION:
X
The reason that ELS was originally written was so that consistent dates
could be generated and saved into a file and that these dates could be
compared at a later date to determine which files had changed.  /bin/ls
is unsuited for this purpose as sometimes it lists the date/time and
other times it lists the date/year depending on the age of the file.  ELS
always lists the date/year/time regardless of when the file was created.
X
For example, els -lag will *always* list files as:
X	drwxrwsr-x 1 markb   markb    12345 Feb 29 1996 12:21 leap.year
X
Whereas /bin/ls -lag will *sometimes* list files as:
X	drwxrwsr-x 1 markb   markb    12345 Feb 29 12:21 leap.year
X
and *sometimes* /bin/ls -lag will list files as:
X	drwxrwsr-x 1 markb   markb    12345 Feb 29  1996 leap.year
X
X
ELS's main feature is that it allows you to specify the format of the output
using the generic format (+G).  ELS also supports formats for specifying the
time format (+T) and for specifying the filename format (+N).
X
X
--------
You can obtain most of the same information with ELS that you can using
/bin/ls.  For example, the following command will give you results similar
to /bin/ls:
X	els -lagR /home/markb
X
If full path names are desired, you can accomplish this as follows:
X	els -lagR +NF /home/markb
X
If you prefer, you can even list these files in ISO-3307 format using
full file names as follows:
X	els -lagR +TI +NF /home/markb/*
X
(ISO-3307 format is defined as YYYYMMDD.hhmmss and is available using the
"+TI" option.  The advantage of using ISO-3307 format is obvious once you
realize that /bin/sort can more easily deal with dates such as
"19960229.122101" than it can with "Feb 29, 1996 12:21:01".)
X
X
Any combination of output format can be accomplished using the +G generic
format.  For example, the command:
X	els -lagR
X
is equivalent to:
X	els +G~tp~lugmnL -aR
X
The '~' is necessary to prevent spacing between the 't' (file type) and
the 'p' (file permissions) field.  Appropriate spacing is normally
inserted between all fields, but occasionally it is desirable to suppress
spacing between certain fields.  Try executing the above command both with
and without the '~' and the difference should be obvious.
X
X
If you prefer, you can list the access time instead of the modification
time using the +G generic format:
X	els +G~tp~luganL -R /home/markb/*
X
X
With ELS you can also list *both* the access and the modification times
as follows:
X	els +G~tp~lugamnL -R /home/markb/*
X
X
If you combine ELS's +G generic format along with its ability to generate
ISO-3307 formatted dates and full path names, you can now do useful things
such as search for the most recently *modified* files in a source-code
hierarchy:
X	els +Gmn +TI +NF -R /home/markb/src | sort | tail
X
X
Another useful example might be to find which files have been most
recently *accessed* in /usr/local/bin:
X	els +Gan +TI +NF -R /usr/local/bin | sort | more
X
This is handy way for locating those god-awful ancient programs that no
one ever uses.
X
X
Since it is often desirable to list the full pathname, ELS allows a
shorthand way of doing this via the +G generic format:
X	els +Gan +NF
X
is equivalent to:
X	els +GaN
X
X
--------
ELS allows you to generate time RELATIVE to now.  For example, you can
list files by the number of weeks since they were last *modified*:
X	els +GmN +TRW -R /usr/local/bin
X
Or you can list files by the number of days since they were last *accessed*
(this is very useful for finding files that no one is using):
X	els +GaN +TRD -R /usr/local/bin
X
ELS even also allows you to generate ABSOLUTE time since the epoch.  For
example, you can list files by the number of seconds since the epoch:
X	els +GmN +TAs -R /usr/local/bin
X
X
The following script shows an example of how this RELATIVE time feature
can be used as a filter to search for large files that haven't been
accessed or modified in a given number of days.  For example, suppose we
wish to search for all files greater than 100K bytes that have not been
accessed within the last 180 days and were modified over 365 days ago:
X
X	#!/bin/sh
X	echo "    Size   Acc   Mod File"
X	echo " =======  ====  ==== ======"
X	els +F" " +GsamN +TRD +D -AR | awk -F" " '
X		{if ($1 >= 100000 && $2 > 180 && $3 > 365)
X			{printf "%8d%6d%6d %s\n", $1, $2, $3, $4};
X		}'
X
X
--------
By specifying the +N format you can separate the pathname and the
filename components.  Suppose you want a sorted list of all files in
a hierarchy, but you want this list sorted by the file's *first* name
followed by the directory from whence each file originated (this is
handy for finding which files in a hierarchy have the same name):
X	els +N"f d" -R | sort | more
X
The +N"f d" format forces a space to follow the filename so that the
listing is broken into two fields.
X
Two other examples of forcing a space might be:
X	els -la +T"NM/D/Y h:m"	(generate U.S. style dates)
X	els -la +T"ND,M,Y h:m"	(generate European style dates)
X
X
--------
ELS allows you to selectively include/exclude files using wildcard patterns.
For example, here is how you can EXCLUDE listing all files in a hierarchy
ending in .o, .a, or .Z:
X	els -laR +E"*.[oaZ]" /home/markb/src
X
Here is how you can INCLUDE all files in a hierarchy ending in .c:
X	els -laR +I"*.c" /home/markb/src
X
X
Here is an example of a series of ELS commands that could be used to
generate a script file containing all the mkdir, chown, and chmod commands
for taking the directory hierarchy of your current working directory and
then recreating a skeleton copy in /tmp:
X
X	els +G'"mkdir %N"' +E'*' -R 	>! /tmp/mk-skeleton
X	els +G'"chown %u%N"' +E'*' -R   >> /tmp/mk-skeleton
X	els +G'"chgrp %g%N"' +E'*' -R   >> /tmp/mk-skeleton
X	els +G'"chmod %P%N"' +E'*' -R   >> /tmp/mk-skeleton
X	cd /tmp; sh mk-skeleton
X
Observe that +E'*' was used to exclude all regular files from being listed
while traversing the directory hierarchy.
X
Also observe that two pairs of quotes were used: the outer quotes were used
to prevent the shell from breaking the +G argument up into two arguments due
to the embedded space following the mkdir, etc. commands; the inner quotes
were used to tell ELS to process the characters verbatim rather than as
output directives.  However, when an output directive is desired inside of a
string, then the indicator "%" is required.  Thus, the following two
statements will achieve the same results:
X
X	els +G'"Date: "'m'", File: "'NL -R
X	els +G'"Date: %m, File: %N%L"' -R
X
Note that the two pairs of quotes can be stated either as '"..."'  or
"'...'" so long as they are equally balanced on both sides:
X
X	els +G'"Date: %m, File: %N%L"' -R
X	els +G"'Date: %m, File: %N%L'" -R
X
The indicator "%" works similar to the how printf woks.  For example, the
following will output two separate fields each 20 characters wide and the
first field will be right justified and second field will be left justified:
X
X	els +G%20N%-20s
X
X
--------
Since ELS's original intent was for tracking file changes in a hierarchy,
special options for "stamping" a hierarchy are available.  A "stamp" is
taken to mean a listing that is performed on a particular date and is
saved to a file so that a similar "stamp" can be performed at a later date
and the two "stamps" can then be compared for any changes that may have
occurred.
X
For example, suppose that on Dec 25, 1995 I did the following:
X	els +SC /home/markb/src > /home/markb/19951225.Stamp
And on Feb 29, 1996 I performed a second stamp:
X	els +SC /home/markb/src > /home/markb/19960229.Stamp
X
I can now determine any changes by doing a "diff":
X	diff /home/markb/19951225.Stamp /home/markb/19960229.Stamp
X
X
Similarly, suppose I was the paranoid type and I wanted to protect my
operating system from Joe Cracker:
X	els +SO /etc /dev /usr /var > /home/markb/19960229.OS-Stamp
X
The +SO stamp is more complete than the +SC stamp as +SO performs a
checksum, and saves the file type, mode, owner, group, size, and date.  If
my system is compromised I can generate a second +SO stamp and compare the
two.
X
You should realize that the checksum algorithm used by ELS is the same
algorithm used by /bin/sum.  This isn't a particularly robust checksum, but
it is adequate for most purposes.  If a more robust checksum is desired,
then I recommend that you modify els.c to suit your purposes.  Note also
that ELS will preserve the access times of all files during checksumming,
which is a very desirable feature for all anal-retentive sysadmins!
X
X
--------
ELS provides a mechanism for recovering the modification dates of files
following a "touch".  For example, suppose you are working on a project with
a group of pea-brains who believes that the "right" way to remake object
files is to first "touch" all the source files before typing "make".  If you
were to run the command "els +U -R" and save the output to a file, then at
some later date you could execute the resulting "untouch" script and restore
the original dates of your files (assuming the checksums of your files
hadn't changed in the interim).
X
Here's a possible scenario:
X	# Create and/or update your untouch script file:
X	els +U -R * > untouch
X
X	# Suppose that later on, someone decides to "touch" your files:
X	touch *
X
X	# You can then recover modification dates of all files whose checksums
X	# haven't changed since the last update of your "untouch" script:
X	sh untouch
X
Note that the "untouch" feature requires the presence of the "chdate" program
which is included with this software.
X
It is also worth noting that ELS's builtin untouch command '+U' is equivalent
to the following set of commands:
X	els +TI +G'"[ -f %N ] && [ `els +GC %N` -eq %C ] && chdate -m %m%N"'
X
X
--------
ELS correctly interprets *negative* Unix-times (since most Unixes record
time using a 32-bit counter representing the number of seconds since Jan 1,
1970 GMT, any files created after Jan 19 03:14:08 2038 GMT will be negative).
X
X    Here is a fun experiment to try on your machine:
X	% touch the_future
X	% chdate -m 20991228.120000 the_future
X
X    Under SunOS4.1.3, "ls -la the_future" gives us the following:
X	-rw-rw-r--  1 markb           0 Nov 22  1963 the_future
X
X    However, using "els -la the_future" we get:
X	-rw-rw-r--  1 markb           0 Dec 28 2099 12:00 the_future
X
In fact, ELS can correctly list the times of any file created on or before
the date Jan 19 03:14:08 2106 GMT.  For those folks still running SunOS4 in
the year 2038, this will prove to be a very useful feature %-)
X
X
--------
Other undocumented features have been squirreled-away which may or may
not be of interest to you.  The command "els +M -R" is a short-hand way of
displaying modification time in ISO-3307 format (it is equivalent to
"els +Gmn +TI +NF -R"), as this is a command that I often find useful.
X
X
The command "els +MM -R" is a short-hand way of displaying access time
in ISO-3307 format (it is equivalent to "els +Gan +TI +NF -L -R").  This
also is a command that I often find useful.
X
(It is important to realize that the access date of a symbolic-link is
updated each time it is examined!  Because of this it is more meaningful
to read the access time of the file that the symbolic-link points at
rather than the access date of the symbolic-link itself.  Thus, the
"-L" option is implied when using "+MM".)
X
X
Two other undocumented features are "els +MMM" and "els +MMMM".  These will
generate the necessary "chdate" commands for restoring the modification
and/or access dates of a file or directory to a previous date after having
changed it.
X
Note that if you want to restore the modification dates of directories, you
need to remember to use the -d option to prevent expanding the contents of
the directory.  E.g.:
X
X	els +MMM -d /home/directory > /tmp/restore-dirdate
X	sh /tmp/restore-dirdate
X
X
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X
DIFFERENCES:
X
* ELS does not yet support the -C option for displaying short listings
X  using multi-columns, but this is not at issue as the primary reason
X  for using ELS is to take advantage of ELS's ability to display long
X  listings in a variety of ways.
X
* ELS does not support some of the less commonly used /bin/ls flags, but
X  similar effects can usually be duplicated using an appropriate combination
X  of +G, +T, and +N format options.
X
* ELS does not search the directories of an expanded symbolic link.  This
X  is actually an advantage over /bin/ls, as consider the following:
X
X	ln -s .. link	# Create a recursive loop
X	/bin/ls -laLR	# ls will loop forever
X	els -laLR	# els will ignore "link" and not get trapped
X
* When using the "-L" option, ELS reports "No such file" if the file
X  pointed to by the symbolic link does not exist.  /bin/ls, on the other
X  hand, simply lists the symbolic link rather than complain about an error.
X
X
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X
BUGS:
X
ELS does not generate multi-colored output ;-)
X
X
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SHAR_EOF
chmod 0664 els/README ||
echo 'restore of els/README failed'
Wc_c="`wc -c < 'els/README'`"
test 18920 -eq "$Wc_c" ||
	echo 'els/README: original size 18920, current size' "$Wc_c"
fi
# ============= els/chdate.1 ==============
if test -f 'els/chdate.1' -a X"$1" != X"-c"; then
	echo 'x - skipping els/chdate.1 (File already exists)'
else
echo 'x - extracting els/chdate.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/chdate.1' &&
.de EX		\"Begin example
.ne 5
.if n .sp 1
.if t .sp .5
.nf
.in +5n
..
.de EE		\"End example
.fi
.in -5n
.if n .sp 1
.if t .sp .5
..
.TH CHDATE 1 "May 22, 1996"
X
.SH NAME
chdate \- Change date using ISO-3307, ls-style, or Unix clock
X
.SH SYNOPSIS
\fBchdate\fR [-LG] [-a|m|am] \fIiso3307_date files\fR
.br
\fBchdate\fR [-LG] [-a|m|am] \fI"ls_style_date" files\fR
.br
\fBchdate\fR -C [-a|m|am] \fIunix_clock files\fR
X
.SH DESCRIPTION
.I chdate
changes the access and modification dates of files using either ISO-3307
dates, ls-style dates, or Unix clock values as its input.  Unless otherwise
indicated, all times are assumed to be local and the necessary conversions
to GMT will be made.
X
.I chdate
will even correctly change the dates of files created after Jan 19
03:14:08 2038 GMT (assuming that the future standard will treat the 32-bits
used by Unix's present-day file system as unsigned).
X
.SH OPTIONS
.TP
.B \-L
Time is represented as either an ISO-3307 date or an ls-style date
and is in Local time (default).
X
.TP
.B \-G
Time is represented as either an ISO-3307 date or an ls-style date
and is in GMT.
X
.TP
.B \-C
Time is represented as a Unix clock value.
X
.TP
.B \-a
change access date of indicated files
X
.TP
.B \-m
change modification date of indicated files (default)
X
.SH DATE FORMATS
An\fI ISO-3307\fR date is defined as YYYYMMDD[.hhmm[ss]]
.br
The date must be all digits (except for the optional period
preceding the hour hh).  The year YYYY must be not less than 1970
and not more than 2106.  The month MM is specified as 01 through
12, and the day DD is specified as 01 through 31.  The time, if
given, is specified as either 0000 through 2359 or 000000 through
235959.  Any unspecified hh, mm, ss parameters will default to 00.
X
An\fI ls-style\fR date is defined as "MON DD [YYYY] [hh:mm[:ss]]"
.br
Because of spaces, ls-style dates need to be enclosed in quotes.
MON is the standard three-letter Ascii abbreviation for the
month.  The day DD is specified as 1 through 31.  The year YYYY,
if given, must be not less than 1970 and not more than 2106.  If
the year is not given then the most recent year gone by will be
assumed (you can avoid possible ambiguity by specifying both the
year and the time, which is how file dates are listed if you use
\'els -l' instead of 'ls -l').  The time, if given, is specified
as either 00:00 through 23:59 or 00:00:00 through 23:59:59.  Any
unspecified hh, mm, ss parameters will default to 0.
X
If both the year and the time are specified, it doesn't matter
which one is last.  Thus, a date such as 'Jan 31 12:34 2001' is
equivalent to 'Jan 31 2001 12:34'.  Furthermore, weekdays (e.g.
Mon, Tue, Wed, etc.) and timezones (e.g. EST, CDT, MST, etc.)
are ignored.  Thus, a date such as 'Wed Jan 31 12:34:56 EST 2001'
is equivalent to 'Jan 31 2001 12:34:56'.
X
A\fI Unix clock\fR value is defined as an unsigned 32 bit decimal,
octal, or hexadecimal value in the range 0 through 4294967294 or 0
through 0xfffffffe (note that -1 is used to represent certain error
conditions).  Unix clock values can be listed using 'els -l +TAs'
X
.SH EXAMPLES
Change the modification date to April 1, 1995 12:34 Local time:
.EX
chdate 19950401.1234 myfile
.EE
Change the modification date to April 1, 1995 Local time using
ls-style dates:
.EX
chdate "Apr  1  1995" myfile
.EE
Change both the access and modification dates to January 1, 2050
12:34:56 Local time:
.EX
chdate -am 20500101.123456 file1 file2 file3
.EE
Change the modification date to Feb 29, 1996 12:34:56 GMT and
change the access date to April 1, 1995 12:34:56 Local time:
.EX
chdate -Gm 19960229.123456 -La 19950401.123456 file1 file2 file3
.EE
Change the modification date to a clock value of 0x87654321 and
change the access date to Feb 29, 1996 12:34:56 GMT:
.EX
chdate -Cm 0x87654321 -Ga 19960229.123456 file1 file2 file3
.EE
X
.SH IMPORTANT NOTE
You must use 'els' instead of 'ls' to correctly list the
date of any file set beyond Jan 19, 2038 03:14:08 GMT.
X
.SH AUTHOR
Mark Baranowski
.br
markb@elen.utah.edu
X
.SH "SEE ALSO"
edate(1),
els(1)
SHAR_EOF
chmod 0664 els/chdate.1 ||
echo 'restore of els/chdate.1 failed'
Wc_c="`wc -c < 'els/chdate.1'`"
test 4048 -eq "$Wc_c" ||
	echo 'els/chdate.1: original size 4048, current size' "$Wc_c"
fi
# ============= els/edate.1 ==============
if test -f 'els/edate.1' -a X"$1" != X"-c"; then
	echo 'x - skipping els/edate.1 (File already exists)'
else
echo 'x - extracting els/edate.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/edate.1' &&
.de EX		\"Begin example
.ne 5
.if n .sp 1
.if t .sp .5
.nf
.in +5n
..
.de EE		\"End example
.fi
.in -5n
.if n .sp 1
.if t .sp .5
..
.TH EDATE 1 "May 22, 1996"
X
.SH NAME
edate \- Enhanced date using ISO-3307, ls-style, or Unix clock
X
.SH SYNOPSIS
Display current time:
.br
\fBedate\fR [-LGC] [+T format]
X
.br
Set system time (must be super-user):
.br
\fBedate\fR [-LG] \fIiso3307_date|ls_style_date\fR
.br
\fBedate\fR -C \fIunix_clock\fR
X
.br
Convert from one time format to another:
.br
\fBedate\fR [-LG] \fIiso3307_date|ls_style_date\fR -o [-LGC] [+T format]
.br
\fBedate\fR -C \fIunix_clock\fR -o [-LGC] [+T format]
X
.SH DESCRIPTION
.I edate
displays, sets, or converts dates using either ISO-3307 dates, ls-style
dates, or Unix clock values as its input and output.  Unless otherwise
indicated, all times are assumed to be local and the necessary conversions
to GMT will be made.
X
.I edate
will even correctly recognize dates beyond Jan 19 03:14:08 2038 GMT
(assuming that the future standard will treat the 32-bits used by
Unix's present-day file system as unsigned).
X
.SH OPTIONS
.TP
.B \-L
Time is represented as either an ISO-3307 date or an ls-style date
and is in Local time (default).
X
.TP
.B \-G
Time is represented as either an ISO-3307 date or an ls-style date
and is in GMT.
X
.TP
.B \-C
Time is represented as a Unix clock value.
X
.TP
.B \-o
Convert and then output time (this avoids setting the system
clock).
X
.TP
.BI \+T options
TIME format, e.g.: edate Jan 1 00:00 +TRD (number of days since Jan 1)
X       N:  Modifier for Numeric dates instead of alpha
X       A:  Modifier for Absolute time since the epoch
X       R:  Modifier for Relative time from now
X       G:  Modifier for GMT dates instead of Local
X       L:  Modifier for Local dates instead of GMT
X       I:  Iso3307 style (same as +TN~YMD.hms~)
X       e:  els style (default, same as +TMDY2)
X       l:  ls style (same as +TMDy)
X       v:  verbose style (same as +TWMD3ZY)
X Y,M,D,W:  year, month, day, weekday
X   h,m,s:  hour, minutes, seconds
X     2,3:  time as h:m, h:m:s
X       y:  year or time depending on age of file
X       Z:  zone name
.TP
.B \+T option modifiers
X       \\:  Output following character verbatim
X       ~:  Toggle spacing off/on
X      %n:  Output a right justified field 'n'
X           characters wide
X     %-n:  Output a left justified field 'n'
X           characters wide
X   "",'':  Output contents of quoted string verbatim
X
Other characters not recognized by the format are output verbatim
X
.SH DATE FORMATS
An\fI ISO-3307\fR date is defined as YYYYMMDD[.hhmm[ss]]
.br
The date must be all digits (except for the optional period
preceding the hour hh).  The year YYYY must be not less than 1970
and not more than 2106.  The month MM is specified as 01 through
12, and the day DD is specified as 01 through 31.  The time, if
given, is specified as either 0000 through 2359 or 000000 through
235959.  Any unspecified hh, mm, ss parameters will default to 00.
X
An\fI ls-style\fR date is defined as "MON DD [YYYY] [hh:mm[:ss]]"
.br
MON is the standard three-letter Ascii abbreviation for the
month.  The day DD is specified as 1 through 31.  The year YYYY,
if given, must be not less than 1970 and not more than 2106.  If
the year is not given then the most recent year gone by will be
assumed (you can avoid possible ambiguity by specifying both the
year and the time, which is how file dates are listed if you use
\'els -l' instead of 'ls -l').  The time, if given, is specified
as either 00:00 through 23:59 or 00:00:00 through 23:59:59.  Any
unspecified hh, mm, ss parameters will default to 0.
X
If both the year and the time are specified, it doesn't matter
which one is last.  Thus, a date such as 'Jan 31 12:34 2001' is
equivalent to 'Jan 31 2001 12:34'.  Furthermore, weekdays (e.g.
Mon, Tue, Wed, etc.) and timezones (e.g. EST, CDT, MST, etc.)
are ignored.  Thus, a date such as 'Wed Jan 31 12:34:56 EST 2001'
is equivalent to 'Jan 31 2001 12:34:56'.
X
A\fI Unix clock\fR value is defined as an unsigned 32 bit decimal,
octal, or hexadecimal value in the range 0 through 4294967294 or 0
through 0xfffffffe (note that -1 is used to represent certain error
conditions).  Unix clock values can be listed using 'els -l +TAs'
X
.SH EXAMPLES
Display the current time in Iso3307 format:
.EX
edate +TI
.EE
Set the system date to April 1, 2000 12:34 Local time:
.EX
edate 20000401.1234
.EE
Set the system date to April 1, 2000 12:34:56 Local time using
ls-style dates:
.EX
edate Apr 1 2000 12:34
.EE
Set the system date to January 1, 2050 12:34:56 GMT:
.EX
edate -G 20500101.123456
.EE
Convert the date Feb 29, 1996 12:34:56 GMT into a clock value:
.EX
edate -G 19960229.123456 -o -C
.EE
Convert the clock value of 0x87654321 into Local time and specify
the output format:
.EX
edate -C 0x87654321 +T"%M %D, %Y %h:%m:%s"
.EE
X
.SH IMPORTANT NOTE
You must use 'edate' instead of 'date' to correctly list the
date when set beyond Jan 19, 2038 03:14:08 GMT.  Moreover, on some
machines 'edate' must be used to set the date beyond Jan 1, 2000.
X
.SH AUTHOR
Mark Baranowski
.br
markb@elen.utah.edu
X
.SH "SEE ALSO"
chdate(1),
els(1)
SHAR_EOF
chmod 0664 els/edate.1 ||
echo 'restore of els/edate.1 failed'
Wc_c="`wc -c < 'els/edate.1'`"
test 5123 -eq "$Wc_c" ||
	echo 'els/edate.1: original size 5123, current size' "$Wc_c"
fi
# ============= els/els.1 ==============
if test -f 'els/els.1' -a X"$1" != X"-c"; then
	echo 'x - skipping els/els.1 (File already exists)'
else
echo 'x - extracting els/els.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/els.1' &&
.de EX		\"Begin example
.ne 5
.if n .sp 1
.if t .sp .5
.nf
.in +5n
..
.de EE		\"End example
.fi
.in -5n
.if n .sp 1
.if t .sp .5
..
.TH ELS 1 "May 22, 1996"
X
.SH NAME
els \- An Enhanced LS look-alike with many additional features
X
.SH SYNOPSIS
\fBels\fR [-aAcdFgilLnoprRstu] [+HB5D] [+GTN \fIformat\fR] [+F \fIchar\fR]
.in +4
[+IEie \fIpattern\fR] [+S \fIchar\fR] [+U] \fIfiles\fR
X
.SH DESCRIPTION
.I ELS
is an Enhanced LS look-alike with many additional features.  Besides
supporting most of what /bin/ls does, ELS also allows you to specify your own
output format for directory listings and it supports many different time
formats (e.g. Ascii, ISO-3307, Absolute/relative time in days, months, years,
etc.).  ELS allows you to specify absolute file paths and names so that
the output can be further manipulated by /bin/sort, etc.  ELS has a built-in
checksumming ability so that files can be listed along with their checksums.
ELS has a "untouch" facility which will allow you to recover the modification
dates following a "touch".
X
ELS will even correctly display the dates of files created after Jan 19
03:14:08 2038 GMT (assuming that the future standard will treat the 32-bits
used by Unix's present-day file system as unsigned).
X
The reason that ELS was originally written was so that consistent dates
could be generated and saved into a file and that these dates could be
compared at a later date to determine which files had changed.  /bin/ls
is unsuited for this purpose as sometimes it lists the date/time and
other times it lists the date/year depending on the age of the file.  ELS
always lists the date/year/time regardless of when the file was created.
X
ELS's other main features are that it allows you to specify the format
of the output using the generic format (+G), it allows you to specify
the time format (+T), and it allows you to specify the filename format
(+N). ELS also allows you to include and exlude directories and files
based on wildcard patterns.
X
.SH "LS-LIKE OPTIONS"
.TP
.B \-a
List all files including . and ..
.TP
.B \-A
List all files except . and ..
.TP
.B \-c
List time of last status change(BSD & SYS5) and sort(BSD)
.TP
.B \-d
List directories as files, but don't list their contents
.TP
.B \-F
Mark files
.TP
.B \-g
List group(BSD), long listing omitting UID(SYS5)
.TP
.B \-i
List inode number
.TP
.B \-l
Long listing using BSD or SYS5 semantics
.TP
.B \-L
List actual file rather than symbolic link
.TP
.B \-n
Long listing using numeric UIDs and GIDs(SYS5)
.TP
.B \-o
Long listing omitting GIDs(SYS5)
.TP
.B \-p
Mark directories
.TP
.B \-r
Reverse the sort
.TP
.B \-R
Recursively list contents of all directories
.TP
.B \-s
List size in blocks(SYS5) or in KBytes(BSD)
.TP
.B \-t
Sort files according to time
.TP
.B \-u
List time of last access
X
.SH "ENHANCED LS OPTIONS"
.TP
.B \+H
Give HELP
.TP
.B \+B
Use BSD semantics
.TP
.B \+5
Use SYS5 semantics
.TP
.B \+D
Don't list directories as files, but list their contents (has the
opposite effect of -d)
.TP
.BI \+G options
GENERAL format, e.g.: els +G~tp~lusmnL (same as els -l)
X       i:  inode number
X     t,T:  type of file in alpha, symbolic
X     p,P:  permission in alpha, numeric
X     l,L:  link count, name
X     u,U:  UID in alpha, numeric
X     g,G:  GID in alpha, numeric
X     s,S:  size in bytes, blocks
X   m,a,c:  time modified, accessed, status changed
X     n,N:  name of file, path/file
X       C:  16 bit checksum (performed on regular files
X           only and access times are preserved)
.TP
.BI \+T options
TIME format, e.g.: els -la +TRD (give age of file in days)
X       N:  Modifier for Numeric dates instead of alpha
X       A:  Modifier for Absolute time since the epoch
X       R:  Modifier for Relative time from now
X       G:  Modifier for GMT dates instead of Local
X       L:  Modifier for Local dates instead of GMT
X       I:  Iso3307 style (same as +TN~YMD.hms~)
X       e:  els style (default, same as +TMDY2)
X       l:  ls style (same as +TMDy)
X       v:  verbose style (same as +TWMD3ZY)
X Y,M,D,W:  year, month, day, weekday
X   h,m,s:  hour, minutes, seconds
X     2,3:  time as h:m, h:m:s
X       y:  year or time depending on age of file
X       Z:  zone name
.TP
.BI \+N options
NAME format, e.g.:  els -laR +NF
X       F:  Full file name (same as +Nd/f)
X       d:  Directory name
X       f:  File name
.TP
.B \+G, \+T, \+N option modifiers
X       \\:  Output following character verbatim
X       ~:  Toggle spacing off/on
X      %n:  Output a right justified field 'n'
X           characters wide
X     %-n:  Output a left justified field 'n'
X           characters wide
X   "",'':  Output contents of quoted string verbatim
X
E.g.: els +G%10u%-10gN (print the uid right justified and the gid left
justified in two 10 character fields followed by the file name)
X
Other characters not recognized by the format are output verbatim
.TP
.BI \+F char
Field separator character, e.g.: els -la +F: +TI
.TP
.BI \+I pattern
Include specified files, e.g.: els -lagR +I"*.c"
.TP
.BI \+E pattern
X
Exclude specified files, e.g.: els -lagR +E"*.[ao]"
.TP
.BI \+i pattern
X
Include specified directories, e.g.: els -lagR +i"[a-m]*"
.TP
.BI \+e pattern
X
Exclude specified directories, e.g.: els -lagR +e"SCCS"
.TP
.BI \+S option
STAMP: The output from this option is used for recording the state
of a hierarchy for future comparison to determine any changes.
X       C:  Stamp a source code hierarchy excluding
X           *.[ao], *.ln, *~,  and SCCS/RCS files
X       c:  Same as +SC, except nothing is excluded
X
X       O:  Stamp an operating system hierarchy (no
X           files are excluded and more detail given)
X       o:  Same as +SO, except checksumming is also
X           performed
.TP
.B \+U
UNTOUCH: Create a script which can be saved into a file for later
recovery of modification dates following a /bin/touch.
X
.SH EXAMPLES
Search for the most recently modified *.c and *.h files in a
source-code hierarchy:
.EX
els +Gmn +TI +NF +I"*.[ch]" -R | sort | tail
.EE
.LP
Search for all files greater than 100K bytes that have not been
accessed within the last 180 days and were modified over 365 days ago:
.EX
#!/bin/sh
echo "    Size   Acc   Mod File"
echo " =======  ====  ==== ======"
els +F" " +GsamN +TRD +D -AR | awk -F" " '
X	{if ($1 >= 100000 && $2 > 180 && $3 > 365)
X		{printf "%8d%6d%6d %s\n", $1, $2, $3, $4};
X	}'
.EE
Create a stamp of a source code hierarchy excluding *.[ao], *.ln, *~,
and SCCS/RCS files:
.EX
els +SC -R /home/myfiles/src
.EE
Create a stamp of an operating system hierarchy (no files are excluded
and checksum is performed):
.EX
els +So / -R /etc /dev /usr /var
.EE
Create an "untouch" script which can be saved into a file for later
recovery of modification dates following a "touch":
.EX
els +U -R * > untouch  # Create untouch script file
/bin/touch *           # Clobber modification dates
sh untouch             # Recover modification dates
.EE
X
.SH BUGS
ELS does not yet support the -C option for displaying short listings
using multi-columns, but this is not at issue as the primary reason
for using ELS is to take advantage of ELS's ability to display long
listings in a variety of ways.
.sp
ELS does not generate multi-colored output ;-)
X
.SH AUTHOR
.nf
Mark Baranowski
markb@elen.utah.edu
.sp
James M. Gleason
Address unknown
.fi
X
.SH "SEE ALSO"
ls(1),
chdate(1),
edate(1)
SHAR_EOF
chmod 0664 els/els.1 ||
echo 'restore of els/els.1 failed'
Wc_c="`wc -c < 'els/els.1'`"
test 7309 -eq "$Wc_c" ||
	echo 'els/els.1: original size 7309, current size' "$Wc_c"
fi
# ============= els/untouch ==============
if test -f 'els/untouch' -a X"$1" != X"-c"; then
	echo 'x - skipping els/untouch (File already exists)'
else
echo 'x - extracting els/untouch (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/untouch' &&
#!/bin/sh
#
# 'untouch' shell script created on Wed May 22 23:03:16 1996
# from the directory /home/chaos1/markb/src/els/release-1.29/els
# using the command: els +UR
#
# Save the following output into a script file so that it can then be run
# at a later time in order to recover file dates following a 'touch':
#
[ -f Makefile ] && [ `els +GC Makefile` -eq 04459 ] && chdate -m 19960522.214412 Makefile
[ -f README ] && [ `els +GC README` -eq 53469 ] && chdate -m 19960517.172904 README
[ -f chdate.1 ] && [ `els +GC chdate.1` -eq 26388 ] && chdate -m 19960522.221459 chdate.1
[ -f chdate.c ] && [ `els +GC chdate.c` -eq 45958 ] && chdate -m 19960522.213715 chdate.c
[ -f defs.h ] && [ `els +GC defs.h` -eq 50626 ] && chdate -m 19960522.213847 defs.h
[ -f edate.1 ] && [ `els +GC edate.1` -eq 50659 ] && chdate -m 19960522.222229 edate.1
[ -f edate.c ] && [ `els +GC edate.c` -eq 28400 ] && chdate -m 19960522.222302 edate.c
[ -f els.1 ] && [ `els +GC els.1` -eq 01076 ] && chdate -m 19960522.230250 els.1
[ -f els.c ] && [ `els +GC els.c` -eq 47994 ] && chdate -m 19960522.213635 els.c
[ -f getdate32.c ] && [ `els +GC getdate32.c` -eq 03830 ] && chdate -m 19960522.213611 getdate32.c
[ -f getdate32.h ] && [ `els +GC getdate32.h` -eq 25809 ] && chdate -m 19960522.213915 getdate32.h
[ -f time32.c ] && [ `els +GC time32.c` -eq 48657 ] && chdate -m 19960522.213542 time32.c
[ -f time32.h ] && [ `els +GC time32.h` -eq 61289 ] && chdate -m 19960522.213831 time32.h
[ -f untouch ] && [ `els +GC untouch` -eq 64544 ] && chdate -m 19960522.230317 untouch
SHAR_EOF
chmod 0775 els/untouch ||
echo 'restore of els/untouch failed'
Wc_c="`wc -c < 'els/untouch'`"
test 1554 -eq "$Wc_c" ||
	echo 'els/untouch: original size 1554, current size' "$Wc_c"
fi
# ============= els/chdate.c ==============
if test -f 'els/chdate.c' -a X"$1" != X"-c"; then
	echo 'x - skipping els/chdate.c (File already exists)'
else
echo 'x - extracting els/chdate.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/chdate.c' &&
/******************************************************************************
X  chdate.c -- Change date a la ISO 3307
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  This program is provided "as is" in the hopes that it might serve some
X  higher purpose.  If you want this program to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute this program so long as this header remains intact.
X  You may also freely distribute modified versions of this program so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Note: The most recent version of this program may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  The following routines support Unix time beyond Jan 19 03:14:08 2038 GMT,
X  assuming that the future standard will treat the 32-bits used by Unix's
X  present-day file system as unsigned.
X
X  ****************************************************************************/
X
static char *id = "\n\
@(#)chdate [1.29], May 22, 1996\n\
@(#)Mark Baranowski (markb@elen.utah.edu)\n\
";
X
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
#include <utime.h>
#include "defs.h"
#include "getdate32.h"
X
/********** Global Routines Referenced **********/
X
/********** Global Routines Defined **********/
X
void chdate(char *file);
void give_usage_or_help(char U_or_H);
X
/********** Global Variables Defined **********/
X
char *Progname;
char LGC;
time_t atime_arg;
time_t mtime_arg;
Boole change_atime;
Boole change_mtime;
X
X
/*****************************************************************************/
X
int main(int argc, char *argv[])
{
X  int iarg;
X  char *cp;
X  
X  if ((cp = strrchr(argv[0], '/')) != NULL)
X    Progname = cp + 1;
X  else
X    Progname = argv[0];
X  iarg = 1;
X  
X  if (iarg < argc && *(argv[iarg]) == '+')
X  {
X    char *opt = argv[iarg];
X    opt++;
X    if (*opt == 'H')
X      give_usage_or_help('H');
X    else if (*opt == 'V')
X      puts(id);
X    else
X      give_usage_or_help('U');
X    return(1);
X  }
X
X  /* Establish initial defaults: */
X  change_atime = FALSE;
X  change_mtime = FALSE;
X
X  do
X  {
X    Boole get_atime_arg;
X    Boole get_mtime_arg;
X      
X    /* Establish defaults for each round: */
X    get_atime_arg = FALSE;
X    get_mtime_arg = FALSE;
X    LGC = 'L';
X    
X    while (iarg < argc && *(argv[iarg]) == '-')
X    {
X      char *opt = argv[iarg];
X      while (*++opt)
X      {
X	if (*opt == 'a')
X	{
X	  change_atime = TRUE;
X	  get_atime_arg = TRUE;
X	}      
X	else if (*opt == 'm')
X	{
X	  change_mtime = TRUE;
X	  get_mtime_arg = TRUE;
X	}
X	else if (*opt == 'L')
X	  LGC = 'L';
X	else if (*opt == 'G')
X	  LGC = 'G';
X	else if (*opt == 'C')
X	  LGC = 'C';
X	else
X	  usage_error();
X      }
X
X      iarg++;
X    }
X    
X    /* If neither -a or -m were specified this round, assume -m: */
X    if (!get_atime_arg && !get_mtime_arg)
X    {
X      change_mtime = TRUE;
X      get_mtime_arg = TRUE;
X    }
X    
X    if (iarg < argc)
X    {
X      time_t time_arg;
X      time_arg = getdate32(argv[iarg]);
X      if (get_atime_arg) atime_arg = time_arg;
X      if (get_mtime_arg) mtime_arg = time_arg;
X    }
X    else
X      usage_error();
X
X    iarg++;
X  } while (iarg < argc && *(argv[iarg]) == '-');
X    
X  if (iarg < argc)
X  {
X    /* Change date of each file: */
X    for (; iarg < argc; iarg++)
X      chdate(argv[iarg]);
X  }
X  else
X    usage_error();
X
X  return(0);
}
X
X
void chdate(char *file)
{
X  struct stat sb;
X  struct utimbuf tb;
X
X  if (stat(file, &sb) != 0)
X  {
X    error_num_exit(file);
X  }
X  else
X  {
X    if (change_atime)
X      tb.actime = atime_arg;
X    else
X      tb.actime = sb.st_atime;
X
X    if (change_mtime)
X      tb.modtime = mtime_arg;
X    else
X      tb.modtime = sb.st_mtime;
X
X    if (utime(file, &tb) != 0)
X      error_num_exit(file);
X  }
}
X
X
void give_usage_or_help(char U_or_H)
{
X  /* Print usage to stderr so as to avoid any redirection.
X     Print help to stdout so as to make it pipe-able through more: */
X  FILE *out, *fopen(), *popen();
X  Boole more = FALSE;
X
X  if (U_or_H == 'H')
X  {
X    char *places[] = {"/usr/ucb/more", "/usr/bin/more", NULL};
X    char **path;
X    for (path = places; *path && fopen(*path, "r") == NULL; path++) /*...*/;
X    if (*path && (out = popen(*path, "w")) != NULL)
X      more = TRUE;
X    else
X      out = stdout;
X  }
X  else
X    out = stderr;
X
X  fprintf(out, "\
\n\
Usage: %s [-LG] [-a|m|am] iso3307_date files\n\
X       %s [-LG] [-a|m|am] \"ls_style_date\" files\n\
X       %s -C [-a|m|am] unix_clock files\n\
\n\
", Progname, Progname, Progname);
X
X  if (U_or_H == 'U')
X  {
X    Void fprintf(out, "\
For more help: %s +H\n\
\n\
", Progname);
X  }
X  else
X  {
X    Void fprintf(out, "\
Where:\n\
X    -L: Local time (default)\n\
X    -G: GMT\n\
X    -C: 32-bit clock\n\
X    -a: change access time of files\n\
X    -m: change modification time of files (default)\n\
\n\
X    iso3307_date :== YYYYMMDD[.hhmm[ss]]\n\
X        The date must be all digits (except for the optional period\n\
X        preceding the hour hh).  The year YYYY must be not less than 1970\n\
X        and not more than 2106.  The month MM is specified as 01 through\n\
X        12, and the day DD is specified as 01 through 31.  The time, if\n\
X        given, is specified as either 0000 through 2359 or 000000 through\n\
X        235959.  Any unspecified hh, mm, ss parameters will default to 00.\n\
\n\
X    ls_style_date :== \"MON DD [YYYY] [hh:mm[:ss]]\"\n\
X        Because of spaces, ls-style dates need to be enclosed in quotes.\n\
X        MON is the standard three-letter Ascii abbreviation for the\n\
X        month.  The day DD is specified as 1 through 31.  The year YYYY,\n\
X        if given, must be not less than 1970 and not more than 2106.  If\n\
X        the year is not given then the most recent year gone by will be\n\
X        assumed (you can avoid possible ambiguity by specifying both the\n\
X        year and the time, which is how file dates are listed if you use\n\
X        'els -l' instead of 'ls -l').  The time, if given, is specified\n\
X        as either 00:00 through 23:59 or 00:00:00 through 23:59:59.  Any\n\
X        unspecified hh, mm, ss parameters will default to 0.\n\
\n\
X        If both the year and the time are specified, it doesn't matter\n\
X        which one is last.  Thus, a date such as 'Jan 31 12:34 2001' is\n\
X        equivalent to 'Jan 31 2001 12:34'.  Furthermore, weekdays (e.g.\n\
X        Mon, Tue, Wed, etc.) and timezones (e.g. EST, CDT, MST, etc.)\n\
X        are ignored.  Thus, a date such as 'Wed Jan 31 12:34:56 EST 2001'\n\
X        is equivalent to 'Jan 31 2001 12:34:56'.\n\
\n\
X    unix_clock :== unsigned 32 bit decimal, octal, or hexadecimal value\n\
X        in the range 0 through 4294967294 or 0 through 0xfffffffe (note\n\
X        that -1 is used to represent certain error conditions).  Unix\n\
X        clock values can be listed using 'els -l +TAs'\n\
\n\
");
X
X    Void fprintf(out, "\
Examples:\n\
X    Change the modification date to April 1, 1995 12:34 Local time:\n\
X	%s 19950401.1234 myfile\n\
\n\
X    Change the modification date to April 1, 1995 Local time using\n\
X    ls-style dates:\n\
X	%s \"Apr  1  1995\" myfile\n\
\n\
X    Change both the access and modification dates to January 1, 2050\n\
X    12:34:56 Local time:\n\
X	%s -am 20500101.123456 file1 file2 file3\n\
\n\
X    Change the modification date to Feb 29, 1996 12:34:56 GMT and\n\
X    change the access date to April 1, 1995 12:34:56 Local time:\n\
X	%s -Gm 19960229.123456 -La 19950401.123456 file1 file2 file3\n\
\n\
X    Change the modification date to a clock value of 0x87654321 and\n\
X    change the access date to Feb 29, 1996 12:34:56 GMT:\n\
X	%s -Cm 0x87654321 -Ga 19960229.123456 file1 file2 file3\n\
\n\
IMPORTANT NOTE:\n\
X    You must use 'els' instead of 'ls' to correctly list the\n\
X    date of any file set beyond Jan 19, 2038 03:14:08 GMT.\n\
\n\
", Progname, Progname, Progname, Progname, Progname);
X  }
X
X  if (more)
X    pclose(out);
X  
X  return;
}
SHAR_EOF
chmod 0664 els/chdate.c ||
echo 'restore of els/chdate.c failed'
Wc_c="`wc -c < 'els/chdate.c'`"
test 8093 -eq "$Wc_c" ||
	echo 'els/chdate.c: original size 8093, current size' "$Wc_c"
fi
# ============= els/edate.c ==============
if test -f 'els/edate.c' -a X"$1" != X"-c"; then
	echo 'x - skipping els/edate.c (File already exists)'
else
echo 'x - extracting els/edate.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/edate.c' &&
/******************************************************************************
X  edate.c -- An enhanced date
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  This program is provided "as is" in the hopes that it might serve some
X  higher purpose.  If you want this program to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute this program so long as this header remains intact.
X  You may also freely distribute modified versions of this program so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Note: The most recent version of this program may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  The following routines support Unix time beyond Jan 19 03:14:08 2038 GMT,
X  assuming that the future standard will treat the 32-bits used by Unix's
X  present-day file system as unsigned.
X
X  ****************************************************************************/
X
static char *id = "\n\
@(#)edate [1.29], May 22, 1996\n\
@(#)Mark Baranowski (markb@elen.utah.edu)\n\
";
X
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <string.h>
#include <sys/time.h>
#include "defs.h"
#include "getdate32.h"
#include "time32.h"
X
/********** Global Routines Referenced **********/
X
/********** Global Routines Defined **********/
X
void edate(void);
void give_usage_or_help(char U_or_H);
char *T_print(char *buff, char *fmt, time_t ftime,
X	      struct tm *fdate, Boole gmt);
char *finish(char *bp, char *fmt, char icase);
X
/********** Global Variables Defined **********/
X
char *Progname;
char LGC;
time_t time_arg;
Boole have_time_arg;
Boole have_output_arg;
X
Boole T_option;
char *T_format;
#define ABS_DATE	'A'
#define REL_DATE	'R'
#define NUM_DATE	'N'
#define ALPHA_DATE	'S'
#define GMT_DATE	'G'
#define LOCAL_DATE	'L'
#define ISO3307_DATE	'I'
#define ELS_DATE	'e'
#define LS_DATE		'l'
#define VERBOSE_DATE	'v'
X
#define YEARS		'Y'
#define MONTHS		'M'
#define WEEKS		'W'
#define DAYS		'D'
#define HOURS		'h'
#define MINS		'm'
#define SECS		's'
#define TIME_2		'2'	/* h:m */
#define TIME_3		'3'	/* h:m:s */
#define YEAR_OR_TIME	'y'
#define ZONE_NAME	'Z'
X
Boole spacing = TRUE;
Boole fielding = FALSE;
Boole as_is;
Boole separated;
char separator = ' ';
X
X
/*****************************************************************************/
X
int main(int argc, char *argv[])
{
X  int iarg;
X  char *cp;
X  
X  if ((cp = strrchr(argv[0], '/')) != NULL)
X    Progname = cp + 1;
X  else
X    Progname = argv[0];
X  iarg = 1;
X  
X  /* Establish initial defaults: */
X  have_time_arg = FALSE;
X  have_output_arg = FALSE;
X  LGC = 'L';
X
X  /* Output defaults: */
X  T_option = FALSE;
X  T_format = "v";
X  spacing = TRUE;
X  as_is = FALSE;
X  separated = FALSE;
X
X  while (iarg < argc)
X  {
X    char *options = argv[iarg++];
X    if (*options == '-')
X    {
X      char opt;
X      options++;
X      while ((opt = *options++) != CNULL)
X      {
X	if (opt == 'L')
X	  LGC = 'L';
X	else if (opt == 'G')
X	  LGC = 'G';
X	else if (opt == 'C')
X	  LGC = 'C';
X	else if (opt == 'o')
X	  have_output_arg = TRUE;
X	else
X	  usage_error();
X      }
X    }
X
X    else if (*options == '+')
X    {
X      char opt;
X      options++;
X      while ((opt = *options++) != CNULL)
X      {
X	if (opt == 'T')
X	{
X	  T_option = TRUE;
X	  T_format = options;
X	  options = "";	/* No more options */
X	  have_output_arg = TRUE; /* Having a format implies output wanted */
X	}
X	else if (opt == 'H')
X	{
X	  give_usage_or_help('H');
X	  exit(1);
X	}
X	else if (opt == 'V')
X	{
X	  puts(id);
X	  exit(1);
X	}
X	else
X	  usage_error();
X      }
X    }
X
X    else
X    {
X      if (!have_time_arg)
X      {
#	define MAX_DATE_ARG 127
#	define SPACE_LEFT (i < MAX_DATE_ARG ? MAX_DATE_ARG - i : 0)
X	char date_arg[MAX_DATE_ARG+1];
X	int i = 0;
X
X	strncpy(&date_arg[i], options, SPACE_LEFT);
X	i += strlen(options);
X
X	while (iarg < argc && *(argv[iarg]) != '-' && *(argv[iarg]) != '+')
X	{
X	  options = argv[iarg++];
X	  strncpy(&date_arg[i], " ", SPACE_LEFT);
X	  i++;
X	  strncpy(&date_arg[i], options, SPACE_LEFT);
X	  i += strlen(options);
X	}
X	if (SPACE_LEFT == 0) usage_error();
X	time_arg = getdate32(date_arg);
X	LGC = 'L'; /* After each use, LGC reverts back to default */
X	have_time_arg = TRUE;
X      }
X      else
X	usage_error();
X    }
X  }
X    
X  if (iarg == argc)
X    edate();
X  else
X    usage_error();
X
X  return(0);
}
X
X
void edate(void)
{
X  if (have_time_arg && !have_output_arg)
X  {
X    struct timeval t;
X    t.tv_sec = time_arg;
X    t.tv_usec = 0;
X    if (settimeofday(&t, NULL) != 0)
X      error_num_exit("Failed to set date");
X  }
X  else
X  {
X    char buff[256];
X    time_t the_time;
X
X    if (have_time_arg)
X      the_time = time_arg;
X    else
X      the_time = time(NULL);
X
X    if (LGC == 'L')
X      Void T_print(buff, T_format, the_time, localtime32(&the_time), FALSE);
X    else if (LGC == 'G')
X      Void T_print(buff, T_format, the_time, gmtime32(&the_time), TRUE);
X    else if (LGC == 'C')
X    {
X      /* Use this if no explicit T_format given on the command-line: */
X      if (! T_option) T_format = "~As~";
X
X      Void T_print(buff, T_format, the_time, NULL, FALSE);
X    }
X
X    Void fputs(buff, stdout);
X    Void fputs("\n", stdout);
X  }
}
X
X
void give_usage_or_help(char U_or_H)
{
X  /* Print usage to stderr so as to avoid any redirection.
X     Print help to stdout so as to make it pipe-able through more: */
X  FILE *out, *fopen(), *popen();
X  Boole more = FALSE;
X
X  if (U_or_H == 'H')
X  {
X    char *places[] = {"/usr/ucb/more", "/usr/bin/more", NULL};
X    char **path;
X    for (path = places; *path && fopen(*path, "r") == NULL; path++) /*...*/;
X    if (*path && (out = popen(*path, "w")) != NULL)
X      more = TRUE;
X    else
X      out = stdout;
X  }
X  else
X    out = stderr;
X
X  fprintf(out, "\
\n\
Usage: Display current time:\n\
X       %s [-LGC] [+T format]\n\
\n\
X       Set system time (must be super-user):\n\
X       %s [-LG] iso3307_date|ls_style_date\n\
X       %s -C unix_clock\n\
\n\
X       Convert from one time format to another:\n\
X       %s [-LG] iso3307_date|ls_style_date -o [-LGC] [+T format]\n\
X       %s -C unix_clock -o [-LGC] [+T format]\n\
\n\
", Progname, Progname, Progname, Progname, Progname);
X
X  if (U_or_H == 'U')
X  {
X    Void fprintf(out, "\
For more help: %s +H\n\
\n\
", Progname);
X  }
X  else
X  {
X    Void fprintf(out, "\
Where:\n\
X    -L: Local time (default)\n\
X    -G: GMT\n\
X    -C: 32-bit clock\n\
X    -o: Output converted value rather than setting time\n\
\n\
X    +T: TIME format, e.g.: edate Jan 1 00:00 +TRD (number of days since Jan 1)\n\
X	N:  Modifier for Numeric dates instead of alpha\n\
X	A:  Modifier for Absolute time since the epoch\n\
X	R:  Modifier for Relative time from now\n\
X	G:  Modifier for GMT dates instead of Local\n\
X	L:  Modifier for Local dates instead of GMT\n\
X	I:  Iso3307 style (same as +TN~YMD.hms~)\n\
X	e:  els style (default, same as +TMDY2)\n\
X	l:  ls style (same as +TMDy)\n\
X	v:  verbose style (same as +TWMD3ZY)\n\
X  Y,M,D,W:  year, month, day, weekday\n\
X    h,m,s:  hour, minutes, seconds\n\
X      2,3:  time as h:m, h:m:s\n\
X	y:  year or time depending on age of file\n\
X	Z:  zone name\n\
\n\
X    T format options also recognize the following modifiers:\n\
X	\\:  Output following character verbatim\n\
X	~:  Toggle spacing off/on\n\
X       %%n:  Output a right justified field 'n' characters wide\n\
X      %%-n:  Output a left justified field 'n' characters wide\n\
X    \"\",'':  Output contents of quoted string verbatim\n\
X	Other characters not recognized by the format are output verbatim\n\
\n\
X    iso3307_date :== YYYYMMDD[.hhmm[ss]]\n\
X        The date must be all digits (except for the optional period\n\
X        preceding the hour hh).  The year YYYY must be not less than 1970\n\
X        and not more than 2106.  The month MM is specified as 01 through\n\
X        12, and the day DD is specified as 01 through 31.  The time, if\n\
X        given, is specified as either 0000 through 2359 or 000000 through\n\
X        235959.  Any unspecified hh, mm, ss parameters will default to 00.\n\
\n\
X    ls_style_date :== MON DD [YYYY] [hh:mm[:ss]]\n\
X        MON is the standard three-letter Ascii abbreviation for the\n\
X        month.  The day DD is specified as 1 through 31.  The year YYYY,\n\
X        if given, must be not less than 1970 and not more than 2106.  If\n\
X        the year is not given then the most recent year gone by will be\n\
X        assumed.  The time, if given, is specified as either 00:00 through\n\
X        23:59 or 00:00:00 through 23:59:59.  Any unspecified hh, mm, ss\n\
X        parameters will default to 0.\n\
\n\
X        If both the year and the time are specified, it doesn't matter\n\
X        which one is last.  Thus, a date such as 'Jan 31 12:34 2001' is\n\
X        equivalent to 'Jan 31 2001 12:34'.  Furthermore, weekdays (e.g.\n\
X        Mon, Tue, Wed, etc.) and timezones (e.g. EST, CDT, MST, etc.)\n\
X        are ignored.  Thus, a date such as 'Wed Jan 31 12:34:56 EST 2001'\n\
X        is equivalent to 'Jan 31 2001 12:34:56'.\n\
\n\
X    unix_clock :== unsigned 32 bit decimal, octal, or hexadecimal value\n\
X        in the range 0 through 4294967294 or 0 through 0xfffffffe (note\n\
X        that -1 is used to represent certain error conditions).  Unix\n\
X        clock values can be listed using 'els -l +TAs'\n\
\n\
");
X
X    Void fprintf(out, "\
Examples:\n\
X    Display the current time in Iso3307 format:\n\
X	%s +TI\n\
\n\
X    Set the system date to April 1, 2000 12:34 Local time:\n\
X	%s 20000401.1234\n\
\n\
X    Set the system date to April 1, 2000 12:34:56 Local time using\n\
X    ls-style dates:\n\
X	%s Apr 1 2000 12:34\n\
\n\
X    Set the system date to January 1, 2050 12:34:56 GMT:\n\
X	%s -G 20500101.123456\n\
\n\
X    Convert the date Feb 29, 1996 12:34:56 GMT into a clock value:\n\
X	%s -G 19960229.123456 -o -C\n\
\n\
X    Convert the clock value of 0x87654321 into Local time and specify\n\
X    the output format:\n\
X	%s -C 0x87654321 +T'%%M %%D, %%Y %%h:%%m:%%s'\n\
\n\
IMPORTANT NOTE:\n\
X    You must use 'edate' instead of 'date' to correctly list the\n\
X    date when set beyond Jan 19, 2038 03:14:08 GMT.  Moreover, on some\n\
X    machines 'edate' must be used to set the date beyond Jan 1, 2000.\n\
\n\
", Progname, Progname, Progname, Progname, Progname, Progname);
X  }
X
X  if (more)
X    pclose(out);
X  
X  return;
}
X
X
char *T_print(char *buff, char *fmt, time_t ftime,
X	      struct tm *fdate, Boole gmt)
{
X  register char *bp;
X  char icase;
X  char delimiter = CNULL;
X  Boole quote_mode = FALSE;
X  int width = 0;
X  Boole width_specified = FALSE;
X  Boole noun_seen = FALSE;
X  Boole tics = FALSE;
X  Boole numeric = FALSE;
X  static char *dow[] = {
X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
X  static char *moy[] = {
X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
X  static time_t the_time = 0;
X  
X  /* Get the current time once during the program: */
X  if (the_time == 0) the_time = time(NULL);
X
X  bp = buff;
X  *bp = CNULL;
X  
X  separated = FALSE;
X  
X  while ((icase = *fmt++) != CNULL)
X  {
X    switch(icase)
X    {
X    case '"':
X    case '\'':
X      if (!quote_mode)
X      {
X	quote_mode = TRUE;
X	delimiter = icase;
X      }
X      else if (delimiter == icase)
X      {
X	quote_mode = FALSE;
X      }
X      break;
X
X    case '%':
X      if (*fmt == '%')
X      {
X	as_is = TRUE;
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      else
X      {
X	width = strtol(fmt, &fmt, 10);
X	width_specified = TRUE;
X      }
X      break;
X
X    case '~':
X      spacing = !spacing;
X      if (spacing) bp = finish(bp, fmt, icase);
X      break;
X
#ifdef UNDERSCORE_MODIFIER
X    case '_':
X      /* Force a field separator.  If you want an underscore, use "\_". */
X      as_is = TRUE;
X      icase = separator;
X      bp = finish(bp, fmt, icase);
X      break;
#endif
X
X    case '\\':
X      /* Print any character following the \quote as-is: */
X      as_is = TRUE;
X      if (*fmt != CNULL)
X      {
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      break;
X
X    case ABS_DATE:
X      tics = TRUE;
X      numeric = FALSE;
X      break;	/* modifiers generate no output */
X      
X    case REL_DATE:
X      tics = TRUE;
X      numeric = FALSE;
X      ftime = the_time - ftime;
X      break;	/* modifiers generate no output */
X      
X    case NUM_DATE:
X      tics = FALSE;
X      numeric = TRUE;
X      break;	/* modifiers generate no output */
X      
X    case ALPHA_DATE:
X      /* THIS FEATURE IS UNDOCUMENTED AS I'M NOT SURE HOW USEFUL IT IS! */
X      tics = FALSE;
X      numeric = FALSE;
X      break;	/* modifiers generate no output */
X      
X    case GMT_DATE:
X      gmt = TRUE;
X      fdate = gmtime32(&ftime);
X      break;	/* modifiers generate no output */
X      
X    case LOCAL_DATE:
X      gmt = FALSE;
X      fdate = localtime32(&ftime);
X      break;	/* modifiers generate no output */
X      
X    default:
X      /* A character within quotes is a noun only when preceded by %: */
X      if (quote_mode && !width_specified)
X      {
X	as_is = TRUE;
X	bp = finish(bp, fmt, icase);
X	break;
X      }
X
X      /* Remove extraneous spacing if appropriate: */
X      if ((fielding || !spacing) && !width_specified)
X      {
X	width = 0;
X	width_specified = TRUE;
X      }
X      
X      /* Determine file's date using local time (if needed): */
X      if (fdate == NULL) fdate = localtime32(&ftime);
X  
X      /* Process a noun: */
X      switch(icase)
X      {
X      case ISO3307_DATE:
X	{
X	  /* Don't turn spacing off via '~' toggle because that might cause
X	     spacing to be turned back on if spacing was currently on. */
X	  Boole save_spacing = spacing;
X	  spacing = FALSE;
X	  bp = T_print(bp, "NYMD.hms", ftime, fdate, gmt);
X	  spacing = save_spacing;
X	}
X	break;
X
X      case ELS_DATE:
X	bp = T_print(bp, "MDY2", ftime, fdate, gmt);
X	break;
X	
X      case LS_DATE:
X	bp = T_print(bp, "MDy", ftime, fdate, gmt);
X	break;
X	
X      case VERBOSE_DATE:
X	bp = T_print(bp, "WMD3ZY", ftime, fdate, gmt);
X	break;
X	
X      case YEAR_OR_TIME:
#       define HALF_A_YEAR  (SECS_PER_YEAR/2)
X	if ((Ulong)the_time - (Ulong)ftime < (Ulong)HALF_A_YEAR)
X	  bp = T_print(bp, "2", ftime, fdate, gmt);
X	else
X	  bp = T_print(bp, " Y", ftime, fdate, gmt);
X	break;
X	
X      case YEARS:
X	{
X	  /* Fix to show 19XX instead of XX: */
X	  int year = fdate->tm_year + 1900;
X	  if (tics)
X	  {
X	    if (!width_specified) width = 2;
X	    Void sprintf(bp, "%*ld", width, ftime / SECS_PER_YEAR);
X	  }
X	  else if (numeric)
X	  {
X	    if (!width_specified || width < 4) width = 4;
X	    Void sprintf(bp, "%0*d", width, year);
X	  }
X	  else
X	  {
X	    if (!width_specified) width = 4;
X	    Void sprintf(bp, "%*d", width, year);
X	  }
X	}
X	break;
X	
X      case MONTHS:
X	if (tics)
X	{
X	  if (!width_specified) width = 3;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_MONTH);
X	}
X	else if (numeric)
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_mon + 1);
X	}
X	else
X	{
X	  if (!width_specified) width = 3;
X	  Void sprintf(bp, "%*s", width, moy[fdate->tm_mon]);
X	}
X	break;
X	
X      case WEEKS:
X	if (tics)
X	{
X	  if (!width_specified) width = 4;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_WEEK);
X	}
X	else if (numeric)
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_wday + 1);
X	}
X	else
X	{
X	  if (!width_specified) width = 3;
X	  Void sprintf(bp, "%*s", width, dow[fdate->tm_wday]);
X	}
X	break;
X	
X      case DAYS:
X	if (tics)
X	{
X	  if (!width_specified) width = 5;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_DAY);
X	}
X	else if (numeric)
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_mday);
X	}
X	else
X	{
X	  if (!width_specified) width = 2;
X	  Void sprintf(bp, "%*d", width, fdate->tm_mday);
X	}
X	break;
X	
X      case HOURS:
X	if (tics)
X	{
X	  if (!width_specified) width = 6;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_HOUR);
X	}
X	else
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_hour);
X	}
X	break;
X	
X      case MINS:
X	if (tics)
X	{
X	  if (!width_specified) width = 8;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_MIN);
X	}
X	else
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_min);
X	}
X	break;
X	
X      case SECS:
X	if (tics)
X	{
X	  /* Use "unsigned" format as this value may use all 32-bits: */
X	  if (!width_specified) width = 10;
X	  Void sprintf(bp, "%*lu", width, ftime);
X	}
X	else
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_sec);
X	}
X	break;
X	
X      case TIME_2:
X	bp = T_print(bp, "h:m", ftime, fdate, gmt);
X	break;
X	
X      case TIME_3:
X	bp = T_print(bp, "h:m:s", ftime, fdate, gmt);
X	break;
X	
X      case ZONE_NAME:
X	if (!width_specified) width = 3;
#ifdef HAVE_TM_ZONE
X	/* tm_zone is right no matter what: */
X	Void sprintf(bp, "%*s", width, fdate->tm_zone);
#else
X	/* tzname is not always what we want: */
X	if (gmt)
X	  Void sprintf(bp, "%*s", width, "GMT");
X	else
X	{
X	  extern char *tzname[2];
X	  Void sprintf(bp, "%*s", width, tzname[fdate->tm_isdst]);
X	}
#endif
X	break;
X	
X      default:
X	/* Output an unrecognized noun as-is: */
X	as_is = TRUE;
X	break;
X      }
X      
X      width_specified = FALSE;
X      if (!as_is) noun_seen = TRUE;
X      bp = finish(bp, fmt, icase);
X      break;
X    }
X  }
X  
X  if (quote_mode) error_msg_exit("Missing delimiter in +T format");
X  if (!noun_seen) error_msg_exit("Incomplete +T format");
X
X  return(bp);
}
X
X
X
char *finish(char *bp, char *fmt, char icase)
{
X  bp += strlen(bp); /* Find where we are at */
X  if (as_is)
X  {      
X    /* Remove separation when it precedes an "as-is" character: */
X    if (separated) bp--;
X    separated = FALSE;
X    
X    /* Add the character as-is: */
X    *bp++ = icase;  *bp = CNULL;
X    as_is = FALSE;
X  }
X  else if (spacing)
X  {
X    if (*fmt != CNULL)
X    {
X      *bp++ = separator; *bp = CNULL;
X      separated = TRUE;
X    }
X    else
X      separated = FALSE;
X  }
X  else
X    separated = FALSE;
X
X  return(bp);
}
SHAR_EOF
chmod 0664 els/edate.c ||
echo 'restore of els/edate.c failed'
Wc_c="`wc -c < 'els/edate.c'`"
test 18009 -eq "$Wc_c" ||
	echo 'els/edate.c: original size 18009, current size' "$Wc_c"
fi
# ============= els/els.c ==============
if test -f 'els/els.c' -a X"$1" != X"-c"; then
	echo 'x - skipping els/els.c (File already exists)'
else
echo 'x - extracting els/els.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/els.c' &&
/******************************************************************************
X  els.c -- An Enhanced LS look-alike with many additional features.
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  This program is provided "as is" in the hopes that it might serve some
X  higher purpose.  If you want this program to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute this program so long as this header remains intact.
X  You may also freely distribute modified versions of this program so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Acknowledgement: This program is based in part on an earlier program
X  written by James M. Gleason (address unknown).  Gleason is responsible
X  for the concept of the generic 'ls' format and the algorithm that
X  generates recursive lists with sorted pointers.
X
X  Note: The most recent version of this program may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  ****************************************************************************/
X
static char *id = "\n\
@(#)els [1.29], May 22, 1996\n\
@(#)Mark Baranowski (markb@elen.utah.edu)\n\
";
X
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
#include <utime.h>
#include <dirent.h>
#include <sys/param.h>
#include "defs.h"
#include "time32.h"
X
X
/* Add /usr/include/sys/stat.h defs if necessary: */
#if !defined(S_ISLNK)
#  define S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK)
#endif
#if !defined(S_ISDIR)
#  define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR)
#endif
X
/* Determine maximum size of file name based on information found in
X   /usr/include/dirent.h */
#if defined(MAXNAMLEN)
#  define MAX_FNAME MAXNAMLEN	/* SunOS4&5, Ultrix, ICS */
#elif defined(_MAXNAMLEN)
#  define MAX_FNAME _MAXNAMLEN	/* HPUX */
#else
#  define MAX_FNAME 255		/* Default: AIX */
#endif
X
/* Maximum size of directory name: */
#if defined(MAXPATHLEN)
#  define MAX_DNAME MAXPATHLEN	/* SunOS4&5, Ultrix, HPUX */
#elif defined(_D_NAME_MAX)
#  define MAX_DNAME _D_NAME_MAX	/* AIX */
#else
#  define MAX_DNAME 511		/* Default: ICS */
#endif
X
/* Maximum size of full name: */
#define  MAX_FULL_NAME  (MAX_DNAME+MAX_FNAME+1)
X
/* Maximum amount of info (besides the full file name) per line: */
#define  MAX_INFO  128
X
/* Maximum amount of output generated per line: */
#define  MAX_OUTPUT  (MAX_INFO+MAX_FULL_NAME)
X
typedef struct Dir_Item
{
X  char *mem;
X  char *fname;
X
X  Boole symlink;
X  Boole dir;
X  Boole dotdir;
X  Boole hidden;
X
X  Boole listable;
X  Boole searchable;
X
X  int errno;
X  struct stat info;
X
X  struct Dir_Item *sort;
X  struct Dir_Item *next;
} Dir_Item;
X
typedef struct Dir_List
{
X  Dir_Item *head;
X  Dir_Item *tail;
} Dir_List;
X
int dir_block_total;
X
X
/********** Global Routines Referenced **********/
X
extern char *malloc();
extern long strtol();
X
/********** Global Routines Defined **********/
X
void do_options(char *options);
void do_options_minus(char *options);
void do_options_plus(char *options);
void give_usage_or_help(char U_or_H);
void analyze_options(void);
void read_dir(Dir_List *dlist, DIR *dir);
void append_dir(Dir_List *dlist, char *fname, int fname_size);
void stat_dir(Dir_List *dlist, char *dname);
void name_dir(Dir_List *dlist, char *dname);
void sort_dir(Dir_List *dlist, char *dname);
void sort_dir_time(Dir_List *dlist, char *dname);
void sort_dir_name(Dir_List *dlist, char *dname);
void list_dir(Dir_List *dlist, char *dname);
void free_dir(Dir_List *dlist);
void filter_file(Dir_Item *file, char *dname);
void list_file(Dir_Item *file, char *dname);
char *G_print(char *buff, char *fmt, char *dname, Dir_Item *file);
char *N_print(char *buff, char *fmt, char *dname, Dir_Item *file);
char *T_print(char *buff, char *fmt, time_t ftime,
X	      struct tm *fdate, Boole gmt);
char *finish(char *bp, char *fmt, char icase);
char *full_name(char *dname, char *fname);
Dir_Item *dir_item_alloc(void);
void dir_item_free(Dir_Item *file);
void *mem_alloc(int size);
void mem_free(void *mem);
char *find_first_occurrence(char *look_for, char *str);
Boole wildcard_match(char *text, char *pattern);
Ulong checksum(char *filename);
void error_msg_exit(char *msg);
int get_stat_blocks(Dir_Item *file);
X
/********** Global Variables Defined **********/
X
int  Argc;
char **Argv;
char *Progname;
X
Boole use_bsd_semantics = TRUE;
Boole use_sys5_semantics = FALSE;
X
Boole multiple_files;
Boole using_full_names;
X
Boole list_hidden;
Boole list_dotdir;
Boole list_long;
Boole list_long_numeric;
Boole list_long_omit_gid;
Boole list_atime;
Boole list_ctime;
Boole list_inode;
Boole list_size_in_blocks;
Boole mark_files;
Boole mark_dirs;
X
/* Recursively list directories */
Boole recursive;
int recursion_level = 0;
X
/* Reverse sort */
Boole reverse_sort;
X
/* Sort by time */
Boole time_sort;
X
/* Interpret -g flag using either BSD or SYS5 semantics */
Boole g_flag;
X
/* Expand symbolic link */
Boole expand_symlink;
X      
/* Expand directories specified as params */
Boole expand_directories;
X
/* List directories as files */
/* NB: This does not affect the expansion of directories, rather, the
X   purpose is to generate directory listings without directory files */
Boole list_directories;
X
/* List regular files only */
Boole list_regular_only;
X
int munge = 0;
Boole stamp = FALSE;
Boole G_option = FALSE;
Boole N_option = FALSE;
Boole T_option = FALSE;
X
/* List of file specs to "E"xclude when listing */
#define MAX_EXCLUDE_LIST  16
int exclude_count;
char *exclude_list[MAX_EXCLUDE_LIST];
Boole exclude_dir[MAX_EXCLUDE_LIST];
X
/* List of file specs to "I"nclude when listing(nothing else is listed) */
#define MAX_INCLUDE_LIST  16
int include_count;
char *include_list[MAX_INCLUDE_LIST];
Boole include_dir[MAX_INCLUDE_LIST];
Boole include_all_dirs = TRUE;
Boole include_all_files = TRUE;
X
char *G_format;
#define INODE			'i'
#define TYPE_IN_ALPHA		't'
#define TYPE_IN_SYMBOLIC	'T'
#define PERMISSION_IN_ALPHA	'p'
#define PERMISSION_IN_NUMERIC	'P'
#define SIZE_IN_BYTES		's'
#define SIZE_IN_BLOCKS		'S'
#define TIME_MODIFIED		'm'
#define TIME_ACCESSED		'a'
#define TIME_MODE_CHANGED	'c'
#define UID_IN_ALPHA		'u'
#define UID_IN_NUMERIC		'U'
#define GID_IN_ALPHA		'g'
#define GID_IN_NUMERIC		'G'
#define _16_BIT_CHECKSUM	'C'
#define NAME_OF_FILE		'n'
#define FULL_NAME_OF_FILE	'N'
#define LINK_COUNT		'l'
#define LINK_NAME		'L'
X
char *N_format;
#define FULL_NAME	'F'
#define DIR_NAME	'd'
#define FILE_NAME	'f'
X
char *T_format;
#define ABS_DATE	'A'
#define REL_DATE	'R'
#define NUM_DATE	'N'
#define ALPHA_DATE	'S'
#define GMT_DATE	'G'
#define LOCAL_DATE	'L'
#define ISO3307_DATE	'I'
#define ELS_DATE	'e'
#define LS_DATE		'l'
#define VERBOSE_DATE	'v'
X
#define YEARS		'Y'
#define MONTHS		'M'
#define WEEKS		'W'
#define DAYS		'D'
#define HOURS		'h'
#define MINS		'm'
#define SECS		's'
#define TIME_2		'2'	/* h:m */
#define TIME_3		'3'	/* h:m:s */
#define YEAR_OR_TIME	'y'
#define ZONE_NAME	'Z'
X
char output_buff[MAX_OUTPUT];
int  output_nlines = 0;
X
Boole spacing = TRUE;
Boole fielding = FALSE;
Boole as_is;
Boole separated;
char separator = ' ';
X
#define IS_MEMBER(c,s)     (strchr(s,c) != NULL)
#define IS_NOT_MEMBER(c,s) (strchr(s,c) == NULL)
X
X
/*****************************************************************************/
X
int main(int argc, char *argv[])
{
X  int i, n_files;
X  char *dname;
X  Dir_List dlist;
X  
X  Argc = argc;
X  Argv = argv;
X  
X  /* Simplify access to the program name */
X  {
X    char *cp;
X    if ((cp = strrchr(argv[0], '/')) != NULL)
X      Progname = cp + 1;
X    else
X      Progname = argv[0];
X  }
X  
#ifndef BUFFER_STDOUT
X  /* Define BUFFER_STDOUT if you desire buffering on stdout and stderr.
X     Buffering stdout and stderr will improve performance, but it will
X     also cause errors to be randomly placed within the output. */
X  setbuf(stdout, NULL);
X  setbuf(stderr, NULL);
#endif /*!BUFFER_STDOUT*/
X  
X  /* Set the default OPTION values */
X
X  if (strcmp(Progname, "els5") == 0)
X    do_options("+5");
X  else
X    do_options("+B");	/* BSD is default */
X
X  list_hidden = FALSE;
X  list_dotdir = FALSE;
X  list_long = FALSE;
X  list_long_numeric = FALSE;
X  list_long_omit_gid = FALSE;
X  list_atime = FALSE;
X  list_ctime = FALSE;
X  list_inode = FALSE;
X  list_size_in_blocks = FALSE;
X  mark_files = FALSE;
X  mark_dirs = FALSE;
X  recursive = FALSE;
X  reverse_sort = FALSE;
X  time_sort = FALSE;
X  g_flag = FALSE;
X  expand_symlink = FALSE;
X  expand_directories = TRUE;
X  list_directories = TRUE;
X  list_regular_only = FALSE;
X  G_format = "n";
X  N_format = "f";
X  T_format = "MDY2";
X  
X  exclude_count = 0;
X  include_count = 0;
X  
X  /* How many files? */
X  n_files = 0;
X  for (i = 1; i < argc; i++)
X  {
X    if (IS_NOT_MEMBER(argv[i][0], "+-"))
X      n_files++;
X  }
X  multiple_files = (n_files > 1);
X  
X  i = 1;
X  do
X  {
X    /* Examine options in the order they arive: */
X    while (i < argc && IS_MEMBER(argv[i][0], "+-"))
X      do_options(argv[i++]);
X    analyze_options();
X    
X    /* Scan the command-line for files and directories to list: */
X    dlist.head = NULL;
X    while (i < argc && IS_NOT_MEMBER(argv[i][0], "+-"))
X    {
X      if (argv[i][0] != CNULL)
X	append_dir(&dlist, argv[i], 0);
X      i++;
X    }
X    
X    /* If there were no valid files, then use the default: */
X    if (dlist.head == NULL)
X      append_dir(&dlist, ".", 0);
X    
X    /* Perform the listing: */
X    dname = "";
X    stat_dir(&dlist, dname);
X    sort_dir(&dlist, dname);
X    list_dir(&dlist, dname);
X    free_dir(&dlist);
X  } while (i < argc);
X    
X  return(0);
}
X
X
X
void do_options(char *options)
{
X  if (*options == '-')
X    do_options_minus(&options[1]);
X  else if (*options == '+')
X    do_options_plus(&options[1]);
}
X
X
X
void do_options_minus(char *options)
{
X  char opt;
X  
X  while ((opt = *options++) != CNULL)
X  {
X    switch(opt)
X    {
X    case 'a': /* List all files  including "." and ".." */
X      list_hidden = TRUE;
X      list_dotdir = TRUE;
X      break;
X
X    case 'A': /* List all files  except "." and ".." */
X      list_hidden = TRUE;
X      list_dotdir = FALSE;
X      break;
X      
X    case 'd': /* Inhibit expansion of directories specified as params */
X      expand_directories = !expand_directories;
X      break;
X      
X    case 'D': /* List directories as files toggle (same as +D) */
X      /* NB: This does not affect the expansion of directories, rather, the
X	 purpose is to generate directory listings without directory files. */
X      list_directories = !list_directories;
X      break;
X      
X    case 'l': /* Long listing as in ls -l */
X      list_long = TRUE;
X      break;
X
X    case 'n': /* Long listing with UID and GID in numeric */
X      list_long = TRUE;
X      list_long_numeric = TRUE;
X      break;
X
X    case 'o': /* Long listing omitting GID */
X      list_long = TRUE;
X      list_long_omit_gid = TRUE;
X      break;
X
X    case 'g': /* Interpret -g flag using either BSD or SYS5 semantics */
X      g_flag = TRUE;
X      break;
X      
X    case 'L': /* Expand symbolic link toggle */
X      expand_symlink = !expand_symlink;
X      break;
X      
X    case 'u': /* List atime toggle */
X      list_atime = !list_atime;
X      break;
X
X    case 'c': /* List ctime toggle */
X      list_ctime = !list_ctime;
X      break;
X
X    case 'i': /* List inode toggle */
X      list_inode = !list_inode;
X      break;
X
X    case 's': /* List size in blocks */
X      list_size_in_blocks = !list_size_in_blocks;
X      break;
X
X    case 'r': /* Reverse sort toggle */
X      reverse_sort = !reverse_sort;
X      break;
X      
X    case 't': /* Sort by time toggle */
X      time_sort = !time_sort;
X      break;
X      
X    case 'R': /* Recursively list all directories found (same as +R) */
X      recursive = !recursive;
X      break;
X
X    case 'F': /* Mark files toggle */
X      mark_files = !mark_files;
X      break;
X      
X    case 'p': /* Mark directories toggle */
X      mark_dirs = !mark_dirs;
X      break;
X      
X    default:
X      give_usage_or_help('U');
X      exit(1);
X      break;
X    }
X  }
X  
X  return;
}
X
X
X
void do_options_plus(char *options)
{
X  char opt;
X  while ((opt = *options++) != CNULL)
X  {
X    switch(opt)
X    {
X    case 'B':
X      use_bsd_semantics = TRUE;
X      use_sys5_semantics = FALSE;
X      break;
X
X    case '5':
X      use_bsd_semantics = FALSE;
X      use_sys5_semantics = TRUE;
X      break;
X
X    case 'D': /* List directories as files toggle (same as -D) */
X      /* NB: This does not affect the expansion of directories, rather, the
X	 purpose is to generate directory listings without directory files. */
X      list_directories = !list_directories;
X      break;
X      
X    case 'R': /* Recursively list all directories found (same as -R) */
X      recursive = !recursive;
X      break;
X
X    case 'M': /* MUNGE dates and file names */
X      munge++;	/* Go to next munge-level */
X      list_directories = FALSE;
X
X      if (munge == 1)
X      {
X	/* Defer to any previous G option from the command-line: */
X	if (!G_option) { do_options("+Gmn"); G_option = FALSE; }
X      }
X      else if (munge == 2)
X      {
X	/* Defer to any previous G option from the command-line: */
X	if (!G_option) { do_options("+Gan"); G_option = FALSE; }
X      }
X      else if (munge == 3)
X      {
X	/* Defer to any previous G option from the command-line: */
X	if (!G_option) { do_options("+G'chdate -m %m%n'"); G_option = FALSE; }
X      }
X      else if (munge == 4)
X      {
X	/* Defer to any previous G option from the command-line: */
X	if (!G_option) { do_options("+G'chdate -m %m -a %a%n'");
X			 G_option = FALSE; }
X      }
X
X      /* Do not defer to any previous T option, as all munge levels imply
X	 that we want ISO3307 dates: */
X      do_options("+TI");
X
X      /* Defer to any previous N option from the command-line: */
X      if (!N_option) { do_options("+NF");  N_option = FALSE; }
X
X      /* Additional options may follow any MUNGE command */
X      break;
X      
X    case 'U': /* Generate Untouch script commands */
X      {
X	int i, nout;
X	time_t the_time;
X	char *cwd, dir[MAX_DNAME];
X	the_time = time(NULL);
X	cwd = dir;
X	getcwd(cwd, MAX_DNAME);
X	if (strncmp("/tmp_mnt/", cwd, 9) == 0) cwd += 8; /* automount fodder */
X	printf("\
#!/bin/sh\n\
#\n\
# 'untouch' shell script created on %s\
# from the directory %s\n\
# using the command:", ctime32(&the_time), cwd);
X	  for (i = 0, nout = 0; i < Argc && nout < 128; i++)
X	  {
X	    printf(" %s", Argv[i]);
X	    nout += strlen(Argv[i]);
X	  }
X	if (i < Argc) printf(" ...");
X	printf("\n\
#\n\
# Save the following output into a script file so that it can then be run\n\
# at a later time in order to recover file dates following a 'touch':\n\
#\n\
");
X      }
X      do_options("+G'[ -f %N ] && [ `els +GC %N` -eq %C ] && chdate -m %m%N'");
X      do_options("+TI");
X      list_regular_only = TRUE;
X      break;
X
X    case 'F': /* Specify field separtor */
X      fielding = TRUE;
X      separator = *options++;
X      break;
X
X    case 'G': /* Specify the general format */
X      G_option = TRUE;	/* Command-line args take precedence */
X      G_format = options;
X      options = "";	/* No more options */
X      break;
X      
X    case 'N': /* Specify the name format */
X      N_option = TRUE;	/* Command-line args take precedence */
X      N_format = options;
X      options = "";	/* No more options */
X      break;
X      
X    case 'T': /* Specify the time & date format */
X      T_option = TRUE;	/* Command-line args take precedence */
X      T_format = options;
X      options = "";	/* No more options */
X      break;
X      
X    case 'e': /* Exclude specified directories */
X    case 'E': /* Exclude specified files */
X      if (exclude_count < MAX_EXCLUDE_LIST)
X      {
X	exclude_list[exclude_count] = options;
X	exclude_dir[exclude_count] = (opt == 'e');
X	exclude_count++;
X      }
X      else
X	Void fprintf(stderr,
X		     "%s: Too many patterns: +%c%s ignored\n",
X		     Progname, opt, options);
X      options = "";	/* No more options */
X      break;
X
X    case 'i': /* Include specified directories */
X    case 'I': /* Include specified files */
X      if (include_count < MAX_INCLUDE_LIST)
X      {
X	include_list[include_count] = options;
X	include_dir[include_count] = (opt == 'i');
X	include_count++;
X
X	if (opt == 'i')	include_all_dirs = FALSE;
X	if (opt == 'I')	include_all_files = FALSE;
X      }
X      else
X	Void fprintf(stderr,
X		     "%s: Too many patterns: +%c%s ignored\n",
X		     Progname, opt, options);
X      options = "";	/* No more options */
X      break;
X      
X    case 'S': /* Select a Stamp format */
X      stamp = TRUE;
X      {
X	int i, nout;
X	time_t the_time;
X	char *cwd, dir[MAX_DNAME];
X	the_time = time(NULL);
X	cwd = dir;
X	getcwd(cwd, MAX_DNAME);
X	if (strncmp("/tmp_mnt/", cwd, 9) == 0) cwd += 8; /* automount fodder */
X	printf("	Stamp performed on %s", ctime32(&the_time));
X	printf("	from the directory %s\n", cwd);
X	printf("	using the command:");
X	for (i = 0, nout = 0; i < Argc && nout < 128; i++)
X	{
X	  printf(" %s", Argv[i]);
X	  nout += strlen(Argv[i]);
X	}
X	if (i < Argc) printf(" ...");
X	printf("\n \n"); /* The embedded space is deliberate */
X      }
X
X      if (*options == 'o')
X      {
X	/*list_directories = FALSE;*/ /*Think about this*/
X	do_options("+GCtPUG~%8s~mNL"); /* ~%8s~ lists device files as X,Y */
X	do_options("+TI");
X	do_options("-A");
X
X	printf("\
CKSum T Mode   UID   GID    Size       Date          File\n\
----- - ----   ---   ---   ------ --------------- ----------\n\
");
X      }
X      else if (*options == 'O')
X      {
X	/*list_directories = FALSE;*/ /*Think about this*/
X	do_options("+GtPUG~%8s~mNL");  /* ~%8s~ lists device files as X,Y */
X	do_options("+TI");
X	do_options("-A");
X
X	printf("\
T Mode   UID   GID    Size       Date          File\n\
- ----   ---   ---   ------ --------------- ----------\n\
");
X      }
X      else if (*options == 'C' || *options == 'c')
X      {
X	/* Tracking directory dates is pointless for sorce code: */
X	list_directories = FALSE;
X
X	do_options("+Gm  u%-6sNL");
X	do_options("+TI");
X	do_options("-A");
X
X	if (*options == 'C')
X	{
X	  /* Exclude libraries, object, lint files: */
X	  do_options("+E*.[ao]");
X	  do_options("+E*.ln");
X	  
X	  /* Exclude SCCS and RCS directories: */
X	  do_options("+eRCS");
X	  do_options("+eSCCS");
X	  
X	  /* Exclude tilde backup files generated by Emacs: */
X	  do_options("+E.~*");
X	  do_options("+E*~");
X	  
X	  /* I use numbers on the end of my files to designate versions; e.g.
X	     els.c.49, els.c.50, els.c.51, etc. thus I choose to ignore them */
X	  do_options("+E*.[0-9][0-9]");
X	  do_options("+E*.[0-9][0-9][0-9]");
X	}
X
X	printf("\
X     Date        Owner    Size      File\n\
---------------  -----    -----  ----------\n\
");
X      }
X      else
X      {
X	do_options("+H");	/* Give help */
X      }
X      options++;
X      break;
X      
X    case 'V': /* Give version and bits of information: */
X      puts(id);
X      printf("	Max dir name: %d\n", MAX_DNAME);
X      printf("	Max file name: %d\n", MAX_FNAME);
#ifdef NO_ST_BLOCKS
X      printf("	NO_ST_BLOCKS\n");
#endif
#ifdef HAVE_TM_ZONE
X      printf("	HAVE_TM_ZONE\n");
#endif
X      printf("\n");
X      exit(1);
X      break;
X
X    case 'H': /* Give help and exit: */
X      give_usage_or_help('H');
X      exit(1);
X      break;
X
X    default: /* Give usage and exit: */
X      give_usage_or_help('U');
X      exit(1);
X      break;
X    }
X  }
X  
X  return;
}
X
X
X
void give_usage_or_help(char U_or_H)
{
X  /* Print usage to stderr so as to avoid any redirection.
X     Print help to stdout so as to make it pipe-able through more: */
X  FILE *out, *fopen(), *popen();
X  Boole more = FALSE;
X
X  if (U_or_H == 'H')
X  {
X    char *places[] = {"/usr/ucb/more", "/usr/bin/more", NULL};
X    char **path;
X    for (path = places; *path && fopen(*path, "r") == NULL; path++) /*...*/;
X    if (*path && (out = popen(*path, "w")) != NULL)
X      more = TRUE;
X    else
X      out = stdout;
X  }
X  else
X    out = stderr;
X
X  Void fprintf(out, "\
\n\
Usage: %s [-aAcdFgilLnoprRstu] [+HB5D] [+GTN format] [+F char] \\\n\
X	[+IEie pattern] [+S char] [+U] files\n\
\n\
", Progname);
X
X  if (U_or_H == 'U')
X  {
X    Void fprintf(out, "\
For more help: %s +H\n\
\n\
", Progname);
X  }
X  else
X  {
X    Void fprintf(out, "\
ls-like options:\n\
\n\
");
X    Void fprintf(out, "\
X    -a: List all files including . and ..\n\
X    -A: List all files except . and ..\n\
X    -c: List time of last status change(BSD & SYS5) and sort(BSD)\n\
X    -d: List directories as files, but don't list their contents\n\
X    -F: Mark files\n\
X    -g: List group(BSD), long listing omitting UID(SYS5)\n\
X    -i: List inode number\n\
X    -l: Long listing using BSD or SYS5 semantics\n\
X    -L: List actual file rather than symbolic link\n\
X    -n: Long listing using numeric UIDs and GIDs(SYS5)\n\
X    -o: Long listing omitting GIDs(SYS5)\n\
X    -p: Mark directories\n\
X    -r: Reverse the sort\n\
X    -R: Recursively list contents of all directories\n\
X    -s: List size in blocks(SYS5) or in KBytes(BSD)\n\
X    -t: Sort files according to time\n\
X    -u: List time of last access\n\
\n\
");
X
X    Void fprintf(out, "\
enhanced ls options:\n\
\n\
");
X    Void fprintf(out, "\
X    +H: Give HELP\n\
X    +B: Use BSD semantics\n\
X    +5: Use SYS5 semantics\n\
X    +D: Don't list directories as files, but list their contents (has the\n\
X	opposite effect of -d)\n\
\n\
X    +G: GENERAL format, e.g.:  els +G~tp~lusmnL (same as els -l)\n\
X	i:  inode number\n\
X      t,T:  type of file in alpha, symbolic\n\
X      p,P:  permission in alpha, numeric\n\
X      l,L:  link count, name\n\
X      u,U:  UID in alpha, numeric\n\
X      g,G:  GID in alpha, numeric\n\
X      s,S:  size in bytes, blocks\n\
X    m,a,c:  time modified, accessed, status changed\n\
X      n,N:  name of file, path/file\n\
X	C:  16 bit checksum (performed on regular files only and\n\
X	    access times are preserved)\n\
\n\
X    +T: TIME format, e.g.:  els -la +TRD (give age of file in days)\n\
X	N:  Modifier for Numeric dates instead of alpha\n\
X	A:  Modifier for Absolute time since the epoch\n\
X	R:  Modifier for Relative time from now\n\
X	G:  Modifier for GMT dates instead of Local\n\
X	L:  Modifier for Local dates instead of GMT\n\
X	I:  Iso3307 style (same as +TN~YMD.hms~)\n\
X	e:  els style (default, same as +TMDY2)\n\
X	l:  ls style (same as +TMDy)\n\
X	v:  verbose style (same as +TWMD3ZY)\n\
X  Y,M,D,W:  year, month, day, weekday\n\
X    h,m,s:  hour, minutes, seconds\n\
X      2,3:  time as h:m, h:m:s\n\
X	y:  year or time depending on age of file\n\
X	Z:  zone name\n\
\n\
X    +N: NAME format, e.g.:  els -laR +NF\n\
X	F:  Full file name (same as +Nd/f)\n\
X	d:  Directory name\n\
X	f:  File name\n\
\n\
X    G, T, and N format options also recognize the following modifiers:\n\
X	\\:  Output following character verbatim\n\
X	~:  Toggle spacing off/on\n\
X       %%n:  Output a right justified field 'n' characters wide\n\
X      %%-n:  Output a left justified field 'n' characters wide\n\
X    \"\",'':  Output contents of quoted string verbatim\n\
X	Other characters not recognized by the format are output verbatim\n\
\n\
");
X
X    Void fprintf(out, "\
X    +F: Field separator character, e.g.:  els -la +F: +TI\n\
\n\
X    +I: Include specified files, e.g.:  els -lagR +I\"*.c\"\n\
X    +E: Exclude specified files, e.g.:  els -lagR +E\"*.[ao]\"\n\
X    +i: Include specified directories, e.g.:  els -lagR +i\"[a-m]*\"\n\
X    +e: Exclude specified directories, e.g.:  els -lagR +e\"SCCS\"\n\
\n\
X    +S: STAMP: The output from this option is used for recording the state\n\
X	of a hierarchy for future comparison to determine any changes.\n\
\n\
X	C:  Stamp a source code hierarchy excluding *.[ao], *.ln, *~,\n\
X	    and SCCS/RCS files, e.g.:  els +SC -R /home/myfiles/src\n\
X	c:  Same as +SC, except nothing is excluded\n\
\n\
X	O:  Stamp an operating system hierarchy (no files are excluded and\n\
X	    more detail given), e.g.:  els +SO / -R /etc /dev /usr /var\n\
X	o:  Same as +SO, except checksumming is also performed\n\
\n\
X    +U: UNTOUCH: Create a script which can be saved into a file for later\n\
X	recovery of modification dates following a /bin/touch, e.g.:\n\
X	    els +U -R * > untouch  # Create untouch script file\n\
X	    /bin/touch *           # Clobber modification dates\n\
X	    sh untouch             # Recover modification dates\n\
\n\
");
X  }
X  
X  if (more)
X    pclose(out);
X  
X  return;
}
X
X
X
void analyze_options()
{
X  char *cp;
X
X
X  /* root gets special treatment under BSD: */
X  if (use_bsd_semantics && geteuid() == 0) list_hidden = TRUE;
X
X
X  /* The -g flag implies long listing for SYS5: */
X  if (use_sys5_semantics && g_flag) list_long = TRUE;
X
X
X  /* Define Name and Time formats for a long listing, unless either +N
X     or +T were explicitly defined on the command-line: */
X  if (list_long)
X  {
X    /* Defer to any previous N option from the command-line: */
X    if (!N_option) { do_options("+Nf");    N_option = FALSE; }
X
X    /* Defer to any previous T option from the command-line: */
X    if (!T_option) { do_options("+TMDY2"); T_option = FALSE; }
X  }
X
X
X  /* Generate G_format if no implicit G_format given on the command-line: */
X  if (!G_option && munge == 0)
X  {
X    static char cmd[32];
X    if (list_long)
X    {
X      char *uid, *gid;
X
X      /* NOTE: SYS5 interprets the -g flag to mean "exclude the UID *and*
X	 perform a long listing", whereas BSD interprets the -g flag to mean
X	 "include GID *if* performing a long listing".
X
X	 It's also true that BSD has no -n or -o flag, however, just for fun
X	 let's also support -n and -o for BSD with the understanding that
X	 when combined -n and -o that the -g flag will always follow SYS5
X	 semantics. */
X
X      if (list_long_numeric && list_long_omit_gid)
X      {
X	uid = "%-8U";
X	gid = "";
X      }
X      else if (list_long_numeric)
X      {
X	uid = "%-8U";
X	gid = "%-8G";
X      }
X      else if (list_long_omit_gid)
X      {
X	uid = "u";
X	gid = "";
X      }
X      else
X      {
X	uid = "u";
X	gid = "g";
X      }
X
X      if (use_sys5_semantics || list_long_numeric || list_long_omit_gid)
X      {
X	/* Take -g flag to mean omit UID in this context: */
X	if (g_flag) uid = "";
X      }
X      else
X      {
X	/* Handle -g flag a la BSD: */
X	if (!g_flag) gid = "";
X      }
X
X      sprintf(cmd, "+G%s%s~tp~l%s%ss%cnL",
X	      list_inode ? "i" : "",
X	      list_size_in_blocks ? "S" : "",
X	      uid,
X	      gid,
X
X	      list_atime ? TIME_ACCESSED :
X	      list_ctime ? TIME_MODE_CHANGED :
X	      /*list_mtime*/ TIME_MODIFIED);
X    }
X    else
X      sprintf(cmd, "+G%s%sn",
X	      list_inode ? "i" : "",
X	      list_size_in_blocks ? "S" : "");
X    
X    do_options(cmd);
X    G_option = FALSE; /* G_format was not specified on the command-line */
X  }
X
X
X  /* Munge-levels 2 and 4 deal with access times.  It is much more
X     interesting to see the access time of the file being pointed at rather
X     than the access time of symbolic link itself.  Munge level 3 changes
X     the modification dates, thus if the file is a symbolic link we want to
X     use the modification time of the file being pointed at. */
X  if (munge >= 2 && munge <= 4) do_options("-L");
X
X  
X  /* Determine ahead of time whether we will be using full file names: */
X  using_full_names = FALSE;
X  cp = G_format;
X  while (*cp != CNULL)
X  {
X    if (*cp == '"' || *cp == '\'')
X    {
X      /* els +G"'Look for percentN (i.e. %N) embedded in string'" */
X      char delimiter = *cp++;
X      while (*cp != CNULL && *cp != delimiter) 
X      {
X	cp++;
X	Void strtol(cp, &cp, 10); /* Scan past width (if any) */
X	if (*cp == FULL_NAME_OF_FILE) using_full_names = TRUE;	
X      }
X    }
X    else if (*cp == '\\')
X      cp++;
X    else if (*cp == FULL_NAME_OF_FILE)  
X      using_full_names = TRUE;
X    if (*cp != CNULL) cp++;
X  }
X  cp = N_format;
X  while (*cp != CNULL)
X  {
X    if (*cp == '"' || *cp == '\'')
X    {
X      /* els +N"'Look for percentN (i.e. %N) embedded in string'" */
X      char delimiter = *cp++;
X      while (*cp != CNULL && *cp != delimiter) 
X      {
X	cp++;
X	Void strtol(cp, &cp, 10); /* Scan past width (if any) */
X	if (*cp == FULL_NAME_OF_FILE) using_full_names = TRUE;	
X      }
X    }
X    else if (*cp == '\\')
X      cp++;
X    else if (*cp == FULL_NAME || *cp == DIR_NAME)  
X      using_full_names = TRUE;
X    if (*cp != CNULL) cp++;
X  }
}
X
X
X
void read_dir(Dir_List *dlist, DIR *dir)
{  
X  register struct dirent *dp;
X  
X  dlist->head = NULL;
X  while ((dp = readdir(dir)) != NULL)
X    append_dir(dlist, dp->d_name, dp->d_reclen);
X  
X  return;
}
X
X
X
void append_dir(Dir_List *dlist,
X		char *fname,
X		int fname_size)
{
X  register Dir_Item *file;
X  
X  /* NB: Rather than have to calculate strlen(fname), I use d_reclen
X     as a rough guess.  It is assumed that d_reclen >= strlen(fname). */
X
X  /* Append file name to directory list: */
X  file = dir_item_alloc();
X  if (dlist->head == NULL)
X    dlist->head = file;
X  else
X    dlist->tail->next = file;
X  dlist->tail = file;
X  file->next = NULL;
X  
X  if (fname_size > 0)
X  {
X    file->mem = mem_alloc(fname_size+1); /* +1 required by Linux */
X    file->fname = file->mem;
X    strcpy(file->fname, fname);
X  }
X  else
X  {
X    /* Command line arguments do not need to have mem alloc'ed: */
X    file->mem = NULL;
X    file->fname = fname;
X  }
X  
X  file->errno = 0;
X  file->sort = file;
X  
X  return;
}
X
X
X
void stat_dir(Dir_List *dlist, char *dname)
{
X  register Dir_Item *file;
X  char *path;
X  int sts;
X  int save_errno = errno;
X  
X  dir_block_total = 0;
X
X  for (file = dlist->head; file != NULL; file = file->next)
X  {
X    char *slash;
X
X    /* Start by setting as many attributes as possible.  These attributes
X       must not depend on any information from stat(): */
X
X    path = full_name(dname, file->fname);
X
X    /* Deal with cases such as: "els ../dir ../.. ../../.xyz" */
X    slash = strrchr(file->fname, '/');
X    if (slash)
X      slash++;
X    else
X      slash = file->fname;
X    file->dotdir = strcmp(slash,".") == 0 || strcmp(slash,"..") == 0;
X    file->hidden = !file->dotdir && slash[0] == '.';
X    
X    /* Set the remaining attributes that are dependent on stat(): */
X
X    errno = 0;
# if defined(S_IFLNK)
X    /* System has symbolic links: */
X    sts = lstat(path, &file->info);
X    file->symlink = (sts == 0 && S_ISLNK(file->info.st_mode));
X    if (file->symlink && expand_symlink)
X    {
X      /* Return info regarding the actual file rather than the link: */
X      sts = stat(path, &file->info);
X    }
# else
X    /* System does not have symbolic links: */
X    sts = stat(path, &file->info);
X    file->symlink = FALSE;
# endif
X    file->errno = errno;
X
X    if (!file->errno)
X    {
X      file->dir = S_ISDIR(file->info.st_mode);
X      filter_file(file, dname);
X      if (file->listable) dir_block_total += get_stat_blocks(file);
X    }
X    else
X    {
X      file->dir = FALSE;
X      /* Even though an error was detected during stat(), we have enough
X	 information to see if it is to be filtered from our display list.
X	 If the file qualifies for filtering, then no error needs be reported
X	 during the output stage! */
X      filter_file(file, dname);
X    }
X  }
X
X  errno = save_errno;
X  
X  return;
}
X
X
X
void name_dir(Dir_List *dlist, char *dname)
{
X  if (!using_full_names)
X  {
X    if (output_nlines > 0) putchar('\n');
X    if (stamp)
X      Void printf("Directory: %s\n", dname);
X    else
X    {
X      if (multiple_files || recursion_level > 1)
X      {
X	if (dlist == NULL && use_bsd_semantics)
X	  /* Special BSD header if error: */
X	  Void printf("%s unreadable\n", dname);
X	else
X	  Void printf("%s:\n", dname);
X      }
X
X      if (dlist != NULL && list_long && dlist->head->dir)
X	Void printf("total %d\n", use_bsd_semantics ?
X		    dir_block_total/2 : dir_block_total);
X    }
X    output_nlines++;
X  }
X
X  if (dlist == NULL)
X    Void fprintf(stderr, "%s: %s: Permission denied\n",
X		 Progname, dname);
}	
X
X
X
void sort_dir(Dir_List *dlist, char *dname)
{
X  if (time_sort)
X    sort_dir_time(dlist, dname);
X
X  else if (list_ctime && use_bsd_semantics)
X    /* BSD's ls -c implies ls -ct, but SYS5's does NOT! */
X    sort_dir_time(dlist, dname);
X
X  else
X    sort_dir_name(dlist, dname);
X  
X  return;
}
X
X
X
void sort_dir_time(Dir_List *dlist, char *dname)
{
X  register Dir_Item *ptr1, *ptr2;
X  register Dir_Item *dp;
X  char sort_by;
X  
X  if (list_atime)
X    /* Sort by time accessed regardless of what's in G_format: */
X    sort_by = TIME_ACCESSED;
X  else if (list_ctime)
X    /* Sort by time mode changed regardless of what's in G_format: */
X    sort_by = TIME_MODE_CHANGED;
X  else
X  {
X    /* Find first occurrence of either m, a, or c in G_format: */
X    char *mac = find_first_occurrence("mac", G_format);
X  
X    if (mac == NULL || *mac == CNULL)
X      sort_by = TIME_MODIFIED;	/* default */
X    else
X      sort_by = *mac;
X  }
X  
X  for (ptr1 = dlist->head; ptr1 != NULL; ptr1 = ptr1->next)
X  {
X    for (ptr2 = ptr1->next; ptr2 != NULL; ptr2 = ptr2->next)
X    {
X      if (sort_by == TIME_MODIFIED)
X      {
X	if (reverse_sort ?
X	    (Ulong)ptr2->sort->info.st_mtime < (Ulong)ptr1->sort->info.st_mtime :
X	    (Ulong)ptr2->sort->info.st_mtime > (Ulong)ptr1->sort->info.st_mtime)
X	{
X	  /* Switch the entry pointers */
X	  dp = ptr1->sort;
X	  ptr1->sort = ptr2->sort;
X	  ptr2->sort = dp;
X	}
X      }
X
X      else if (sort_by == TIME_ACCESSED)
X      {
X	if (reverse_sort ?
X	    (Ulong)ptr2->sort->info.st_atime < (Ulong)ptr1->sort->info.st_atime :
X	    (Ulong)ptr2->sort->info.st_atime > (Ulong)ptr1->sort->info.st_atime)
X	{
X	  /* Switch the entry pointers */
X	  dp = ptr1->sort;
X	  ptr1->sort = ptr2->sort;
X	  ptr2->sort = dp;
X	}
X      }
X
X      else if (sort_by == TIME_MODE_CHANGED)
X      {
X	if (reverse_sort ?
X	    (Ulong)ptr2->sort->info.st_ctime < (Ulong)ptr1->sort->info.st_ctime :
X	    (Ulong)ptr2->sort->info.st_ctime > (Ulong)ptr1->sort->info.st_ctime)
X	{
X	  /* Switch the entry pointers */
X	  dp = ptr1->sort;
X	  ptr1->sort = ptr2->sort;
X	  ptr2->sort = dp;
X	}
X      }
X    }
X  }
X  
X  return;
}
X
X
X
void sort_dir_name(Dir_List *dlist, char *dname)
{
X  register Dir_Item *ptr1, *ptr2;
X  register Dir_Item *dp;
X  
X  for (ptr1 = dlist->head; ptr1 != NULL; ptr1 = ptr1->next)
X  {
X    for (ptr2 = ptr1->next; ptr2 != NULL; ptr2 = ptr2->next)
X    {
X      if (reverse_sort ?
X	  strcmp(ptr2->sort->fname, ptr1->sort->fname) > 0 :
X	  strcmp(ptr2->sort->fname, ptr1->sort->fname) < 0)
X      {
X	/* Switch the entries(by pointer only) */
X	dp = ptr1->sort;
X	ptr1->sort = ptr2->sort;
X	ptr2->sort = dp;
X      }
X    }
X  }
X  
X  return;
}
X
X
X
void list_dir(Dir_List *dlist,
X	      char *dname)
{
X  register Dir_Item *ptr;
X  Dir_List sub_dlist;
X  char cur_dname[MAX_FULL_NAME];
X  DIR *dir;
X  
X  recursion_level++;
X  sub_dlist.head = NULL;
X  
X  for (ptr = dlist->head; ptr != NULL; ptr = ptr->next)
X  {
X    list_file(ptr->sort, dname);
X
X    /* Recursively list directory (if appropriate): */
X    if (ptr->sort->searchable ||
X	(recursion_level == 1 && ptr->sort->dotdir))
X    {
X      if (expand_directories && (recursion_level == 1 || recursive))
X      {
X	if (recursion_level == 1 || !ptr->sort->dotdir)
X	{
X	  append_dir(&sub_dlist, ptr->sort->fname, 0);
X	}
X      }
X    }
X  }
X  
X  if (sub_dlist.head != NULL)
X  {
X    for (ptr = sub_dlist.head; ptr != NULL; ptr = ptr->next)
X    {
X      if (dname[0] == CNULL || strcmp(dname, ".") == 0)
X	Void strcpy(cur_dname, ptr->sort->fname);
X      else
X      {
X	char *fmt;
X	if (dname[strlen(dname)-1] == '/')
X	  fmt = "%s%s";
X	else
X	  fmt = "%s/%s";
X	Void sprintf(cur_dname, fmt, dname, ptr->sort->fname);
X      }
X      
X      if ((dir = opendir(cur_dname)) == NULL)
X      {
X	name_dir(NULL, cur_dname);
X      }
X      else
X      {
X	Dir_List cur_dlist;
X	read_dir(&cur_dlist, dir);
X	stat_dir(&cur_dlist, cur_dname);
X	name_dir(&cur_dlist, cur_dname);
X	sort_dir(&cur_dlist, cur_dname);
X	list_dir(&cur_dlist, cur_dname);
X	free_dir(&cur_dlist);
X	closedir(dir);
X      }
X    }
X    free_dir(&sub_dlist);
X  }
X  
X  recursion_level--;
X
X  return;
}
X
X
X
void free_dir(Dir_List *dlist)
{
X  register Dir_Item *ptr1, *ptr2;
X  
X  ptr1 = dlist->head;
X  while (ptr1 != NULL)
X  {
X    ptr2 = ptr1->next;
X    if (ptr1->mem != NULL) mem_free(ptr1->mem);
X    dir_item_free(ptr1);
X    ptr1 = ptr2;
X  }
X  dlist->head = NULL;
X
X  return;
}
X
X
X
void filter_file(Dir_Item *file, char *dname)
{
X  int i;
X  Boole file_is_listable;
X  Boole file_is_searchable;
X  Boole found;
X
X  if (file->fname[0] == CNULL)
X  {
X    file_is_listable = FALSE;
X    file_is_searchable = FALSE;
X  }
X  else
X  {
X    file_is_listable = TRUE;
X    
X    /******************************************/
X    /* Check for ".", "..", and ".xxx" files: */
X    /******************************************/
X    
X    /* The user asked for the file explicitly on the command-line
X       whenever recursion_level == 0, thus, always grant such a
X       request; otherwise, hidden files must meet the list_* criteria.
X       */
X
X    if (file->dotdir)
X      file_is_listable = list_dotdir || recursion_level == 0;
X    else if (file->hidden)
X      file_is_listable = list_hidden || recursion_level == 0;
X    
X    /******************************************/
X    /* Check file name against "E"xclude list */
X    /******************************************/
X    if (file_is_listable && exclude_count > 0)
X    {
X      i = 0;
X      found = FALSE;
X      while (i < exclude_count && !found)
X      {
X	if ((!file->dir && !exclude_dir[i]) ||
X	    ( file->dir &&  exclude_dir[i]))
X	{
X	  if (file->dir && strchr(exclude_list[i], '/'))
X	    /* Exclude a directory having a path component: */
X	    found = wildcard_match(full_name(dname, file->fname),
X				   exclude_list[i]);
X	  else
X	    /* Exclude a file/directory having *no* path component: */
X	    found = wildcard_match(file->fname,
X				   exclude_list[i]);
X	}
X	i++;
X      }
X      file_is_listable = !found;
X    }
X    
X    /******************************************/
X    /* Check file name against "I"nclude list */
X    /******************************************/
X    if (file_is_listable && include_count > 0)
X    {
X      i = 0;
X      found = FALSE;
X      while (i < include_count && !found)
X      {
X	if ((!file->dir && !include_dir[i]) ||
X	    ( file->dir &&  include_dir[i]))
X	  found = wildcard_match(file->fname, include_list[i]);
X	i++;
X      }
X      file_is_listable = found || (!file->dir && include_all_files);
X
X
X      /* When listing via inclusion, we want to search sub directories even
X	 if they don't meet our search criterion, otherwise we will end up
X	 with hardly any files or subdirectories to search!  The exception
X	 to this rule is when the user explicitly specifies directories
X	 to include using the "+i" option. */
X      file_is_searchable = file->dir && !file->symlink &&
X	(file_is_listable || include_all_dirs);
X    }
X    else
X    {
X      /* When listing normally or via exclusion, we want to search
X	 subdirectories only if they didn't get scratched from our search
X	 list.  Even still we will end up searching quite a few files! */
X      file_is_searchable = file->dir && !file->symlink && 
X	file_is_listable;
X    }
X  }
X  
X  /* If listing regular files only then exclude all other files: */
X
X  if (list_regular_only && file_is_listable)
X    file_is_listable = ((file->info.st_mode & S_IFMT) == S_IFREG);
X
X  /* FINALLY, file is listable if it met the list_* filtering criteria and
X     it isn't a dir, or it is listable if the list_* criteria also specifies
X     directories (unless this is the very first recursion level).  All this
X     is needed to mimic "ls" behavior: */
X  
X  file->listable =
X    file_is_listable && (!file->dir ||
X			 !expand_directories ||
X			 (list_directories && recursion_level > 0));
X
X  file->searchable = file_is_searchable;
X
X
/*#define SHOW_FILTERING*/
#ifdef SHOW_FILTERING
X      printf("sl, d, dd, h: %d, %d, %d, %d -- l, s: %d, %d -- %s\n",
X	     file->symlink, file->dir,
X	     file->dotdir, file->hidden,
X	     file->listable, file->searchable, file->fname);
#endif
X
X  return;
}
X
X
X
void list_file(Dir_Item *file,
X	       char *dname)
{
X  if (file->errno)
X  {
X    /* An error was found while getting the file status.  Report the 
X       error only if the file fits our list or search criteria: */
X    if (file->listable || file->searchable)
X    {
X      char msg[MAX_FULL_NAME];
X      int save_errno = errno;
X      Void sprintf(msg, "%s: %s", Progname, full_name(dname, file->fname));
X      errno = file->errno;
X      perror(msg);
X      errno = save_errno;
X    }
X  }
X  else
X  {
X    /* No errors were detected while getting the file status.  List the
X       file only if it fits our list criteria: */
X    if (file->listable)
X    {
X      Void G_print(output_buff, G_format, dname, file);
X      Void fputs(output_buff, stdout);
X      output_nlines++;
X    }
X  }
X  
X  return;
}
X
X
X
char *G_print(char *buff,
X	      char *fmt,
X	      char *dname,
X	      Dir_Item *file)
{
X  register char *bp;
X  char icase;
X  char delimiter = CNULL;
X  Boole quote_mode = FALSE;
X  int width = 0;
X  Boole width_specified = FALSE;
X  Boole noun_seen = FALSE;
X  register struct stat *info = &file->info;
X  int ftype;
X  Boole isdevice;
X  static struct passwd *passwd = NULL;
X  static struct group  *group  = NULL;
X
X  ftype = (info->st_mode & S_IFMT);
X  isdevice = (ftype == S_IFBLK || ftype == S_IFCHR);
X
X  bp = buff;
X  *bp = CNULL;
X  
X  spacing = TRUE;
X  as_is = FALSE;
X  separated = FALSE;
X
X  while ((icase = *fmt++) != CNULL)
X  {
X    switch(icase)
X    {
X    case '"':
X    case '\'':
X      if (!quote_mode)
X      {
X	quote_mode = TRUE;
X	delimiter = icase;
X      }
X      else if (delimiter == icase)
X      {
X	quote_mode = FALSE;
X      }
X      break;
X
X    case '%':
X      if (*fmt == '%')
X      {
X	as_is = TRUE;
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      else
X      {
X	width = strtol(fmt, &fmt, 10);
X	width_specified = TRUE;
X      }
X      break;
X
X    case '~':
X      spacing = !spacing;
X      if (spacing) bp = finish(bp, fmt, icase);
X      break;
X
#ifdef UNDERSCORE_MODIFIER
X    case '_':
X      /* Force a field separator.  If you want an underscore, use "\_". */
X      as_is = TRUE;
X      icase = separator;
X      bp = finish(bp, fmt, icase);
X      break;
#endif
X
X    case '\\':
X      /* Print any character following the \quote as-is: */
X      as_is = TRUE;
X      if (*fmt != CNULL)
X      {
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      break;
X
X    default:
X      /* A character within quotes is a noun only when preceded by %: */
X      if (quote_mode && !width_specified)
X      {
X	as_is = TRUE;
X	bp = finish(bp, fmt, icase);
X	break;
X      }
X
X      /* Remove extraneous spacing if appropriate: */
X      if ((fielding || !spacing) && !width_specified)
X      {
X	width = 0;
X	width_specified = TRUE;
X      }
X
X      /* Process a noun: */
X      switch(icase)
X      {
X      case TYPE_IN_ALPHA:
X	{
X	  char type;
X	  if      (ftype == S_IFREG)  /* Regular */
X	    type = '-';
X	  else if (ftype == S_IFDIR)  /* Directory */
X	    type = 'd';
X	  else if (ftype == S_IFCHR)  /* Char Special */
X	    type = 'c';
X	  else if (ftype == S_IFBLK)  /* Block Special */
X	    type = 'b';
#ifdef S_IFIFO
X	  else if (ftype == S_IFIFO)  /* Fifo */
X	    type = 'p';
#endif
#ifdef S_IFLNK
X	  else if (ftype == S_IFLNK)  /* Symbolic Link */
X	    type = 'l';
#endif
#ifdef S_IFSOCK
X	  else if (ftype == S_IFSOCK) /* Socket */
X	    type = 's';
#endif
X	  else
X	    type = '?';
X
X	  if (!width_specified) width = 1;
X	  Void sprintf(bp, "%*c", width, type);
X	}
X	break;
X	
X      case TYPE_IN_SYMBOLIC:
X	{
X	  char type;
X	  if      (ftype == S_IFREG)  /* Regular */
X	    type = (info->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH) ? '*' : ' ');
X	  else if (ftype == S_IFDIR)  /* Directory */
X	    type = '/';
X	  else if (ftype == S_IFCHR)  /* Char Special */
X	    type = ' ';
X	  else if (ftype == S_IFBLK)  /* Block Special */
X	    type = ' ';
#ifdef S_IFIFO
X	  else if (ftype == S_IFIFO)  /* Fifo */
X	    type = ' ';
#endif
#ifdef S_IFLNK
X	  else if (ftype == S_IFLNK)  /* Symbolic Link */
X	    type = '@';
#endif
#ifdef S_IFSOCK
X	  else if (ftype == S_IFSOCK) /* Socket */
X	    type = '=';
#endif
X	  else
X	    type = '?';
X
X	  if (!width_specified) width = 1;
X	  Void sprintf(bp, "%*c", width, type);
X	}
X	break;
X	
#ifdef TYPE_IN_NUMERIC
X      case TYPE_IN_NUMERIC:
X	/* This is probably non-portable to some of the more obscure Unixes: */
X	if (!width_specified) width = 2;
X	Void sprintf(bp, "%*d", width, ftype >> 12);
X	break;
#endif /*TYPE_IN_NUMERIC*/
X	
X      case PERMISSION_IN_ALPHA:
X	{
X	  char perm[16], *pp;
X	  pp = perm;
X	  if (info->st_mode & S_IRUSR)
X	    *pp++ = 'r';
X	  else
X	    *pp++ = '-';
X	  
X	  if (info->st_mode & S_IWUSR)
X	    *pp++ = 'w';
X	  else
X	    *pp++ = '-';
X	  
X	  if ((info->st_mode & S_IXUSR) && (info->st_mode & S_ISUID))
X	    *pp++ = 's';
X	  else if (info->st_mode & S_ISUID)
X	    *pp++ = 'S';
X	  else if (info->st_mode & S_IXUSR)
X	    *pp++ = 'x';
X	  else
X	    *pp++ = '-';
X	  
X	  if (info->st_mode & S_IRGRP)
X	    *pp++ = 'r';
X	  else
X	    *pp++ = '-';
X	  
X	  if (info->st_mode & S_IWGRP)
X	    *pp++ = 'w';
X	  else
X	    *pp++ = '-';
X	  
X	  if ((info->st_mode & S_IXGRP) && (info->st_mode & S_ISGID))
X	    *pp++ = 's';
X	  else if (info->st_mode & S_ISGID)
X	    *pp++ = 'S';
X	  else if (info->st_mode & S_IXGRP)
X	    *pp++ = 'x';
X	  else
X	    *pp++ = '-';
X	  
X	  if (info->st_mode & S_IROTH)
X	    *pp++ = 'r';
X	  else
X	    *pp++ = '-';
X	  
X	  if (info->st_mode & S_IWOTH)
X	    *pp++ = 'w';
X	  else
X	    *pp++ = '-';
X	  
X	  if ((info->st_mode & S_IXOTH) && (info->st_mode & S_ISVTX))
X	    *pp++ = 't';
X	  else if (info->st_mode & S_ISVTX)
X	    *pp++ = 'T';
X	  else if (info->st_mode & S_IXOTH)
X	    *pp++ = 'x';
X	  else
X	    *pp++ = '-';
X	  
X	  *pp = CNULL;
X
X	  if (!width_specified) width = 9;
X	  Void sprintf(bp, "%*s", width, perm);
X	}
X	break;
X	
X      case PERMISSION_IN_NUMERIC:
X	if (!width_specified || width < 4) width = 4;
X	Void sprintf(bp, "%0*o", width, (info->st_mode & 07777));
X	break;
X	
X      case INODE:
#ifdef S5R2_WORKALIKE
X	/* SYS5 R2 assumes inodes never take more than 5 places; however,
X	   BSD always assumes 6 places. */
X	if (!width_specified) width = (use_bsd_semantics ? 6 : 5);
#else
X	/* Most modern SYS5s assume inodes take 6 places */
X	if (!width_specified) width = 6;
#endif
X	Void sprintf(bp, "%*d", width, info->st_ino);
X	break;
X	
X      case SIZE_IN_BYTES:
X	if (!width_specified) width = (use_bsd_semantics ? 8 : 7);
X	if (isdevice)
X	{
X	  char major[8], minor[8], str[16];
X	  if (sizeof(dev_t) == sizeof(short))
X	  {
X	    /* The following works for the majority of unixes: */
X	    Void sprintf(major, "%lu", (Ulong)info->st_rdev >> 8);
X	    Void sprintf(minor, "%lu", (Ulong)info->st_rdev & 0xff);
X	  }
X	  else
X	  {
X	    /* The following works for SunOS5.x, but is it portable? */
X	    Void sprintf(major, "%lu", (Ulong)info->st_rdev >> 18);
X	    Void sprintf(minor, "%lu", (Ulong)info->st_rdev & 0x3fff);
X	  }
X	  Void sprintf(str, "%s,%*s",
X		       major,
X		       (!spacing ? 0 : (use_bsd_semantics ? 4 : 3)),
X		       minor);
X	  Void sprintf(bp, "%*s", width, str);
X	}
X	else
X	  Void sprintf(bp, "%*ld", width, info->st_size);
X	break;
X	
X      case SIZE_IN_BLOCKS:
X	if (!width_specified) width = 4;
X	Void sprintf(bp, "%*ld", width,
X		     use_bsd_semantics ?
X		     get_stat_blocks(file)/2 :
X		     get_stat_blocks(file));
X	break;
X	
X      case LINK_COUNT:
X	if (!width_specified) width = (use_bsd_semantics ? 2 : 3);
X	Void sprintf(bp, "%*d", width, info->st_nlink);
X	break;
X	
X      case TIME_MODIFIED:
X      case TIME_ACCESSED:
X      case TIME_MODE_CHANGED:
X	if (!width_specified)
X	  bp = T_print(bp, T_format,
X		       icase == TIME_MODIFIED ? info->st_mtime :
X		       icase == TIME_ACCESSED ? info->st_atime :
X		       /*icase == TIME_MODE_CHANGED ?*/ info->st_ctime,
X		       NULL, FALSE);
X	else
X	{
X	  char tmp[MAX_INFO];
X	  Void T_print(tmp, T_format,
X		       icase == TIME_MODIFIED ? info->st_mtime :
X		       icase == TIME_ACCESSED ? info->st_atime :
X		       /*icase == TIME_MODE_CHANGED ?*/ info->st_ctime,
X		       NULL, FALSE);
X	  Void sprintf(bp, "%*s", width, tmp);
X	}
X	break;
X
X      case UID_IN_ALPHA:
X	/* We don't need to look it up again if we already have it */
X	if (passwd == NULL || passwd->pw_uid != info->st_uid)
X	  passwd = getpwuid(info->st_uid);
X
X	if (!width_specified) width = -8;
X	if (passwd != NULL)
X	  Void sprintf(bp, "%*s", width, passwd->pw_name);
X	else
X	  /* No name, print the number instead: */
X	  Void sprintf(bp, "%*lu", width, (Ulong)info->st_uid);
X	break;
X
X      case GID_IN_ALPHA:
X	/* We don't need to look it up again if we already have it */
X	if (group == NULL || group->gr_gid != info->st_gid)
X	  group = getgrgid(info->st_gid);
X
X	if (!width_specified) width = -8;
X	if (group != NULL)
X	  Void sprintf(bp, "%*s", width, group->gr_name);
X	else
X	  /* No name, print the number instead: */
X	  Void sprintf(bp, "%*lu", width, (Ulong)info->st_gid);
X	break;
X	
X      case UID_IN_NUMERIC:
X	if (!width_specified) width = 5;
X	Void sprintf(bp, "%*lu", width, (Ulong)info->st_uid);
X	break;
X
X      case GID_IN_NUMERIC:
X	if (!width_specified) width = 5;
X	Void sprintf(bp, "%*lu", width, (Ulong)info->st_gid);
X	break;
X	
X      case _16_BIT_CHECKSUM:
X	/* Perform checksum on regular files and preserve the access times: */
X	{
X	  Ulong cksum = 0;
X	  if (ftype == S_IFREG)
X	  {
X	    char *path = full_name(dname, file->fname);
X	    struct utimbuf tb;
X	    cksum = checksum(path);
X	    tb.actime = info->st_atime;
X	    tb.modtime = info->st_mtime;
X	    Void utime(path, &tb);
X	  }
X	  if (!width_specified || width < 5) width = 5;
X	  Void sprintf(bp, "%0*lu", width, cksum);
X	}
X	break;
X	
X      case NAME_OF_FILE:
X      case FULL_NAME_OF_FILE:
X	{
X	  char *N_format_to_use;
X	  if (icase == FULL_NAME_OF_FILE && !N_option)
X	    N_format_to_use = "F";
X	  else
X	    N_format_to_use = N_format;
X	
X	  if (!width_specified)
X	    bp = N_print(bp, N_format_to_use, dname, file);
X	  else
X	  {
X	    char tmp[MAX_INFO];
X	    Void N_print(tmp, N_format_to_use, dname, file);
X	    Void sprintf(bp, "%*s", width, tmp);
X	  }
X	}
X	break;
X	
X      case LINK_NAME:
#     if defined(S_IFLNK)
X	if (file->symlink && !expand_symlink)
X	{
X	  char path_name[MAX_FULL_NAME], link_name[MAX_FULL_NAME];
X	  int i = 0;
X	  Void sprintf(path_name, "%s/%s",
X		       dname[0] ? dname : ".",
X		       file->fname);
X	  i = readlink(path_name, link_name, MAX_FULL_NAME);
X	  if (i < 0 || i >= MAX_FULL_NAME) i = 0;
X	  link_name[i] = CNULL;
X	  if (fielding)
X	    strcpy(bp, link_name);
X	  else
X	    sprintf(bp, "->%s%s", (spacing ? " " : ""), link_name);
X
X	  if (mark_files)
X	  {
X	    /* Mark file using info from actual file rather than link: */
X	    Dir_Item tmp;
X	    tmp = *file;
X	    if (stat(full_name(dname, tmp.fname), &tmp.info) == 0)
X	    {
X	      bp += strlen(bp);
X	      bp = G_print(bp, "T", dname, &tmp);
X	    }
X	  }
X	}
#     endif
X	break;
X	
X      default:
X	/* Output an unrecognized noun as-is: */
X	as_is = TRUE;
X	break;
X      }
X      
X      width_specified = FALSE;
X      if (!as_is) noun_seen = TRUE;
X      bp = finish(bp, fmt, icase);
X      break;
X    }
X  }
X  
X  while (bp != buff && isspace(bp[-1])) bp--; /* remove trailing spaces */
X  *bp++ = '\n';
X  *bp = CNULL;
X  
X  if (quote_mode) error_msg_exit("Missing delimiter in +G format");
X  if (!noun_seen) error_msg_exit("Incomplete +G format");
X
X  return(bp);
}
X
X
X
char *N_print(char *buff, char *fmt,
X	      char *dname, Dir_Item *file)
{
X  register char *bp;
X  char icase;
X  char delimiter = CNULL;
X  Boole quote_mode = FALSE;
X  int width = 0;
X  Boole width_specified = FALSE;
X  Boole noun_seen = FALSE;
X  char *slash = NULL;
X
X  /*****************************************************/
X  /* Put dir name and/or file name into cononical form */
X  /*****************************************************/
X
X  if (using_full_names)
X  {
X    if (strcmp(dname, "/") == 0)
X    {
X      /* Do nothing */;
X    }
X    else if ((slash = strrchr(file->fname, '/')) != NULL)
X    {
X      if (slash == file->fname)
X	dname = "/";
X      else
X	dname = file->fname;
X      *slash = CNULL;
X      file->fname = slash+1;
X    }
X    else
X    {
X      int last_dir_char = strlen(dname) - 1;
X      if (last_dir_char >= 0 && dname[last_dir_char] == '/')
X      {
X	slash = &dname[last_dir_char];
X	*slash = CNULL;
X      }
X    }
X  }
X    
X  bp = buff;
X  *bp = CNULL;
X
X  separated = FALSE;
X
X  while ((icase = *fmt++) != CNULL)
X  {
X    switch(icase)
X    {
X    case '"':
X    case '\'':
X      if (!quote_mode)
X      {
X	quote_mode = TRUE;
X	delimiter = icase;
X      }
X      else if (delimiter == icase)
X      {
X	quote_mode = FALSE;
X      }
X      break;
X
X    case '%':
X      if (*fmt == '%')
X      {
X	as_is = TRUE;
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      else
X      {
X	width = strtol(fmt, &fmt, 10);
X	width_specified = TRUE;
X      }
X      break;
X
X    case '~':
X      spacing = !spacing;
X      if (spacing) bp = finish(bp, fmt, icase);
X      break;
X
#ifdef UNDERSCORE_MODIFIER
X    case '_':
X      /* Force a field separator.  If you want an underscore, use "\_". */
X      as_is = TRUE;
X      icase = separator;
X      bp = finish(bp, fmt, icase);
X      break;
#endif
X
X    case '\\':
X      /* Print any character following the \quote as-is: */
X      as_is = TRUE;
X      if (*fmt != CNULL)
X      {
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      break;
X
X    default:
X      /* A character within quotes is a noun only when preceded by %: */
X      if (quote_mode && !width_specified)
X      {
X	as_is = TRUE;
X	bp = finish(bp, fmt, icase);
X	break;
X      }
X
X      /* Remove extraneous spacing if appropriate: */
X      if ((fielding || !spacing) && !width_specified)
X      {
X	width = 0;
X	width_specified = TRUE;
X      }
X
X      /* Process a noun: */
X      switch(icase)
X      {
X      case FULL_NAME:
X	{
X	  char *d, *s;
X	  if (strcmp(dname, "/") == 0)
X	  {
X	    d = ""; s = "/";
X	  }
X	  else if (dname[0] != CNULL && strcmp(dname, ".") != 0)
X	  {
X	    d = dname; s = "/";
X	  }
X	  else
X	  {
X	    d = ""; s = "";
X	  }
X
X	  if (!width_specified)
X	    Void sprintf(bp, "%s%s%s", d, s, file->fname);
X	  else
X	  {
X	    char tmp[MAX_FULL_NAME];
X	    Void sprintf(tmp, "%s%s%s", d, s, file->fname);
X	    Void sprintf(bp, "%*s", width, tmp);
X	  }
X	}
X	break;
X	
X      case DIR_NAME:
X	if (dname[0] != CNULL)
X	  Void sprintf(bp, "%*s", width, dname);
X	else
X	  Void sprintf(bp, "%*s", width, ".");
X	break;
X	
X      case FILE_NAME:
X	Void sprintf(bp, "%*s", width, file->fname);
X	break;
X	
X      default:
X	/* Output an unrecognized noun as-is: */
X	as_is = TRUE;
X	break;
X      }
X      
X      width_specified = FALSE;
X      if (!as_is) noun_seen = TRUE;
X      bp = finish(bp, fmt, icase);
X      break;
X    }
X  }
X
X  if (mark_files)
X  {
X    if (!file->symlink)
X      bp = G_print(bp, "T", dname, file);
X  }
X  else if (mark_dirs && file->dir)
X  {
X    *bp++ = '/';
X    *bp = CNULL;
X  }
X
X  if (slash != NULL)
X    *slash = '/';
X
X  if (quote_mode) error_msg_exit("Missing delimiter in +N format");
X  if (!noun_seen) error_msg_exit("Incomplete +N format");
X
X  return(bp);
}
X
X
X
char *T_print(char *buff, char *fmt, time_t ftime,
X	      struct tm *fdate, Boole gmt)
{
X  register char *bp;
X  char icase;
X  char delimiter = CNULL;
X  Boole quote_mode = FALSE;
X  int width = 0;
X  Boole width_specified = FALSE;
X  Boole noun_seen = FALSE;
X  Boole tics = FALSE;
X  Boole numeric = FALSE;
X  static char *dow[] = {
X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
X  static char *moy[] = {
X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
X  static time_t the_time = 0;
X  
X  /* Get the current time once during the program: */
X  if (the_time == 0) the_time = time(NULL);
X
X  bp = buff;
X  *bp = CNULL;
X  
X  separated = FALSE;
X  
X  while ((icase = *fmt++) != CNULL)
X  {
X    switch(icase)
X    {
X    case '"':
X    case '\'':
X      if (!quote_mode)
X      {
X	quote_mode = TRUE;
X	delimiter = icase;
X      }
X      else if (delimiter == icase)
X      {
X	quote_mode = FALSE;
X      }
X      break;
X
X    case '%':
X      if (*fmt == '%')
X      {
X	as_is = TRUE;
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      else
X      {
X	width = strtol(fmt, &fmt, 10);
X	width_specified = TRUE;
X      }
X      break;
X
X    case '~':
X      spacing = !spacing;
X      if (spacing) bp = finish(bp, fmt, icase);
X      break;
X
#ifdef UNDERSCORE_MODIFIER
X    case '_':
X      /* Force a field separator.  If you want an underscore, use "\_". */
X      as_is = TRUE;
X      icase = separator;
X      bp = finish(bp, fmt, icase);
X      break;
#endif
X
X    case '\\':
X      /* Print any character following the \quote as-is: */
X      as_is = TRUE;
X      if (*fmt != CNULL)
X      {
X	icase = *fmt++;
X	bp = finish(bp, fmt, icase);
X      }
X      break;
X
X    case ABS_DATE:
X      tics = TRUE;
X      numeric = FALSE;
X      break;	/* modifiers generate no output */
X      
X    case REL_DATE:
X      tics = TRUE;
X      numeric = FALSE;
X      ftime = the_time - ftime;
X      break;	/* modifiers generate no output */
X      
X    case NUM_DATE:
X      tics = FALSE;
X      numeric = TRUE;
X      break;	/* modifiers generate no output */
X      
X    case ALPHA_DATE:
X      /* THIS FEATURE IS UNDOCUMENTED AS I'M NOT SURE HOW USEFUL IT IS! */
X      tics = FALSE;
X      numeric = FALSE;
X      break;	/* modifiers generate no output */
X      
X    case GMT_DATE:
X      gmt = TRUE;
X      fdate = gmtime32(&ftime);
X      break;	/* modifiers generate no output */
X      
X    case LOCAL_DATE:
X      gmt = FALSE;
X      fdate = localtime32(&ftime);
X      break;	/* modifiers generate no output */
X      
X    default:
X      /* A character within quotes is a noun only when preceded by %: */
X      if (quote_mode && !width_specified)
X      {
X	as_is = TRUE;
X	bp = finish(bp, fmt, icase);
X	break;
X      }
X
X      /* Remove extraneous spacing if appropriate: */
X      if ((fielding || !spacing) && !width_specified)
X      {
X	width = 0;
X	width_specified = TRUE;
X      }
X      
X      /* Determine file's date using local time (if needed): */
X      if (fdate == NULL) fdate = localtime32(&ftime);
X  
X      /* Process a noun: */
X      switch(icase)
X      {
X      case ISO3307_DATE:
X	{
X	  /* Don't turn spacing off via '~' toggle because that might cause
X	     spacing to be turned back on if spacing was currently on. */
X	  Boole save_spacing = spacing;
X	  spacing = FALSE;
X	  bp = T_print(bp, "NYMD.hms", ftime, fdate, gmt);
X	  spacing = save_spacing;
X	}
X	break;
X
X      case ELS_DATE:
X	bp = T_print(bp, "MDY2", ftime, fdate, gmt);
X	break;
X	
X      case LS_DATE:
X	bp = T_print(bp, "MDy", ftime, fdate, gmt);
X	break;
X	
X      case VERBOSE_DATE:
X	bp = T_print(bp, "WMD3ZY", ftime, fdate, gmt);
X	break;
X	
X      case YEAR_OR_TIME:
#       define HALF_A_YEAR  (SECS_PER_YEAR/2)
X	if ((Ulong)the_time - (Ulong)ftime < (Ulong)HALF_A_YEAR)
X	  bp = T_print(bp, "2", ftime, fdate, gmt);
X	else
X	  bp = T_print(bp, " Y", ftime, fdate, gmt);
X	break;
X	
X      case YEARS:
X	{
X	  /* Fix to show 19XX instead of XX: */
X	  int year = fdate->tm_year + 1900;
X	  if (tics)
X	  {
X	    if (!width_specified) width = 2;
X	    Void sprintf(bp, "%*ld", width, ftime / SECS_PER_YEAR);
X	  }
X	  else if (numeric)
X	  {
X	    if (!width_specified || width < 4) width = 4;
X	    Void sprintf(bp, "%0*d", width, year);
X	  }
X	  else
X	  {
X	    if (!width_specified) width = 4;
X	    Void sprintf(bp, "%*d", width, year);
X	  }
X	}
X	break;
X	
X      case MONTHS:
X	if (tics)
X	{
X	  if (!width_specified) width = 3;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_MONTH);
X	}
X	else if (numeric)
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_mon + 1);
X	}
X	else
X	{
X	  if (!width_specified) width = 3;
X	  Void sprintf(bp, "%*s", width, moy[fdate->tm_mon]);
X	}
X	break;
X	
X      case WEEKS:
X	if (tics)
X	{
X	  if (!width_specified) width = 4;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_WEEK);
X	}
X	else if (numeric)
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_wday + 1);
X	}
X	else
X	{
X	  if (!width_specified) width = 3;
X	  Void sprintf(bp, "%*s", width, dow[fdate->tm_wday]);
X	}
X	break;
X	
X      case DAYS:
X	if (tics)
X	{
X	  if (!width_specified) width = 5;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_DAY);
X	}
X	else if (numeric)
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_mday);
X	}
X	else
X	{
X	  if (!width_specified) width = 2;
X	  Void sprintf(bp, "%*d", width, fdate->tm_mday);
X	}
X	break;
X	
X      case HOURS:
X	if (tics)
X	{
X	  if (!width_specified) width = 6;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_HOUR);
X	}
X	else
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_hour);
X	}
X	break;
X	
X      case MINS:
X	if (tics)
X	{
X	  if (!width_specified) width = 8;
X	  Void sprintf(bp, "%*ld", width, ftime / SECS_PER_MIN);
X	}
X	else
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_min);
X	}
X	break;
X	
X      case SECS:
X	if (tics)
X	{
X	  /* Use "unsigned" format as this value may use all 32-bits: */
X	  if (!width_specified) width = 10;
X	  Void sprintf(bp, "%*lu", width, ftime);
X	}
X	else
X	{
X	  if (!width_specified || width < 2) width = 2;
X	  Void sprintf(bp, "%0*d", width, fdate->tm_sec);
X	}
X	break;
X	
X      case TIME_2:
X	bp = T_print(bp, "h:m", ftime, fdate, gmt);
X	break;
X	
X      case TIME_3:
X	bp = T_print(bp, "h:m:s", ftime, fdate, gmt);
X	break;
X	
X      case ZONE_NAME:
X	if (!width_specified) width = 3;
#ifdef HAVE_TM_ZONE
X	/* tm_zone is right no matter what: */
X	Void sprintf(bp, "%*s", width, fdate->tm_zone);
#else
X	/* tzname is not always what we want: */
X	if (gmt)
X	  Void sprintf(bp, "%*s", width, "GMT");
X	else
X	{
X	  extern char *tzname[2];
X	  Void sprintf(bp, "%*s", width, tzname[fdate->tm_isdst]);
X	}
#endif
X	break;
X	
X      default:
X	/* Output an unrecognized noun as-is: */
X	as_is = TRUE;
X	break;
X      }
X      
X      width_specified = FALSE;
X      if (!as_is) noun_seen = TRUE;
X      bp = finish(bp, fmt, icase);
X      break;
X    }
X  }
X  
X  if (quote_mode) error_msg_exit("Missing delimiter in +T format");
X  if (!noun_seen) error_msg_exit("Incomplete +T format");
X
X  return(bp);
}
X
X
X
char *finish(char *bp, char *fmt, char icase)
{
X  bp += strlen(bp); /* Find where we are at */
X  if (as_is)
X  {      
X    /* Remove separation when it precedes an "as-is" character: */
X    if (separated) bp--;
X    separated = FALSE;
X    
X    /* Add the character as-is: */
X    *bp++ = icase;  *bp = CNULL;
X    as_is = FALSE;
X  }
X  else if (spacing)
X  {
X    if (*fmt != CNULL)
X    {
X      *bp++ = separator; *bp = CNULL;
X      separated = TRUE;
X    }
X    else
X      separated = FALSE;
X  }
X  else
X    separated = FALSE;
X
X  return(bp);
}
X
X
X
char *full_name(char *dname,
X		char *fname)
{
X  static char full_name[MAX_FULL_NAME];
X  
X  /* Gather any information on this parameter */
X  if (dname[0] == CNULL || strcmp(dname, ".") == 0)
X  {
X    return(fname);
X  }
X  else
X  {
X    if (strcmp(dname, "/") == 0) dname = "";
X    Void sprintf(full_name, "%s/%s", dname, fname);
X    return(full_name);
X  }
}
X
X
X
#define DIR_LIST_CHUNK  (2048/sizeof(Dir_Item))
Dir_List dir_item_list = {NULL, NULL};
Dir_Item *dir_item_alloc(void)
{
X  register Dir_Item *file;
X  if (dir_item_list.head == NULL)
X  {
X    dir_item_list.head = mem_alloc(DIR_LIST_CHUNK*sizeof(Dir_Item));
X    dir_item_list.tail = dir_item_list.head+DIR_LIST_CHUNK-1;
X    for (file = dir_item_list.head; file != dir_item_list.tail; file++)
X      file->next = file+1;
X    file->next = NULL;
X  }
X
X  file = dir_item_list.head;
X  dir_item_list.head = dir_item_list.head->next;
X  return(file);
}
X
X
X
void dir_item_free(Dir_Item *file)
{
X  if (dir_item_list.head == NULL)
X    dir_item_list.head = file;
X  else
X    dir_item_list.tail->next = file;
X    
X  dir_item_list.tail = file;
X  file->next = NULL;
X  return;
}
X
X
X
void *mem_alloc(int size)
{
X  char *ptr;
X  
X  ptr = malloc(size);
X  if (ptr == NULL)
X  {
X    Void fprintf(stderr, "%s: Unable to allocate working memory\n",
X		 Progname);
X    exit(1);
X  }
X  
X  return((void *)ptr);
}
X
X
X
void mem_free(void *mem)
{
X  free(mem);
X  return;
}
X
X
X
char *find_first_occurrence(char *look_for, char *str)
{
X  char *found = NULL;
X  while (*str && (found = strchr(look_for, *str)) == NULL) str++;
X  return(found);
}
X
X
X
Boole wildcard_match(char *text, char *pattern)
{
X  for (;;)
X  {
X    switch(*pattern)
X    {
X    case NULL:
X      /* END OF PATTERN: */
X      return(*text == NULL);
X      break;
X
X    case '?':
X      /* SCAN ONE CHAR OF ANY KIND: */
X      if (*text == NULL) return(FALSE);
X      text++;
X      pattern++;
X      break;
X
X    case '[':
X      /* MATCH ONE CHAR FROM A RANGE: */
X      {
X	Boole match = FALSE;
X	Boole first = TRUE;
X	Boole negate;
X	char lastpat = '\0';
X	pattern++;
X	negate = (*pattern == '^');
X	if (negate) pattern++;
X	while (*pattern != NULL && *pattern != ']')
X	{
X	  if (*pattern == '\\')
X	  {
X	    pattern++;
X	    if (*pattern == NULL) return(FALSE); /* Missing ] */
X	    match |= (*text == *pattern);
X	  }
X	  else if (*pattern == '-' && !first)
X	  {
X	    pattern++;
X	    if (*pattern == NULL) return(FALSE); /* Missing ] */
X	    match |= (lastpat <= *text && *text <= *pattern);
X	  }
X	  else
X	  {
X	    match |= (*text == *pattern);
X	  }
X	  lastpat = *pattern;
X	  first = FALSE;
X	  pattern++;
X	}
X	if (*pattern == NULL) return(FALSE); /* Missing ] */
X	match ^= negate;
X	if (!match) return(FALSE);
X      }
X      text++;
X      pattern++;
X      break;
X
X    case '\\':
X      /* MATCH ONE SPECIAL CHAR: */
X      pattern++;
X      if (*text == NULL) return(FALSE);
X      if (*text != *pattern) return(FALSE);
X      text++;
X      if (*pattern != NULL) pattern++; /* consider pattern of: "xyz\^@abc" */
X      break;
X
X    case '*':
X      /* SCAN ZERO OR MORE CHARS: */
X      while (*pattern == '*') pattern++;
X      if (*pattern == NULL) return(TRUE);
X
X      /* Speed things up by scanning ahead for the first match of a REGULAR
X	 CHAR.  This step is optional and could be skipped without affecting
X	 the final results of this algorithm: */
X      if (IS_NOT_MEMBER(*pattern, "?[\\"))
X	while (*text != NULL && *text != *pattern) text++;
X
X      /* Recursively look for consecutive groups of matches.  If we return
X	 to the top level without exhausting the text, we succeeded: */
X      while (*text != NULL && !wildcard_match(text, pattern)) text++;
X
X      return(*text != NULL);
X      break;
X
X    default:
X      /* MATCH ONE REGULAR CHAR: */
X      if (*text == NULL) return(FALSE);
X      if (*text != *pattern) return(FALSE);
X      text++;
X      pattern++;
X      break;
X    }
X  }
X  /*NOTREACHED*/
}
X
X
X
Ulong checksum(char *filename)
{
X  FILE *fp;
X  Ulong checksum;
X  int c;
X
X  checksum = 0;
X  
X  /* PERFORM 16-BIT CHECKSUM (same algorithm as /bin/sum): */
X  fp = fopen(filename, "r");
X  if (fp != NULL)
X  {
X    Boole rotate;
X    while ((c = getc(fp)) != EOF)
X    {
X      rotate = (checksum & 1) != 0;
X      checksum >>= 1;
X      if (rotate) checksum |= 0x8000;
X      checksum += c;
X      checksum &= 0xffff;
X    }
X    fclose(fp);
X  }
X  
X  return(checksum);
}
X
X
X
/* Display error message and exit: */
void error_msg_exit(char *msg)
{
X  fprintf(stderr, "%s: %s\n", Progname, msg);
X  exit(1);
}
X
X
X
int get_stat_blocks(Dir_Item *file)
{
#ifdef NO_ST_BLOCKS
X  /* Crude approximation to what the real st_blocks should be: */
X  return((file->info.st_size + 511)/512);
#else
X  return(file->info.st_blocks);
#endif
}
SHAR_EOF
chmod 0664 els/els.c ||
echo 'restore of els/els.c failed'
Wc_c="`wc -c < 'els/els.c'`"
test 66869 -eq "$Wc_c" ||
	echo 'els/els.c: original size 66869, current size' "$Wc_c"
fi
# ============= els/defs.h ==============
if test -f 'els/defs.h' -a X"$1" != X"-c"; then
	echo 'x - skipping els/defs.h (File already exists)'
else
echo 'x - extracting els/defs.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/defs.h' &&
/******************************************************************************
X  defs.h -- Ubiquitous defines for ELS
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  These functions are provided "as is" in the hopes that they might serve some
X  higher purpose.  If you want these functions to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute these functions so long as this header remains intact.
X  You may also freely distribute modified versions of these functions so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Note: The most recent version of these functions may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  ****************************************************************************/
X
#ifndef ELS__DEFS_H
#define ELS__DEFS_H
X
#define Void		(void)
#define Local		static
typedef char		Boole;
typedef	unsigned char	Uchar;
typedef	unsigned short	Ushort;
typedef	unsigned int	Uint;
typedef	unsigned long	Ulong;
X
#undef   NULL
#define  NULL	0
#define  CNULL	'\0'
#define  TRUE	(Boole)1
#define  FALSE	(Boole)0
X
#define  SECS_PER_MIN   ((Ulong)60)
#define  SECS_PER_HOUR  (60 * SECS_PER_MIN)
#define  SECS_PER_DAY   (24 * SECS_PER_HOUR)
#define  SECS_PER_WEEK  (7 * SECS_PER_DAY)
#define  SECS_PER_YEAR  (365 * SECS_PER_DAY + SECS_PER_DAY / 4) /* Average */
#define  SECS_PER_MONTH (SECS_PER_YEAR / 12)			/* Average */
X
#endif
SHAR_EOF
chmod 0664 els/defs.h ||
echo 'restore of els/defs.h failed'
Wc_c="`wc -c < 'els/defs.h'`"
test 1551 -eq "$Wc_c" ||
	echo 'els/defs.h: original size 1551, current size' "$Wc_c"
fi
# ============= els/getdate32.c ==============
if test -f 'els/getdate32.c' -a X"$1" != X"-c"; then
	echo 'x - skipping els/getdate32.c (File already exists)'
else
echo 'x - extracting els/getdate32.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/getdate32.c' &&
/******************************************************************************
X  getdate32.c -- get ISO3307/LS-style/unix-clock date using all 32 bits
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  These functions are provided "as is" in the hopes that they might serve some
X  higher purpose.  If you want these functions to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute these functions so long as this header remains intact.
X  You may also freely distribute modified versions of these functions so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Note: The most recent version of these functions may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  ****************************************************************************/
X
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>
#include "defs.h"
#include "getdate32.h"
#include "time32.h"
X
X
time_t getdate32(char *arg)
{
# define DATE_SIZE  127
X  char date[DATE_SIZE+1], *cp;
X  time_t t;
X  
X  if (arg == NULL) usage_error();
X  
X  /* Scan any leading blanks: */
X  while (*arg != CNULL && isspace(*arg)) arg++;
X
X  cp = arg;
X  if (isdigit(*cp))
X  {
X    strncpy(date, cp, DATE_SIZE);
X  }
X  else if (isalpha(*cp))
X  {
X    static char *weekday[] = {
X      "Sunday", "Monday", "Tuesday", "Wednesday",
X      "Thursday", "Friday", "Saturday"};
X    static char *month[] = {
X      "January", "February", "March", "April", "May", "June",
X      "July", "August", "September", "October", "November", "December"};
X    Ulong YYYY = 0, MM, WD, DD, hh = 0, mm = 0, ss = 0;
X    Boole have_year = FALSE;
X    Boole have_time = FALSE;
X    Boole have_timezone = FALSE;
X    Boole looking;
X    char *next;
X    int len;
X    
X    /* Ignore optional weekday: */
X    len = 0;
X    while (isalpha(cp[len])) len++;
X    if (len >= 3)
X    {
X      /* A weekday must have at least 3 characters that match: */
X      for (WD = 0; WD < 7; WD++)
X	if (strncmp_ci(weekday[WD], cp, len) == 0) break;
X      if (WD < 7)
X      {
X	cp += len;
X	while (*cp != CNULL && isspace(*cp)) cp++;
X      }
X    }
X
X    len = 0;
X    while (isalpha(cp[len])) len++;
X    if (len >= 3)
X    {
X      /* A month must have at least 3 characters that match: */
X      for (MM = 0; MM < 12; MM++)
X	if (strncmp_ci(month[MM], cp, len) == 0) break;
X      if (MM >= 12) invalid_date(arg, "Unrecognized month");
X      MM++;
X      cp += len;
X    }
X    else
X    {
X      invalid_date(arg, "Unrecognized abbreviation");
X    }
X
X    DD = strtoul(cp, &cp, 10);
X    if (DD == 0) invalid_date(arg, "Invalid day");
X
X    /* Some people like putting a comma after the day: */
X    if (*cp == ',') cp++;
X
X    looking = TRUE;
X    while (looking)
X    {
X      while (*cp != CNULL && isspace(*cp)) cp++;
X      if (isdigit(*cp))
X      {
X	Void strtoul(cp, &next, 10);
X	if (*next != ':')
X	{
X	  if (!have_year)
X	  {
X	    YYYY = strtoul(cp, &cp, 10);
X	    have_year = TRUE;
X	  }
X	  else
X	    looking = FALSE; /* Only one year allowed */
X	}
X	else
X	{
X	  if (!have_time)
X	  {
X	    hh = strtoul(cp, &cp, 10);
X	    if (*cp != ':') invalid_date(arg, "Invalid time");
X	    cp++;
X	    if (!isdigit(*cp)) invalid_date(arg, "Invalid time");
X	    mm = strtoul(cp, &cp, 10);
X	    if (*cp == ':')
X	    {
X	      cp++;
X	      if (!isdigit(*cp)) invalid_date(arg, "Invalid time");
X	      ss = strtoul(cp, &cp, 10);
X	    }
X	    have_time = TRUE;
X	  }
X	  else
X	    looking = FALSE; /* Only one time allowed */
X	}
X      }
X      else if (isalpha(*cp))
X      {
X	if (!have_timezone)
X	{
X	  while (*cp != CNULL && !isspace(*cp)) cp++;
X	  have_timezone = TRUE;
X	}
X	else
X	  looking = FALSE; /* Only one timezone allowed */
X      }
X      else
X      {
X	looking = FALSE; /* End of date */
X      }
X    }
X
X    /* There should be no data leftover: */
X    if (*cp != CNULL) invalid_date(arg, NULL);
X
X    /* If no year given then make an intelligent guess: */
X    if (!have_year)
X    {
X      /* It is typical of "ls -l" listings to not bother giving the year
X	 unless the file is more than 6 months old.  Hence, the best we can
X	 do is assume that the missing year is the most recent gone by: */
X      struct tm *arg_time;
X      time_t the_time = time(NULL);
X      arg_time = localtime32(&the_time);
X      YYYY = arg_time->tm_year + 1900;
X      if ((MM - 1 > arg_time->tm_mon) ||
X	  (MM - 1 == arg_time->tm_mon && DD > arg_time->tm_mday))
X	  YYYY--;
X    }
X
X    /* There should be exactly 15 characters in the date: */
X    sprintf(date, "%04lu%02lu%02lu.%02lu%02lu%02lu", YYYY, MM, DD, hh, mm, ss);
X    if (strlen(date) != 15) invalid_date(arg, NULL);
X  }
X  else
X  {
X    usage_error();
X  }
X
X  date[DATE_SIZE] = CNULL;
X  
X  if (LGC == 'C')
X  {
X    char *leftovers;
X    t = (time_t)strtoul(date, &leftovers, 0);
X    if (*leftovers)
X      invalid_date(date, "Clock contains non-numeric character");
X  }
X  else
X  {
X    int len = 0;
X    int i = 0;
X
X    /* Make sure that date has 8, 10, 12, or 14 numbers while allowing
X       for an optional dot separator between date and time: */
X    for (i = 0; date[i] != CNULL; i++)
X      if (date[i] == '.')
X      {
X	if (i != 8)
X	  invalid_date(date, "Date must be in the form of YYYYMMDD.hhmmss");
X      }
X      else
X      {
X	if (!isdigit(date[i]))
X	  invalid_date(date, "Date contains non-numeric character");
X	else
X	  len++;
X      }
X    if (len > 14)
X      invalid_date(date, "Date has too many numbers");
X    else if (len < 8 || (len & 1) != 0)
X      invalid_date(date, "Date has too few numbers");
X
X    /* Convert date to seconds: */
X    t = convert_iso3307(arg, date);
X  }
X  
X  if (t == (time_t)-1)
X    invalid_date(date, "Date is out of range");
X
X  return(t);
}
X
X
/* Convert YYYYMMDD.hhmmss to internal time: */
time_t convert_iso3307(char *arg, char *date)
{
X  time_t t;
X  int i, len;
X  long n, hour, yday, days, month, year;
X  Boole isleapyear = FALSE;
X  
X  i = 0;
X  len = 0;
X  hour = 0;
X  yday = 0;
X  days = 0;
X  month = 0;
X  year = 0;
X  while (date[i] != CNULL && len < 14)
X  {
X    if (i == 8 && date[8] == '.') i++;
X    n  = (date[i++] - '0') * 10;
X    n += (date[i++] - '0');
X    len += 2;
X    switch(len)
X    {
X    case 2: /* YY: century */
X      year = n * 100;
X      break;
X
X    case 4: /* YY: year */
X      year += n;
X
X      /* Even though "Dec 31, 1969 23:00 Local time" might be valid,
X	 the GMT conversion algorithm gets flakey around this time: */
X      if (year < 1970)
X	invalid_date(arg, "Cannot represent years before 1970");
X      if (year > 2106)
X	invalid_date(arg, "Cannot represent years beyond 2106");
X
X      days = (year - 1970) * 365;
X
X      /* Tack on days for leap years in years already gone by (the first year
X	 this occurs is 1973): */
X      days += ((year - 1969) / 4);
X      if (year > 2100) days--;
X
X      /* Determine whether the current year is a leap-year: */
X      isleapyear = ((year % 4) == 0 && year != 2100);
X      break;
X
X    case 6: /* MM: month */
X      switch (n) {
X      case 12:
X	yday += 30;
X      case 11:
X	yday += 31;
X      case 10:
X	yday += 30;
X      case 9:
X	yday += 31;
X      case 8:
X	yday += 31;
X      case 7:
X	yday += 30;
X      case 6:
X	yday += 31;
X      case 5:
X	yday += 30;
X      case 4:
X	yday += 31;
X      case 3:
X	yday += (isleapyear ? 29 : 28);
X      case 2:
X	yday += 31;
X      case 1:
X	break;
X      default:
X	invalid_date(arg, "Invalid month");
X      }
X      month = n;
X      break;
X
X    case 8: /* DD: day */
X      {
X	int last_day;
X	switch (month) {
X	case 12:
X	case 10:
X	case 8:
X	case 7:
X	case 5:
X	case 3:
X	case 1:
X	  last_day = 31;
X	  break;
X	case 11:
X	case 9:
X	case 6:
X	case 4:
X	  last_day = 30;
X	  break;
X	case 2:
X	  last_day = (isleapyear ? 29 : 28);
X	  break;
X	default:
X	  last_day = 0;
X	  break;
X	}
X	if (n < 1 || n > last_day)
X	  invalid_date(arg, "Invalid day");
X      }
X      yday = yday + n - 1;
X      days += yday;
X      t = days * SECS_PER_DAY;
X      break;
X
X    case 10: /* hh: hour */
X      if (n > 23)
X	invalid_date(arg, "Invalid hour");
X      hour = n;
X      t += hour * SECS_PER_HOUR;
X      break;
X
X    case 12: /* mm: minute */
X      if (n > 59)
X	invalid_date(arg, "Invalid minute");
X      t += n * SECS_PER_MIN;
X      break;
X
X    case 14: /* ss: second */
X      if (n > 59)
X	invalid_date(arg, "Invalid second");
X      t += n;
X      break;
X
X    default:
X      break;
X    }
X  }
X
X  /* Test if date invalid: */
X  if ((long)t == -1 ||
X      (year <= 1970 && (long)t < 0) ||
X      (year >= 2106 && (long)t > 0))
X    invalid_date(arg, "Date is out of range");
X
X  if (LGC == 'L')
X  {
X    /* Take an initial guess at the GMT offset.  If the date happens to
X       be within a few hours of when daylight savings begins/ends, then
X       a second guess will be necessary. */
X
X    /* First guess: */
X    t += guess_gmtoff(&t, yday, hour);
X    
X    /* Take a second guess in case the date happened to be changing
X       between daylight savings and standard time during the first guess,
X       except during the year 2106 when things get touchy: */
X    if (year < 2106)
X      t += guess_gmtoff(&t, yday, hour);
X    
X    /* Test if conversion to local time made date invalid: */
X    if ((long)t == -1 ||
X	(year <= 1970 && (long)t < 0) ||
X	(year >= 2106 && (long)t >= 0))
X      invalid_date(arg, "Date is out of range");
X  }
X
X  return(t);
}
X
X
long guess_gmtoff(time_t *clock, int yday, int hour)
{
X  struct tm loc;
X  int gmt_offset;
X  int day_offset;
X  
X  /* Try to guess the GMT timezone/daylight-savings offset by picking
X     a clock value that comes closest to the desired yday and hour parameters.
X     (There are simpler ways of doing this, but not all Unixes support the
X     tm_gmtoff value in the tm structure.) */
X  
X  loc = *localtime32(clock);
X  
X  /* Test for year-end wrap-around: */
X  if (yday == 0 && loc.tm_yday >= 364)
X    day_offset = 1;
X  else if (loc.tm_yday == 0 && yday >= 364)
X    day_offset = -1;
X  else
X    day_offset = yday - loc.tm_yday;
X  
X  gmt_offset = 24 * day_offset + (hour - loc.tm_hour);
X
#ifdef DEBUG
X  printf("24*(%d-%d) + (%d-%d) = %d\n",
X       yday, loc.tm_yday, hour, loc.tm_hour, gmt_offset);
#endif
X    
X  return(gmt_offset * SECS_PER_HOUR);
}
X
X
void usage_error(void)
{
X  give_usage_or_help('U');
X  exit(1);
}
X
X
/* Display invalid date message and exit: */
void invalid_date(char *date, char *msg)
{
X  char buf[256];
X  sprintf(buf, "%s: %s", date,
X	  msg != NULL ? msg : "Invalid date");
X  error_msg_exit(buf);
}
X
X
/* Display error message and exit: */
void error_msg_exit(char *msg)
{
X  fprintf(stderr, "%s: %s\n", Progname, msg);
X  exit(1);
}
X
X
/* Display errno message and exit: */
void error_num_exit(char *file)
{
X  char msg[256];
X  if (file != NULL)
X    sprintf(msg, "%s: %s", Progname, file);
X  else
X    sprintf(msg, "%s", Progname);
X  if (errno != 0)
X    perror(msg);
X  else
X    fprintf(stderr, "%s: Unknown error\n", msg);
X  exit(1);
}
X
X
/* strncmp_ci: same as strncmp, except comparisons are case-insensitive */
int strncmp_ci(register char *s1,
X	       register char *s2,
X	       register int n)
{
X  register char c1, c2;
X  int diff = 0;
X  do
X  {
X    if (n-- <= 0) break;
X    c1 = *s1++;
X    c2 = *s2++;
X    if (isupper(c1)) c1 = tolower(c1);
X    if (isupper(c2)) c2 = tolower(c2);
X  } while ((diff = c1 - c2) == 0 && c1 && c2);
X  return (diff);
}
X
X
/* Not all unixes have strtoul, so here's my version: */
Local Ulong _strtoul(char *str, char **ptr, int base,
X		     Boole scan_ws, Boole is_signed,
X		     Boole *negative, Boole *overflow)
{
X  int digit;
X  Ulong result, last_result;
X  char *next = str;
X
X  result = 0;
X  last_result = 0;
X  *negative = FALSE;
X  *overflow = FALSE;
X  
X  /* Scan leading whitespace only if specified: */
X  if (scan_ws) while (isspace(*str)) str++;
X  
X  if (*str == '-')
X  {
X    *negative = TRUE;
X    str++;
X  }
X  else if (*str == '+')
X    str++;
X  
X  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X   * Base may be either explicitly specified (i.e. the parameter "base" is
X   * non-zero) or implicitly discerned (e.g. the number starts with 0x,
X   * etc.).  If the base is explicit then the number may or may not be
X   * preceded with an implicit base; however, if both an implicit and an
X   * explicit base are specified they both need to agree.
X   */
X  
X  if (*str == '0')
X  {
X    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X     * Attempt to discern an implicit base:
X     *	0x... --> implicit base = 16
X     *	0...  --> implicit base = 8
X     */
X    str++;
X    next = str;
X    if ((base == 0 || base == 16) && (*str == 'x' || *str == 'X'))
X    {
X      str++;
X      base = 16;
X    }
X    else if ((base == 0 || base == 8) && (*str != '\0'))
X    {
X      base = 8;
X    }
X    else
X    {
X      /* We have either a single zero or an explicit base: */
X      str--;	/* restore the zero, we may need it later */
X      next = str;
X    }
X  }
X  
X  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
X   * If no explicit base was specified and no implicit base was discerned,
X   * then use base 10:
X   */
X  if (base == 0) base = 10;
X
X  if (base < 2 || base > 36)
X    errno = EINVAL;
X  else
X  {
X    Boole digit_ok;
X    do {
X      digit = base;
X      if (isascii(*str))
X      {
X	if (isdigit(*str))
X	  digit = *str - '0';
X	else if (islower(*str))
X	  digit = *str - 'a' + 10;
X	else if (isupper(*str))
X	  digit = *str - 'A' + 10;
X      }
X      digit_ok = (digit < base);
X      
X      if (digit_ok)
X      {
X	result *= base;
X	result += digit;
X	str++;
X	next = str;
X	if (result / base != last_result) *overflow = TRUE;
X	if (is_signed && (long)result < 0) *overflow = TRUE;
X	last_result = result;
X      }
X    } while (digit_ok && *str);
X    
X    if (*negative) result = (Ulong)(-(long)result);
X  }
X    
X  if (ptr) *ptr = next;
X  
X  return(result);
}
X
X
long strtol(char *str, char **ptr, int base)
{
X  Boole negative, overflow;
X  long l;
X  l = (long)_strtoul(str, ptr, base, TRUE, TRUE, &negative, &overflow);
X  if (overflow)
X  {
X    long maxneg = 1 << (8*sizeof(long)-1);
X    errno = ERANGE;
X    return(negative ? maxneg : ~maxneg);
X  }
X  else
X    return(l);
}
X
X
Ulong strtoul(char *str, char **ptr, int base)
{
X  Boole negative, overflow;
X  Ulong ul;
X  ul = _strtoul(str, ptr, base, TRUE, FALSE, &negative, &overflow);
X  if (overflow)
X  {
X    errno = ERANGE;
X    return(~(Ulong)0);
X  }
X  else
X    return(ul);
}
SHAR_EOF
chmod 0664 els/getdate32.c ||
echo 'restore of els/getdate32.c failed'
Wc_c="`wc -c < 'els/getdate32.c'`"
test 14356 -eq "$Wc_c" ||
	echo 'els/getdate32.c: original size 14356, current size' "$Wc_c"
fi
# ============= els/getdate32.h ==============
if test -f 'els/getdate32.h' -a X"$1" != X"-c"; then
	echo 'x - skipping els/getdate32.h (File already exists)'
else
echo 'x - extracting els/getdate32.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/getdate32.h' &&
/******************************************************************************
X  getdate32.h -- get ISO3307/LS-style/unix-clock date using all 32 bits
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  These functions are provided "as is" in the hopes that they might serve some
X  higher purpose.  If you want these functions to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute these functions so long as this header remains intact.
X  You may also freely distribute modified versions of these functions so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Note: The most recent version of these functions may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  ****************************************************************************/
X
#ifndef ELS__GETDATE32_H
#define ELS__GETDATE32_H
X
time_t getdate32(char *arg);
time_t convert_iso3307(char *arg, char *date);
long guess_gmtoff(time_t *clock, int yday, int hour);
void usage_error(void);
void invalid_date(char *date, char *msg);
void error_msg_exit(char *msg);
void error_num_exit(char *file);
int strncmp_ci(char *s1, char *s2, int n);
long strtol();
Ulong strtoul();
X
extern char *Progname;
extern char LGC;
X
#endif
SHAR_EOF
chmod 0664 els/getdate32.h ||
echo 'restore of els/getdate32.h failed'
Wc_c="`wc -c < 'els/getdate32.h'`"
test 1385 -eq "$Wc_c" ||
	echo 'els/getdate32.h: original size 1385, current size' "$Wc_c"
fi
# ============= els/time32.c ==============
if test -f 'els/time32.c' -a X"$1" != X"-c"; then
	echo 'x - skipping els/time32.c (File already exists)'
else
echo 'x - extracting els/time32.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/time32.c' &&
/******************************************************************************
X  time32.c -- extended time functions that use all 32 bits
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  These functions are provided "as is" in the hopes that they might serve some
X  higher purpose.  If you want these functions to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute these functions so long as this header remains intact.
X  You may also freely distribute modified versions of these functions so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Note: The most recent version of these functions may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  The following functions support Unix time beyond Jan 19 03:14:08 2038 GMT,
X  assuming that the future standard will treat the 32-bits used by Unix's
X  present-day file system as unsigned.
X
X  These functions work by mapping years within the region 2038-2106 down
X  into the region of 2005-2037.  All fields of the "tm" structure,
X  including those fields dealing with day-of-week and daylight savings time
X  are correct!  Bear in mind, however, that the definition of daylight
X  savings time changes with the whims of man, thus the notion of daylight
X  savings held during 2005-2037 may not be the same as the notion held
X  thereafter.
X
X  See also: time(3)
X
X  ****************************************************************************/
X
#include "defs.h"
#include "time32.h"
X
X
Local void mapclock32(time_t *clock, int *years);
Local void mapyears32(int *years, time_t *clock);
X
X
struct tm *localtime32(time_t *clock)
{
X  struct tm *xtime;
X  time_t xclock = *clock;
X  int xyears;
X  mapclock32(&xclock, &xyears);
X  xtime = localtime(&xclock);
X  xtime->tm_year += xyears;
X  return(xtime);
}
X
X
struct tm *gmtime32(time_t *clock)
{
X  struct tm *xtime;
X  time_t xclock = *clock;
X  int xyears;
X  mapclock32(&xclock, &xyears);
X  xtime = gmtime(&xclock);
X  xtime->tm_year += xyears;
X  return(xtime);
}
X
X
#ifdef HAVE_TIMELOCAL
/* SunOS4 (and possibly other BSDs) has timelocal() and timegm(): */
time_t timelocal32(struct tm *tm)
{
X  struct tm xtime = *tm;
X  time_t xclock;
X  mapyears32(&xtime.tm_year, &xclock);
X  return(timelocal(&xtime) + xclock);
}
X
X
time_t timegm32(struct tm *tm)
{
X  struct tm xtime = *tm;
X  time_t xclock;
X  mapyears32(&xtime.tm_year, &xclock);
X  return(timegm(&xtime) + xclock);
}
#endif
X
X
char *asctime32(struct tm *tm)
{
X  /* asctime() seems to work as-is without having to do any magic: */
X  return(asctime(tm));
}
X
X
char *ctime32(time_t *clock)
{
X  return(asctime(localtime32(clock)));
}
X
X
X
#define  _28_YEARS   ( 28 * SECS_PER_YEAR)
#define  _90_YEARS   ( 88 * SECS_PER_YEAR + 2*365*SECS_PER_DAY)
#define  _96_YEARS   ( 96 * SECS_PER_YEAR)
#define  JAN_1_2100  (128 * SECS_PER_YEAR + 2*365*SECS_PER_DAY)
#define  JAN_1_2102  (JAN_1_2100 + 2*365*SECS_PER_DAY)
X
Local void mapclock32(time_t *clock, int *years)
{
X  Ulong xclock = (Ulong)*clock;
X  int xyears = 0;
X    
X  /* Years from 1970 up until Jan 19 03:14:08 2038 GMT need no mapping. */
X
X  if ((long)xclock < 0)
X  {
X    /* Map years beyond Jan 19 03:14:08 2038 GMT: */
X    if ((Ulong)xclock >= JAN_1_2100)
X    {
X      if ((Ulong)xclock < JAN_1_2102)
X      {
X	/* Map years 2100 and 2101:
X	   (These two years must be contiguously mapped in order for
X	   localtime(JAN_1_2101) to return the correct "tm_yday" value.
X	   Hint: think about how Jan 1 2101, 00:00 GMT maps into
X	   Dec 31 2100 LOCAL TIME). */
X	
X	xclock -= _90_YEARS;
X	xyears = 90;
X      }
X      else
X      {
X	/* Map years from 2102 up until Feb 7 04:28:14 2106 GMT: */
X	xclock -= _96_YEARS;
X	xclock += SECS_PER_DAY; /* Compensate for missing leap year! */
X	xyears = 96;
X      }
X    }
X    else
X    {
X      /* Map years from 2038 up until 2100: */
X      while ((long)xclock < 0)
X      {
X	xclock -= _28_YEARS;
X	xyears += 28;
X      }
X    }
X  }
X
X  *clock = xclock;
X  *years = xyears;
X  return;
}
X
X
#define  _1970  ( 70)
#define  _2038  ( 68 + _1970)
#define  _2100  (130 + _1970)
#define  _2102  (132 + _1970)
X
Local void mapyears32(int *years, time_t *clock)
{
X  Ulong xclock = 0;
X  int xyears = *years;
X    
X  /* Years from 1970 up until Jan 19 03:14:08 2038 GMT need no mapping. */
X
X  if (xyears >= _2038)
X  {
X    /* Map years beyond Jan 19 03:14:08 2038 GMT: */
X    if (xyears >= _2100)
X    {
X      if (xyears < _2102)
X      {
X	/* Map years 2100 and 2101:
X	   (These two years must be contiguously mapped in order for
X	   localtime(JAN_1_2101) to return the correct "tm_yday" value.
X	   Hint: think about how Jan 1 2101, 00:00 GMT maps into
X	   Dec 31 2100 LOCAL TIME). */
X	
X	xyears -= 90;
X	xclock = _90_YEARS;
X      }
X      else
X      {
X	/* Map years from 2102 up until Feb 7 04:28:14 2106 GMT: */
X	xyears -= 96;
X	xclock = _96_YEARS;
X	xclock -= SECS_PER_DAY; /* Compensate for missing leap year! */
X      }
X    }
X    else
X    {
X      /* Map years from 2038 up until 2100: */
X      while (xyears >= _2038)
X      {
X	xyears -= 28;
X	xclock += _28_YEARS;
X      }
X    }
X  }
X
X  *years = xyears;
X  *clock = xclock;
X  return;
}
SHAR_EOF
chmod 0664 els/time32.c ||
echo 'restore of els/time32.c failed'
Wc_c="`wc -c < 'els/time32.c'`"
test 5212 -eq "$Wc_c" ||
	echo 'els/time32.c: original size 5212, current size' "$Wc_c"
fi
# ============= els/time32.h ==============
if test -f 'els/time32.h' -a X"$1" != X"-c"; then
	echo 'x - skipping els/time32.h (File already exists)'
else
echo 'x - extracting els/time32.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/time32.h' &&
/******************************************************************************
X  time32.h -- extended time functions that use all 32 bits
X  
X  Author: Mark Baranowski (markb@elen.utah.edu)
X
X  Version: 1.29
X  Date: May 22, 1996
X  
X  These functions are provided "as is" in the hopes that they might serve some
X  higher purpose.  If you want these functions to serve some lower purpose,
X  then that too is all right.  So as to keep these hopes alive you may
X  freely distribute these functions so long as this header remains intact.
X  You may also freely distribute modified versions of these functions so long
X  as you indicate that such versions are modified and so long as you
X  provide access to the unmodified original copy.
X  
X  Note: The most recent version of these functions may be obtained from
X  ftp://perseus.elen.utah.edu/pub/markb/els.tar.Z
X
X  ****************************************************************************/
X
#ifndef ELS__TIME32_H
#define ELS__TIME32_H
X
#include <time.h>
X
extern struct tm *localtime32(time_t *clock);
extern struct tm *gmtime32(time_t *clock);
#ifdef HAVE_TIMELOCAL
extern time_t timelocal32(struct tm *tm);
extern time_t timegm32(struct tm *tm);
#endif
extern char *asctime32(struct tm *tm);
extern char *ctime32(time_t *clock);
X
#endif
SHAR_EOF
chmod 0664 els/time32.h ||
echo 'restore of els/time32.h failed'
Wc_c="`wc -c < 'els/time32.h'`"
test 1283 -eq "$Wc_c" ||
	echo 'els/time32.h: original size 1283, current size' "$Wc_c"
fi
# ============= els/Makefile ==============
if test -f 'els/Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping els/Makefile (File already exists)'
else
echo 'x - extracting els/Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'els/Makefile' &&
# DEFAULT SETTINGS:
CC = gcc
CPPFLAGS =
CFLAGS = -O
LD = $(CC)
LDFLAGS =
BINDIR = /usr/local/bin
MANEXT = 1
MANDIR = /usr/local/man/man$(MANEXT)
X
X
# ARGUMENTS:
default:
X	@ echo ""
X	@ echo "Please run make with one of the following arguments:"
X	@ echo "	sysv, sunos5, solaris2, linux"
X	@ echo "	bsd, sunos4, ultrix"
X	@ echo "	aix, rs6000"
X	@ echo "	hpux, hpux-gnu"
X	@ echo "	dynix, sequent, dynix-gnu, sequent-gnu"
X	@ echo "	isc, isc386"
X	@ echo ""
X	@ echo "For example, to build for SunOS5 (aka Solaris2):"
X	@ echo "	make sunos5"
X	@ echo ""
X
# TARGETS:
all: chdate edate els els5
X
install:
X	cp chdate edate els $(BINDIR)
X	cp chdate.1 $(MANDIR)/chdate.$(MANEXT)
X	cp edate.1 $(MANDIR)/edate.$(MANEXT)
X	cp els.1 $(MANDIR)/els.$(MANEXT)
X	rm -f $(BINDIR)/els5
X	ln  $(BINDIR)/els  $(BINDIR)/els5
X
clean:
X	rm -f chdate edate els els5
X	rm -f chdate.o edate.o els.o getdate32.o time32.o
X
X
bsd \
ultrix \
sunos4:
X	$(MAKE) all \
X	"CPPFLAGS = -DHAVE_TIMELOCAL -DHAVE_TM_ZONE"
X
sysv \
linux \
solaris2 \
sunos5:
X	$(MAKE) all
X
rs6000 \
aix:
X	$(MAKE) all \
X	"CC = cc"
X
hpux:
X	$(MAKE) all \
X	"CC = cc" \
X	"CPPFLAGS = -D_HPUX_SOURCE" \
X	"CFLAGS = -O -Aa"
X
hpux-gnu:
X	$(MAKE) all \
X	"CPPFLAGS = -D_HPUX_SOURCE"
X
dynix \
sequent:
X	$(MAKE) all \
X	"CC = cc" \
X	"CPPFLAGS = -DNO_ST_BLOCKS"
X
dynix-gnu \
sequent-gnu:
X	$(MAKE) all \
X	"CPPFLAGS = -DNO_ST_BLOCKS"
X
isc \
isc386:
X	$(MAKE) all \
X	"CC = cc" \
X	"CPPFLAGS = -DNO_ST_BLOCKS"
X
X
chdate: chdate.o time32.o getdate32.o
X	$(LD) $(LDFLAGS) chdate.o time32.o getdate32.o -o chdate
X	
edate: edate.o time32.o getdate32.o
X	$(LD) $(LDFLAGS) edate.o time32.o getdate32.o -o edate
X	
els: els.o time32.o
X	$(LD) $(LDFLAGS) els.o time32.o -o els
X	
# els5 uses SYS5 semantics instead of BSD semantics by default:
els5: els
X	rm -f els5
X	ln els els5
X
X
chdate.o: chdate.c defs.h getdate32.h
X	$(CC) $(CFLAGS) $(CPPFLAGS) -c chdate.c
edate.o: edate.c defs.h getdate32.h
X	$(CC) $(CFLAGS) $(CPPFLAGS) -c edate.c
els.o: els.c defs.h time32.h
X	$(CC) $(CFLAGS) $(CPPFLAGS) -c els.c
getdate32.o: getdate32.c defs.h getdate32.h time32.h
X	$(CC) $(CFLAGS) $(CPPFLAGS) -c getdate32.c
time32.o: time32.c defs.h time32.h
X	$(CC) $(CFLAGS) $(CPPFLAGS) -c time32.c
SHAR_EOF
chmod 0664 els/Makefile ||
echo 'restore of els/Makefile failed'
Wc_c="`wc -c < 'els/Makefile'`"
test 2162 -eq "$Wc_c" ||
	echo 'els/Makefile: original size 2162, current size' "$Wc_c"
fi
exit 0
-- 
         +--+
 PERTH---> * -+   SkipUtah!
         |    |   Greatest Snow Job on Earth
         +----+


