home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_200
/
264_01
/
ls.c
< prev
next >
Wrap
Text File
|
1979-12-31
|
16KB
|
661 lines
/*
* ls - list contents of directory
*
* Usage: ls [-alrstxzAR1] [path...]
*
* Flags:
* -a list all files, including hidden and system files, ".", and ".."
* -l long listing form (extra information)
* -r reverse order of sorting
* -s display size of each file in kilobytes, and total for each directory
* -t sort by time/date (latest first)
* -x sort by extension
* -z sort by size
* -A list all files except "." and ".."
* -R list subdirectories recursively
* -1 display 1 entry per line of short form
*
* This program is in the public domain.
* David MacKenzie
* 6522 Elgin Lane
* Bethesda, MD 20817
*
* Latest revision: 05/07/88
*/
#include <ctype.h>
#ifndef tolower
#define tolower(s) _tolower(s)
#endif
#include "getdir.h"
/* Arbitrary internal limit on filename path length. */
#define MAXPATH 125
/* For dynamically allocating space to store contents of each directory. */
#define ENTRIES_INCR 25
/*
* isdir(e)
* struct sablk *e;
*/
#define isdir(e) ((e)->sa_attrib & FA_DIREC)
/*
* kbytes(b)
* int b;
* Macro to return the number of kilobytes (rounded up) taken by a given
* number of bytes.
*/
#define kbytes(b) ((b) / 1024 + ((b) % 1024 != 0))
/* Shortened format for array in memory, to save space. */
struct sablk {
char sa_attrib;
unsigned sa_ftime;
unsigned sa_fdate;
unsigned long sa_fsize;
char *sa_fname;
};
/*
* Linked list of names of directories to list recursively (automatically
* initialized to NULL).
*/
struct dirs {
char *dirs_name;
struct dirs *dirs_next;
} *dirlist;
char dirbuf[512]; /* Buffer for disk input. */
char *monthtab[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
int aflag = 0; /* Display . and .. */
int lflag = 0; /* Long listing form. */
int rflag = 0; /* Reverse order of sort. */
int sflag = 0; /* Display file sizes. */
int tflag = 0; /* Sort by time/date. */
int xflag = 0; /* Sort by extension. */
int zflag = 0; /* Sort by size. */
int Aflag = 0; /* Display hidden and system files. */
int Rflag = 0; /* Display subdirectories recursively. */
int oneflag = 0; /* One entry per line in short listing. */
int listingargs; /* Boolean for sort comparison function. */
char *
Malloc(), *Realloc();
main(argc, argv)
int argc;
char **argv;
{
void listfiles(), listdir();
int entcmp();
struct sablk *initarray(), *saveent();
char *basename(), *index();
struct dirs *dirp;
struct ffblk *dir = (struct ffblk *) dirbuf;
struct sablk *save_array; /* Array of dir entries for sorting. */
int last_entry = -1; /* Last valid entry of save_array. */
int last_normal; /* Last non-directory entry. */
char pathbuf[MAXPATH]; /* Path of globbed arguments. */
char *tail; /* Tail component of pathbuf. */
int optind; /* Loop index. */
for (optind = 1; optind < argc && *argv[optind] == '-'; ++optind) {
while (*++argv[optind]) {
switch (*argv[optind]) {
case 'a':
aflag = Aflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'r':
rflag = 1;
break;
case 's':
sflag = 1;
break;
case 't':
tflag = 1;
break;
case 'x':
xflag = 1;
break;
case 'z':
zflag = 1;
break;
case 'A':
Aflag = 1;
break;
case 'R':
Rflag = 1;
break;
case '1':
oneflag = 1;
break;
default:
printf("Usage: ls [-alrstxzAR1] [path...]\n");
exit(1);
break;
}
}
}
setdta(dirbuf); /* Set disk transfer area. */
if (optind == argc)
argv[argc++] = ".";
save_array = initarray();
for (; optind < argc; ++optind) {
/*
* Normally, when we say getfirst("dir") or getnext("dir"), where
* "dir" is the name of a directory, they return "dir" back to us
* along with its attributes, by which we discover that it has the
* FA_DIREC attribute. However, for certain pseudo-directories
* (which are real, regular directories on Unix), if we say, for
* instance, getfirst("\"), we get nothing back - no matches. To work
* around that problem, we have this special test for the problem
* pseudo-directories.
*/
if (isroot(argv[optind]) || isdot(argv[optind])) {
(void) strcpy(dir->ff_fname, argv[optind]);
dir->ff_attrib = FA_DIREC;
dir->ff_ftime = dir->ff_fdate = 0;
dir->ff_fsize = 0L;
save_array = saveent(save_array, dir, ++last_entry);
} else if (!getfirst(argv[optind], FA_ALL)) {
/*
* Copy any leading drive and/or path into pathbuf because the
* MS-DOS globbing routines return only the base of the
* filenames.
*/
(void) strcpy(pathbuf, argv[optind]);
tail = basename(pathbuf);
do {
if (dir->ff_fname[0] != '.' &&
(Aflag ||
(dir->ff_attrib & (FA_HIDDEN | FA_SYSTEM)) == 0)) {
(void) strcpy(tail, dir->ff_fname);
(void) strcpy(dir->ff_fname, pathbuf);
save_array = saveent(save_array, dir, ++last_entry);
}
} while (!getnext(argv[optind], FA_ALL));
} else if (index(argv[optind], '*') || index(argv[optind], '?')) {
printf("No match.\n");
exit(1);
}
}
listingargs = 1;
qsort((char *) save_array, last_entry + 1, sizeof(struct sablk),
entcmp);
/* Find last non-directory entry. */
for (last_normal = last_entry;
last_normal >= 0 && isdir(&save_array[last_normal]);
--last_normal);
/* First the regular listing... */
listfiles(save_array, last_normal);
/* ...then the directories. */
if (last_normal < last_entry) {
if (last_normal++ >= 0)
putchar('\n');
for (;;) {
listdir(save_array[last_normal].sa_fname, last_entry > 0);
/*
* We avoid a lot of memory waste by not using recursive function
* calls to implement recursive listing.
*/
while (dirlist) {
/* Chop head of list off and show it. */
dirp = dirlist;
dirlist = dirp->dirs_next;
putchar('\n');
/* This call might add a new head to the list! */
listdir(dirp->dirs_name, 1);
free(dirp->dirs_name);
free((char *) dirp);
}
if (++last_normal > last_entry)
break;
putchar('\n');
}
}
exit(0);
}
/*
* Display contents of one directory.
*/
void
listdir(name, label)
char *name; /* Name of directory to display. */
int label; /* Diplay the directory's name:? */
{
int entcmp();
void listfiles(), addsubdir();
char *concat(), *savestr();
struct sablk *save_array;
int last_entry;
int total_kbytes;
int i; /* Loop index. */
total_kbytes = getdir(name, &save_array, &last_entry);
if (label)
printf("%s:\n", name);
if (lflag || sflag)
printf("total %d\n", total_kbytes);
if (last_entry == -1)
return;
listingargs = 0;
qsort((char *) save_array, last_entry + 1, sizeof(struct sablk),
entcmp);
listfiles(save_array, last_entry);
if (Rflag)
for (i = last_entry; i >= 0; --i)
if (isdir(&save_array[i]) && save_array[i].sa_fname[0] != '.')
/* Add entry to list of subdirectories. */
addsubdir(concat(name, save_array[i].sa_fname));
for (i = 0; i <= last_entry; ++i)
free(save_array[i].sa_fname);
free(save_array);
}
/*
* Read contents of directory into array and return size in kilobytes.
*/
getdir(name, psave_array, plast_entry)
char *name;
struct sablk **psave_array;
int *plast_entry;
{
char *concat();
struct sablk *initarray(), *saveent();
struct ffblk *dir = (struct ffblk *) dirbuf;
struct sablk *save_array; /* Array of dir entries for sorting. */
int last_entry = -1; /* Last valid entry of save_array. */
int total_kbytes = 0; /* Ignored if sflag and lflag not given. */
char *nametmp;
int i; /* Temporary. */
save_array = initarray();
nametmp = concat(name, "*.*");
for (i = getfirst(nametmp, FA_ALL); !i; i = getnext(nametmp, FA_ALL))
if ((aflag || dir->ff_fname[0] != '.') &&
(Aflag || (dir->ff_attrib & (FA_HIDDEN | FA_SYSTEM)) == 0)) {
save_array = saveent(save_array, dir, ++last_entry);
if (sflag || lflag)
total_kbytes += kbytes(dir->ff_fsize);
}
if (last_entry == -1) {
(void) free(save_array);
*psave_array = (void *) 0;
} else
*psave_array = save_array;
*plast_entry = last_entry;
return total_kbytes;
}
void
listfiles(save_array, last_entry)
struct sablk *save_array;
int last_entry;
{
if (last_entry < 0)
return;
if (lflag)
longlist(save_array, last_entry);
else
shortlist(save_array, last_entry);
}
/*
* List files in verbose format.
*/
longlist(save_array, last_entry)
struct sablk *save_array;
int last_entry;
{
int i;
for (i = 0; i <= last_entry; ++i)
longent(&save_array[i]);
}
/*
* List files in table format.
*/
shortlist(save_array, last_entry)
struct sablk *save_array;
int last_entry;
{
int width = 0; /* Length of longest filename. */
int columns = 0; /* # of columns to show. */
int lines = 0; /* # of rows to show. */
int w; /* Temporary. */
int x; /* Temporary. */
int i; /* Loop index. */
int j; /* Loop index. */
if (oneflag)
columns = 1;
else {
/*
* Find the length of the longest filename. Add one character for
* the '\' at the ends of directories.
*/
for (i = 0; i <= last_entry; ++i)
if ((w = strlen(save_array[i].sa_fname) +
(isdir(&save_array[i]) != 0)) > width)
width = w;
/* Round off to next tab stop and adjust for kbytes field. */
width = (width & ~7) + 8 + sflag * 5;
if (width > 80)
columns = 1;
else
columns = 80 / width;
}
lines = (last_entry + columns) / columns;
for (i = 0; i < lines; ++i) {
for (j = 0; j < columns; ++j) {
x = j * lines + i;
shortent(&save_array[x]);
if (x + lines > last_entry) {
putchar('\n');
break;
}
w = strlen(save_array[x].sa_fname) + sflag * 5 +
(isdir(&save_array[x]) != 0);
while (w < width) {
w = (w & ~7) + 8;
putchar('\t');
}
}
}
}
/*
* Long listing of one file.
*/
longent(ent)
struct sablk *ent;
{
int month;
if (sflag)
printf("%4d ", kbytes(ent->sa_fsize));
month = ((ent->sa_fdate >> 5) & 15) - 1;
if (month < 0 || month > 11)
month = 11;
/* Aztec C scrambles the minutes if this is 1 printf. */
printf("%c%c%c%c%c %9lu %s %02u 19%02u ",
ent->sa_attrib & FA_DIREC ? 'd' : '-',
ent->sa_attrib & FA_SYSTEM ? 's' : '-',
ent->sa_attrib & FA_HIDDEN ? 'h' : '-',
ent->sa_attrib & FA_RDONLY ? 'r' : '-',
ent->sa_attrib & FA_ARCH ? 'a' : '-',
ent->sa_fsize,
monthtab[month], /* Month. */
ent->sa_fdate & 31, /* Day. */
((ent->sa_fdate >> 9) & 127) + 80); /* Year. */
printf("%2u:%02u %-13s\n",
(ent->sa_ftime >> 11) & 31, /* Hour. */
(ent->sa_ftime >> 5) & 63, /* Minute. */
ent->sa_fname);
}
/*
* Display short form of file.
*/
shortent(ent)
struct sablk *ent;
{
if (sflag)
printf("%4d ", kbytes(ent->sa_fsize));
printf("%s", ent->sa_fname);
if (isdir(ent))
putchar('\\');
}
struct sablk *
initarray()
{
return (struct sablk *)
Malloc(sizeof(struct sablk) * ENTRIES_INCR);
}
struct sablk *
saveent(save_array, dir, last_entry)
struct sablk *save_array;
struct ffblk *dir;
int last_entry;
{
char *normalize(), *savestr();
if (last_entry > 0 && last_entry % ENTRIES_INCR == 0)
save_array = (struct sablk *)
Realloc(save_array, sizeof(struct sablk) *
(last_entry + ENTRIES_INCR));
save_array[last_entry].sa_attrib = dir->ff_attrib;
save_array[last_entry].sa_ftime = dir->ff_ftime;
save_array[last_entry].sa_fdate = dir->ff_fdate;
save_array[last_entry].sa_fsize = dir->ff_fsize;
save_array[last_entry].sa_fname = savestr(normalize(dir->ff_fname));
return save_array;
}
/*
* Return file stuck on to the end of dir.
*/
char *
concat(dir, file)
char *dir, *file;
{
static char catbuf[MAXPATH];
int i;
if (strlen(dir) + strlen(file) + 2 > sizeof(catbuf)) {
printf("ls: filename too long\n");
exit(1);
}
if (!strcmp(dir, "") || !strcmp(dir, ".")) {
(void) strcpy(catbuf, file);
} else {
(void) strcpy(catbuf, dir);
i = strlen(dir) - 1;
if (dir[i] != '\\' && dir[i] != ':' && file[0] != '\\')
(void) strcat(catbuf, "\\");
strcat(catbuf, file);
}
return catbuf;
}
char *
savestr(s)
char *s;
{
char *t;
t = Malloc(strlen(s) + 1);
(void) strcpy(t, s);
return t;
}
/*
* Convert uppercase letters to lowercase, and non-graphic characters to '?'.
* Return the argument.
*/
char *
normalize(s)
char *s;
{
char *t;
for (t = s; *t; ++t)
if (!isascii(*t) || !isgraph(*t))
*t = '?';
else if (isupper(*t) && *t != '_')
/* Aztec C's ctype thinks that isupper('_') is true . . . */
*t = tolower(*t);
return s;
}
/*
* Comparison routine for qsort().
*/
entcmp(e1, e2)
struct sablk *e1, *e2;
{
char *index();
int result;
/* Command line argument directories are sorted to the end. */
if (listingargs) {
if (isdir(e1) && !isdir(e2))
return 1;
else if (!isdir(e1) && isdir(e2))
return -1;
/*
* Either both or neither are command line directories; do regular
* sort.
*/
}
if (zflag)
result = e1->sa_fsize < e2->sa_fsize ? -1 :
e1->sa_fsize > e2->sa_fsize ? 1 :
strcmp(e1->sa_fname, e2->sa_fname);
else if (tflag)
result = e1->sa_fdate < e2->sa_fdate ? 1 :
e1->sa_fdate > e2->sa_fdate ? -1 :
e1->sa_ftime < e2->sa_ftime ? 1 :
e1->sa_ftime > e2->sa_ftime ? -1 :
strcmp(e1->sa_fname, e2->sa_fname);
else if (xflag) {
char *ex1 = index(e1->sa_fname, '.');
char *ex2 = index(e2->sa_fname, '.');
result = !ex1 && ex2 ? -1 :
ex1 && !ex2 ? 1 :
ex1 && ex2 ? strcmp(ex1, ex2) :
strcmp(e1->sa_fname, e2->sa_fname);
} else
result = strcmp(e1->sa_fname, e2->sa_fname);
return rflag ? -result : result;
}
/*
* Return true if the path has the form "d:" or "\" or "d:\".
*/
isroot(p)
char *p;
{
int i = strlen(p);
return i == 2 && p[1] == ':' ||
!strcmp(p, "\\") ||
i == 3 && !strcmp(p + 1, ":\\");
}
/*
* Return true if the path has the form:
* ".." or "d:.." or "." or "d:." or "\." or "d:\.".
*/
isdot(p)
char *p;
{
int i = strlen(p);
return !strcmp(p, "..") ||
i == 4 && !strcmp(p + 1, ":..") ||
!strcmp(p, ".") ||
i == 3 && !strcmp(p + 1, ":.") ||
!strcmp(p, "\\.") ||
i == 4 && !strcmp(p + 1, ":\\.");
}
/*
* Return a pointer to the base of path p, e.g. with any drive
* and leading path removed.
*/
char *
basename(p)
char *p;
{
char *tail;
for (tail = p; *p; ++p)
if (*p == ':' || *p == '\\')
tail = p + 1;
return tail;
}
char *
Malloc(n)
unsigned n;
{
char *malloc();
char *p;
p = malloc(n);
if (!p) {
perror("malloc");
exit(1);
}
return p;
}
char *
Realloc(p, n)
char *p;
unsigned n;
{
char *realloc();
p = realloc(p, n);
if (!p) {
perror("realloc");
exit(1);
}
return p;
}
/*
* Add directory d to list of subdirectories to list recursively.
*/
void
addsubdir(d)
char *d;
{
char *savestr();
struct dirs *dirp;
/* Make the new entry the head of the list. */
dirp = (struct dirs *) Malloc(sizeof(struct dirs));
dirp->dirs_name = savestr(d);
dirp->dirs_next = dirlist;
dirlist = dirp;
}