[net.sources] ls - a Unix-like directory lister for MS-DOS

nather@utastro.UUCP (Ed Nather) (11/22/84)

[]
This is one of a set of programs I use to make the transition back and
forth between Unix 4.2bsd and MS-DOS less traumatic.  It mimics `ls'
on 4.2bsd in many of its options and actions, but includes a few things
I found necessary in the MS-DOS environment.  It compiles successfully
with either DeSmet C or CI-C86 compilers.  It should be kept in RAM disk
unless you have a hard disk in your system, to minimize loading time.
Please read the installation instructions before you compile -- there are
`customizing' options you may want (or need) to change.

I want to thank noao!jacoby for his excellence in beta-testing; `ls' has
many fewer bugs because of his careful interest.  If you find any bugs or
oversights, please mail me enough information so I can find and correct
them.  If you are overwhelmed with gratitude after using the program, mail
me a nifty one *you* use a lot.

----------------Clip here and feed to sh, not csh-------------------

echo Extracting ls.doc ...
cat > ls.doc << '---EOF---'
ls - a Unix-like directory listing program for MS-DOS

(c) 1984, R. Edward Nather

Syntax: ls [-aclrstuR] [(path)name ... ]

    Options may appear in any order, grouped or separated; if separate,
    each must be preceded by a dash.  The name(s) may refer to files or
    directories.  If no name is given the current directory is listed.

Options:

    (none)  Show filenames (only) sorted alphabetically
     -a     all: include system files, hidden files, "." and ".."
     -c     columnar: list information in 1-column format
     -l     long listing: include file's size, date, time, attributes
     -r     reverse the sorting direction
     -s     report size(s) only
     -t     sort by time of last file modification
     -u     include actual disk use, with totals & available space
     -R     recursively list all subdirectories

Description:

With no options specified, "ls" lists the filenames in the current directory,
sorted alphabetically into 5 columns; if a (path)name of a directory is
included, its contents will be displayed.  If two or more names are given
they will be sorted and shown successively, identified by their (path)names.
Wild card characters ("*","?") are acceptable in (path)name arguments.
Subdirectories are identified by a trailing file-separator character in this
display format.

System files, "hidden" files and the directory synonyms "." and ".." are
normally not included in the listing, but the -a option will bring them out
of the woodwork.  "Volume Labels" are always ignored.  If they have any
logic or value it has been carefully hidden by Microsoft.

The listed filenames will be sorted according to their last modification time
if the -t option is included, normally latest first.  The direction of sort
(alphabetic or time sort) is reversed by the -r option.

The -l option yields a two-column "long" listing, sorted vertically by column,
showing nearly everything known about the files (i.e. all the news that fits).
The file attributes are shown symbolically by single characters: "d" for
directories or "s" for system files, "h" for hidden files, and "w" for writable
(not read-only) files.  The lack of one of these attributes is shown by a dash.
The actual length of the file, in bytes, is given unless the -u option is
included (see below).  The month and time of the last modification are shown if
the file is less than 12 months old, otherwise month and year are printed.

Note that the file sizes displayed in the "long" listing will always be smaller
than the actual disk space used, because MS-DOS never writes a record smaller
than the "cluster" size -- which is 1024 bytes on a 9-sector floppy and 4096
bytes on a hard disk with only a DOS partition present.  

The -u option tells you how much disk space is actually used.  If only one
filename or directory is listed, its total disk use is shown, followed by
its identifying (path)name.  If the -l option is included with -u, the size
shown for each file is the amount of disk space used -- almost always larger
than the file size. More than one name on the command line will get you
successive listings, with disk use subtotals preceding the identifying
(path)name, and followed by the total disk space used by the files listed.
All this is followed by the space remaining on the drive; if more than one
drive is involved, it is the space remaining on the last one on the command
line.

The -c option forces output into a single column, so "ls -c" will give a
sorted list of filenames (only) in a single column, and "ls -lc" will
provide a "long" listing in one column instead of two.  These formats can
be useful if you want to use the file names or information for further
operations (e.g. as arguments in ".bat" files).  This format is the 4.2bsd
Unix default when sending the listing to any device other than the display;
here you must explicitly ask for single column output to get it.

The -R option will include (recursively) the contents of any subdirectories
encounterd, and their subdirectories, ad finitum.  This can be a lengthy
listing on a hard disk; if started in the root directory with the -a option
it lists the whole shebang.

The -s option omits filenames (unless they are specified on the command line)
and lists sizes only.  This effectively shuts off the -l, -t and -r options,
and turns -u on, yielding a nifty summary of disk usage by directory, along
with a total at the end and the space remaining on the drive.  Include -a if
you want hidden and system files to show up.

As a special case, the -s option recognizes the numerals "1" and "2" (they
must immediately follow the "s" option) and will use disk block sizes
appropriate to single-sided diskettes (-s1) or double-sided diskettes (-s2)
in the calculatation of actual disk usage.  This is useful if you want to
know if a given set of files is going to fit on a diskette that uses block
sizes different from those on the source disk.  For example, MS-DOS is far
more profligate with disk space on a hard disk than on a floppy, and disk
use appears to "shrink" onto a floppy backup.  These options can tell you
how much shrinkage to expect.

The program includes a built-in pager which stops scrolling after 22 lines
have been displayed on your terminal, and indicates there is more to be
shown if there really is.  A carriage return shows one more line; any other
character gets another screenful.  This action takes place only if the
output device is your console screen -- the pause will not occur if the
output is redirected to any other device or into a file.

Installation:

The program assumes the file separator character is "/".  If this assumption
is incorrect, you should change the two symbolic constants QS and DQS to
reflect the true condition.  Note that you must use two backslash characters
(e.g. #define DQS "\\") to get one of them.

A set of symbolic "customizing constants" has been included that allow most
of the options to be either "on" or "off" by default.  As delivered, all of
the options are off -- the corresponding symbolic constant is defined as
zero.  To get the optional action, you must include the option on the command
line when you call "ls" into operation.  However, you may want a particular
option to be *on* by default; it can then be turned *off* by including the
option on the command line.  To bring this about, just set its symbolic
constant to 1 before you compile the program.  Here's the list:

Name        Action when off                 Action when on

ID          Identify directories only       Always identify directories.
            when more than one is shown.

ALL         Do not show system or hidden    Show everything except (gag)
            files, nor directory synonyms.  volume labels.

COLM        List file names in 5 columns,   List in one column format.
            long listing in 2 columns.

LONG        List names of files and         List everything known about a
            directories only.  Identify     file or directory.
            directories with QS character.

RSORT       Sort in normal alphabetic       Sort into reverse alphabetic
            order or by most recent time.   order or by oldest time.

TSORT       Sort into alphabetic order.     Sort by time of file creation
                                            or modification.

DU          Show file size based on         Show file size based on total
            number of bytes in the file.    disk space actually used.


The program can be compiled with either the DeSmet or CI-C86 compilers.
For DeSmet C, the assembly langauge support program "sysint.a" must be
assembled separately, and either included in the library or linked
separately using the program "Bind."

For CI-C86, you must include the option -dCIC86 on the command line for
the first pass of the compiler "cc1" or add the line

      #define CIC86

to the source code.  The support routine "sysint" is present in the
CI-C86 library and will be automatically linked into the final .exe file.

If your system has a 10MB hard disk configured for more than one partition,
you may need to change the value of the symbolic constant HARDCL; its value
should be the cluster size, in bytes, for the MS-DOS partition.

Using "ls":

The program has to be loaded into memory before it can run, then it must
consult the disk directory to find things.  This is a bit slow on a floppy
but is gratifyingly fast if the program is located on a hard disk or in
a ram disk, if there are less than a screenful of filenames to be shown.
It starts a bit slower if disk usage is requested, and it takes a second
or so to find and sort more than a hundred filenames at one whack.

Since the program has several options, you might want to encapsulate useful
combinations as ".bat" files, and give each a different name.  For example,
I have a file called "ll.bat" that contains

    ls -l %1 %2 %3 %4 %5 %6 %7 %8 %9

so that "ll" means "long listing."  Other options can be included on the "ll"
command line and they will work properly.  I have another called "du.bat" that
contains 

    ls -asR %1 %2 %3 %5 %6 %7 %8 %9 

and summarizes disk use in a compact format.

Bugs/Deficiencies:

The options are far from being mutually orthogonal; in particular, the
command "ls -u filename", for a single file that is not a directory, gives
a dumb-looking printout.

If you find yourself using DIR after you have used "ls" then I have
failed.

---EOF---
echo Extracting ls.c ...
cat > ls.c << '---EOF---'

/* ls - a Unix-like directory listing program for MS-DOS 2.x
 *
 * Copyright (c) 1984  R. Edward Nather
 *
 */

#include <stdio.h>

/* customizing constants */

#define QS	   '/'			/* filename separator character */
#define DQS	   "/"			/* filename separator (string) */
#define ID 		0			/* always identify directory if 1 */
#define ALL 	0			/* show hidden files by default if 1 */
#define LONG 	0			/* long listing by default if 1 */
#define COLM 	0			/* 1-column listing by default if 1 */
#define RSORT	0			/* reverse sort by default if 1 */
#define TSORT	0			/* time sort by default if 1 */
#define DU		0			/* include disk use by default if 1 */


#define NAMESIZ 13			/* 12 character name + NULL */
#define ONECS 512			/* cluster size on one-sided floppy */
#define TWOCS 1024			/* cluster size on two-sided floppy */
#define HARDCS 4096			/* cluster size on hard disk */
#define SCRSIZ 22			/* scrolling size of display screen */

struct dta					/* DOS Disk Transfer Address table */
	{
	char reserved[21];		/* used in "find next" operation */
	char attr;				/* file attribute byte */
	int ftime;				/* time of last modification */
	int fdate;				/* date of last modification */
	long fsize;				/* file size in bytes */
	char fname[NAMESIZ];	/* filename and extension */
	};

struct outbuf				/* output buffer -- array of file structs */
	{
	unsigned oattr;
	unsigned odate;
	unsigned otime;
	long osize;
	char oname[NAMESIZ+1];
	} *obuf;

char spath[80];				/* holds current pathname string */

/* global variables and flags */

int allf = ALL;				/* include hidden & system files */
int ll = LONG;				/* long listing */
int colm = COLM;			/* 1-column format */
int rev = RSORT;			/* reverse sort */
int tsrt = TSORT;			/* timesort the listing */
int usage = DU;				/* print disk usage */
int recd;					/* recursive descent requested */
int sizonly;				/* only print sizes */

int np;						/* number of groups printed */
int nargs;					/* number of non-option arguments */
int clsize = 0;				/* size of a cluster, in bytes */
int clmask;					/* clsize-1 for rounding & chopping */
int drive;					/* code number for drive requested */
int tsc;					/* 1 if output is to console screen */
long left;					/* unused space left on disk */
long total;					/* total of sizes encountered */

main(argc, argv)
int argc;
char *argv[];
{
char *s;
int c = 0;
int nt = 0;

/* process input options */

while(--argc > 0 && (*++argv)[0] == '-') {
	for(s = argv[0]+1; *s != '\0'; s++) {
		switch(*s) {
			case 'a':					/* -a: list all files */
				allf = (allf == 0);		/* reverse default value */
				break;
			case 'c':					/* -c: 1-column listing requested */
				colm = (colm == 0);
				break;
			case 'l':					/* -l: long listing requested */
				ll = (ll == 0);
				break;
			case 'r':					/* -r: reverse sort direction */
				rev = (rev == 0);
				break;
			case 's':					/* -s: print sizes only */
				sizonly = 1;
				if(*(s+1) == '1') {
					clsize = ONECS;		/* diskuse for 1-sided floppy */
					s++;
					nt++;
					}
				else if(*(s+1) == '2') {
					clsize = TWOCS;			/* or 2-sided */
					s++;
					nt++;
					}
				break;
			case 't':					/* -t: time sort requested */
				tsrt = (tsrt == 0);
				break;
			case 'u':					/* -u: print disk usage */
				usage = (usage == 0);	
				break;
			case 'R':					/* -R: recursively list subdirs */
				recd = 1;
				break;
			default:
				fprintf(stderr, "unknown arg %c\n", *s);
				exit(1);
			}
		}
	}

nargs = argc;
tsc = toscreen();			/* find out if output is to console screen */
obuf = (struct outbuf *)malloc(sizeof(*obuf));	/* point to free memory */

if(argc == 0) {
	argc++;
	curdrv(spath);					/* default to current drive */
	}
else
	strcpy(spath, *argv);

for(;;) {							/* cycle through args present */
	if(spath[1] == ':' && spath[2] == '\0')		/* if drive only */
		getpath(spath);							/* get path */
	if(usage || sizonly || ll)
		c = getsize(spath);			/* get use data only if needed */
	if(c == 0)
		search(spath);				/* go do the hard work */
	if(--argc > 0)
		strcpy(spath, *++argv);
	else {
		if(usage || sizonly) {
			if(np > 1) {
				fprintf(stdout, "-------\n%7ld bytes total", total);
				if(!nt)
					fputs("; ", stdout);
				}
			if(!nt)
				fprintf(stdout, "%7ld bytes left on drive %c\n",
					left, drive+'a');
			}
		return;
		}
	}
}

getsize(path)		/* get file cluster size */
char *path;
{
if(clsize == 0)							/* if size not already set */
	if((clsize = getcl(path)) == 0) {	/* get cluster size for drive */
		fprintf(stderr,
		 "Invalid drive: %c\n", *path);
		return(1);
		}
clmask = clsize-1;
return(0);
}

/* toscreen - find out if output is to console screen */

toscreen()
{
struct { unsigned int ax, bx, cx, dx, si, di, ds, es; 
       } r;

r.ax = 0x4400;
r.bx = 1;
sysint(0x21, &r, &r);
return(r.dx & 1);
}

/* search - search 'path' for filename or directory */

search(path)
char *path;
{
struct dta dta;					/* DOS file data table */
extern struct outbuf;			/* array of file structs */
extern int nargs;				/* number of files or directories */
int path_len;					/* length of initial path */
int z;							/* char counter */
int k = 0;						/* counts number of entries found */
char work[80];					/* working path string */
int comp();						/* string, time comparison routine */
int mask = 0x0010;				/* attribute mask */
long bytes = 0;					/* count of disk usage this directory */

if(allf)
	mask = 0x001F;
strcpy(work,path);
path_len = strlen(work);		/* save original path length */

if(!find_first(work, &dta, 0) || work[path_len-1] == QS) {
	if(work[path_len-1] != QS) {
		strcat(work, DQS);				/* if path is to a directory */
		path_len++;
		}
	strcat(work,"*.*");					/* list everything in it */
	}

if(find_first(work, &dta, mask)) {
	do {
		if(dta.attr & 0x08)					/* ignore volume label */
			continue;
		if(dta.fname[0] == '.' && !allf)	/* unless -a option */
			continue;						/* ignore "." and ".." */

		obuf[k].oattr = dta.attr;			/* stash this entry */
		obuf[k].otime = dta.ftime;
		obuf[k].odate = dta.fdate;
		obuf[k].osize = dta.fsize;
		strcpy(obuf[k].oname, dta.fname);

		if(usage || sizonly) {
			if((dta.attr & 0x10) && dta.fname[0] != '.') {
				bytes += clsize;				/* sum up disk usage */
				}
			else if(dta.fsize) {
				obuf[k].osize = ((dta.fsize + clmask) & (long)(~clmask));
				bytes += obuf[k].osize;
				}
			}

		k++;
		} while(find_next(&dta));
	}
else {
	work[path_len-1] = NULL;
	fprintf(stderr, "Can't find a file or directory named \"%s\"\n", work);
	return;
	}

work[path_len] = NULL;						/* restore directory pathname */
if(np++ && !sizonly)
	fputc(endlin(),stdout);							/* separate listing blocks */
if(usage || sizonly) {
	total += bytes;									/* total bytes to date */
	fprintf(stdout, "%7ld  ", bytes);
	}
if(recd || nargs > 1 || usage || sizonly || ID) {
	fprintf(stdout, "%s", work);					/* identify the block */
	fputc(endlin(),stdout);
	}
if(!sizonly) {
	qsort(obuf,k,sizeof(obuf[0]),comp);				/* sort the entries */
	if(ll)
		longlist(k);								/* and print them */
	else
		shortlist(k);
	}
if(!recd)
	return;											/* quit if not -R */
strcat(work, "*.*");
if(find_first(work, &dta, mask))				/* else find all sub-dirs */
	do	{
		if(dta.attr & 0x10 && dta.fname[0] != '.') {
			work[path_len] = 0;						/* discard old name */
			for(z=0; dta.fname[z] != NULL; z++)
				dta.fname[z] = tolower(dta.fname[z]);
			strcat(work, dta.fname);				/* install a new one */
			strcat(work, DQS);
			search(work);							/* and recurse */
			}
	} while(find_next(&dta));
return;
}

/* find_first - find first file in chosen directory */

find_first(path, dta, mask)
char *path;
struct dta *dta;
int mask;
{
struct { int ax, bx, cx;
         char *dx;
         int si, di, ds, es;
       } r;
extern int _showds();

r.ax = 0x1A00;							/* DOS interrupt 1A */
r.dx = (char *)dta;
r.ds = _showds();
sysint(0x21, &r, &r);					/* sets data transfer address */

r.ax = 0x4E00;							/* DOS interrupt 4E */
r.cx = mask;		
r.dx = path;
r.ds = _showds();
return(!(sysint(0x21, &r, &r) & 1));	/* fills the structure */
}

/* find_next - find the next file in the same directory */

find_next(dta)
struct dta *dta;
{
struct { int ax, bx, cx;
         char *dx;
         int si, di, ds, es;
       } r;
extern int _showds();

r.ax = 0x1A00;
r.dx = (char *)dta;
r.ds = _showds();
sysint(0x21, &r, &r);							/* set dta */

r.ax = 0x4F00;
return(!(sysint(0x21, &r, &r) & 1));			/* fill the table */
}

/* curdrv - get current default drive */

curdrv(sp)
char *sp;
{
struct { int ax, bx, cx;
         char *dx, *si, *di, *ds, *es;
       } r;

r.ax = 0x1900;							/* DOS interrupt 19 */
sysint(0x21, &r, &r);					/* gets current drive number */
*sp++ = r.ax + 'a';						/* convert to symbolic drive name */
*sp++ = ':';
return;
}

/* getpath - get path to directory on indicated drive */

getpath(sp)
char *sp;
{
struct { int ax, bx, cx, dx;
         char *si;
         int di, ds, es;
       } r;
extern int _showds();

strcat(sp, DQS);			/* append root file symbol to drive name */

r.ax = 0x4700;				/* DOS interrupt 47 gets path string */
r.dx = *sp - '`';			/* convert drive name to index */
r.ds = _showds();
r.si = sp + 3;				/* paste string after root symbol */
sysint(0x21, &r, &r);
return;
}

/* getcl - get cluster size & space left on requested drive */

getcl(pp)
char *pp;
{
struct { int ax, bx, cx, dx, si, di, ds, es;} r;
int cs;
extern long left;
extern int drive;

if(*(pp+1) == ':')					/* use specified drive if any */
	r.ax = *pp - 'a';
else {
	r.ax = 0x1900;					/* else get code for default drive */
	sysint(0x21, &r, &r);
	}
drive = r.ax & 0x7F;
if(!usage && !sizonly && drive == 2) 
	return(HARDCS);
else {
	r.dx = drive + 1;				/* 0 = default, 1 = a, etc */
	r.ax = 0x3600;
	sysint(0x21, &r, &r);			/* DOS interrupt hex 36 */
	if(r.ax == 0xFFFF)				/* gets free disk space */
		return(0);					/* and other goodies */
	else {
		cs = r.ax * r.cx;			/* r.ax = sectors/cluster */
		left = (long)cs * r.bx;		/* r.bx = # unused clusters */
		return(cs);					/* r.cx = bytes/sector */
		}							/* r.dx = drive capacity (clusters) */
	}
}

/* comp - compare size of two entries */

comp(a,b)
struct outbuf *a, *b;
{
int y;

if(tsrt) {
	if(a->odate != b->odate)					/* if dates differ */
		y = (a->odate < b->odate) ? -1 : 1;		/* that settles it */
	else
		y = (a->otime < b->otime) ? -1 : 1;		/* else compare times */
	return((rev) ? y : -y);
	}
else {
	y = strcmp(a->oname, b->oname);				/* name comparison */
	return((rev) ? -y : y);
	}
}

/* shortlist - print a list of names in 5 columns */

shortlist(k)
int k;					/* total number to print */
{
int i, m, n;

if(colm)
	n = k;				/* set for 1-column listing */
else
	n = (k + 4)/5;		/* or 5-column */

for(i=0; i < n; i++){
	for(m = 0; (i+m) < k; m += n) {
		if(obuf[i+m].oattr & 0x10)
			strcat(obuf[i+m].oname, DQS);		/* mark directories */
		putname(i+m);							/* print the name */
		fputs("   ", stdout);		
		}
	fputc(endlin(),stdout);
	}
return;
}

/* putname - convert name to lower case and print */

putname(i)
int i;
{
int c, j = 0;

while((c = tolower(obuf[i].oname[j])) != 0) {
	fputc(c, stdout);
	j++;
	}
while(j++ < NAMESIZ - 1)					/* pad to columnarize */
	fputc(' ', stdout);
}

/* endlin - end a line and watch for screen overflow */

static int lc = 0;					/* line counter */

endlin(fp)
FILE *fp;
{
extern int tsc;							/* true if output is to screen */
int c;

if(tsc && ++lc >= SCRSIZ) {			/* pause if output is to console screen */
	fputs("\n\033[7m--More--", fp);			/* and we've shown a screenful */
	c = ci();
	fputs("\033[0m\b\b\b\b\b\b\b\b        \b\b\b\b\b\b\b", fp);
	switch(c) {
		case '\r':					/* <RETURN> - show 1 more line */
			lc = SCRSIZ - 1;
			break;
		case 'q':					/* quit with "q" or "ctrl-C" */
		case '\003':
			exit(0);
		default:
			lc = 0;					/* else show another screenful */
			break;
		}
	return('\b');
	}	
else
	return('\n');
}

/* longlist - list everything about files in two columns */

struct llst {				/* structure to hold file information */
	char *fattr;			/* file attribute pointer */
	long size;				/* file size */
	int day;				/* the day of creation */
	int mnum;				/* month number */
	int yr;
	int hh;					/* creation times */
	int mm;
	int ap;					/* am or pm */
	} l;

longlist(k)
int k;			/* total number to list */
{

int i, m, n, cdate;
char *mon, *mname();

cdate = gcdate();						/* get current date (in months) */
if(colm)
	n = k;								/* set for 1 column listing */
else
	n = (k + 1)/2;						/* or for 2 column listing */
for(i=0; i < n; i++){
	for(m = 0; (m+i) < k; m += n) {		
		fill(i+m, &l);							/* fill llst structure */
		mon = mname(l.mnum);					/* conv month # to name */
		fprintf(stdout, "%s%7ld  %2d %s ",
			l.fattr, l.size, l.day, mon);
		if(cdate >= (l.yr * 12 +l.mnum) + 12)
			fprintf(stdout, " %4d  ", l.yr);	/* print year if too old */
		else {
			fprintf(stdout, "%2d:%02d%c ",
				l.hh, l.mm, l.ap);				/* else print time */
			}
		putname(i+m);
		if(m+n < k)
			fputs("\272 ", stdout);				/* double bar separator */
		}
	fputc(endlin(),stdout);
	}
return;
}

/* fill - fill long list structure with file information */

fill(i, ll)
int i;
struct llst *ll;
{
int j, k;
static char fbuf[16][4] = {
	"--w",
	"---",
	"-hw",
	"-h-",
	"s-w",
	"s--",
	"shw",
	"sh-",
	"d-w",
	"d--",
	"dhw",
	"dh-",
	"d-w",
	"d--",
	"dhw",
	"dh-"
	};

if((obuf[i].oattr & 0x10) && obuf[i].oname[0] != '.') {
	ll->size = clsize;
	j = 8;								/* if directory, use block size */
	}									/* and set dir attr offset */
else {
	ll->size = obuf[i].osize;			/* else use file size */
	j = 0;								/* and file attr offset */
	}
ll->fattr = fbuf[(obuf[i].oattr & 0x07) + j];	/* point to symbolic attr */
ll->day = obuf[i].odate & 0x1F;
ll->mnum = (obuf[i].odate >> 5) & 0x0F;
ll->yr = (obuf[i].odate >> 9) + 1980;
k = obuf[i].otime >> 5;							/* this is a mess */
ll->mm = k & 0x3f;
ll->ap = ((ll->hh = k >> 6) >= 12) ? 'p' : 'a';
if(ll->hh > 12)
	ll->hh -= 12;
if(ll->hh == 0)
	ll->hh = 12;
return;
}


/* gcdate - get current date (in months) for comparison */

gcdate()
{
struct { unsigned int ax, bx, cx, dx, si, di, ds, es;} r;

	r.ax = 0x2A00;
	sysint(0x21, &r, &r);
	return(r.cx * 12 + (r.dx >> 8));	/* yr * 12 + month */
}

/* mname - convert month number to month name */

char *mname(n)
int n;
{
static char *name[] = {
	"???",
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec"
	};
return((n < 1 || n > 12) ? name[0] : name[n]);
}

#ifdef CIC86

_showds()
{
struct{int cs, ss, ds, es;} r;

segread(&r);
return(r.ds);
}

ci()
{
return(bdos(7) & 0xFF);
}

#endif

---EOF---
echo Extracting sysint.a ...
cat > sysint.a << '---EOF---'

;
; sysint -- system interrupt call
;
; Tom Poindexter, October 1983
;
; struct regval {int ax, bx, cx, dx, si, di, ds, es;} input_regs, output_regs;
;
; int int_vec;
;
; sysint(int_vec, &input_regs, &output_regs)
;
	cseg
	public	sysint_

sysint_:
	push	bp			; standard
	mov	bp,sp			;	prolog
	push	ds			; C data seg

	mov	ax,[bp+4]		; get interrupt #
	mov	cs:intcal+1,al		; modify int

	push	bp			; save for after int
	mov	bp,[bp+06]		; point to input_regs
	mov	ax,[bp+00]
	mov	bx,[bp+02]
	mov	cx,[bp+04]
	mov	dx,[bp+06]
	mov	si,[bp+08]
	mov	di,[bp+10]
	mov	ds,[bp+12]
	mov	es,[bp+14]

intcal:
	int	255			; call the interrupt

	pop	bp			; restore parameter pointer
	pushf				; save flags for return
	mov	bp,[bp+08]		; point to output_regs
	mov	[bp+14],es
	mov	[bp+12],ds
	mov	[bp+10],di
	mov	[bp+08],si
	mov	[bp+06],dx
	mov	[bp+04],cx
	mov	[bp+02],bx
	mov	[bp+00],ax

	pop	ax			; flags into ax

	pop	ds			; recover data seg
	pop	bp			; standard
	ret				;	epilog
---EOF---
ls -l ls.doc ls.c sysint.a

-- 

                                 Ed Nather
                                 {allegra,ihnp4}!{ut-sally,noao}!utastro!nather
                                 Astronomy Dept., U. of Texas, Austin