[net.sources] le.c

peter@baylor.UUCP (Peter da Silva) (08/15/85)

Having recieved numerous (well, 2) requests for "le", here's the source offered
without comment. 4.2 people will undoubtedly have to patch it to handle the
crufty new directories, but I have done that for a descendent of this (a visual
shell) and it's no hassle. V7 people rejoice: it runs under that venerable
system. I have never tried to convert it ro its descendents fo SIII/SV, so
let me know what luck you have.

---- le.c -----
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

#define D_DEV	01
#define D_INO	02
#define D_MODE	04
#define D_LINKS	010
#define D_UID	020
#define D_GID	040
#define D_RDEV	0100
#define D_SIZE	0200
#define D_ATIME	0400
#define D_MTIME	01000
#define D_CTIME	02000

#define D_SMODE 04000
#define D_SUID  010000
#define D_SGID  020000

#define FALSE 0
#define TRUE 1

#define MAXENTS 512
#define MAXID 64

struct entry {
	struct direct e_dir;
	char filler;
	struct stat e_stat;
} entries[MAXENTS];
int nentries;

char *errname;
extern int errno;
int xerrno;
char *tab=" ";
long maxsize;
char sizstr[2][10] = {"%6lu%s","%6s%s"};
int dohead=0, dodir=1;
int flags = D_MODE|D_LINKS|D_UID|D_SIZE|D_MTIME;

char *emesg[3]={
	"No such error",
#define TOO_MANY 1
	"Too many directory entries",
	0
};

getdir(dir)
char *dir;
{
	int	entcmp();
	char *nameof();
	FILE *fp;
	int valid;

	if(!(fp = fopen(dir, "r"))) {
		errname=dir;
		return FALSE;
	}

	maxsize=0L;
	for(nentries=0;
	    nentries<MAXENTS &&
	      fread(&entries[nentries].e_dir,
		    sizeof entries[nentries].e_dir, 1, fp)>0;
	    nentries += valid) {
		if(valid=entries[nentries].e_dir.d_ino?1:0) {
			entries[nentries].filler=0;
			if(stat(nameof(entries[nentries].e_dir.d_name, dir),
				&entries[nentries].e_stat
			       )==-1
			  ) {
				fclose(fp);
				errname=nameof(entries[nentries].e_dir.d_name,
						dir);
				return FALSE;
			}
			if(entries[nentries].e_stat.st_size>maxsize)
				maxsize=entries[nentries].e_stat.st_size;
		}
	}

	if(nentries>=MAXENTS) {
		errno=0;
		xerrno=TOO_MANY;
		errname=dir;
		return FALSE;
	}

	fclose(fp);

	qsort(entries, nentries, sizeof(struct entry), entcmp);

	setsize(maxsize);

	return TRUE;
}

setsize(size)
long size;
{
	char tmp[32];
	int siz;
	sprintf(tmp, "%lu", size);
	siz=strlen(tmp);
	if(siz<6) siz=6;
	sprintf(sizstr[0], "%%%dlu%%s", siz);
	sprintf(sizstr[1], "%%%ds%%s", siz);
	if(dohead) {
		header();
		dohead=0;
	}
}

char *
nameof(name, dir)
char *name, *dir;
{
	char nambuf[BUFSIZ];

	if(!dir[0])
		return name;
	if(dir[0]=='.' && !dir[1])
		return name;
	else if(dir[0]=='/' && !dir[1]) {
		sprintf(nambuf, "/%s", name);
		return nambuf;
	} else {
		sprintf(nambuf, "%s/%s", dir, name);
		return nambuf;
	}
}

pname(name)
char *name;
{
	int len = 0;

	for(;*name; name++)
		if(*name<' ') { printf("^%c", *name+'@'); len+=2; }
		else if(*name=='\0177') { printf("^?"); len+=2; }
		else if(*name>'\0177') { printf("\\%03o", *name); len += 4; }
		else { putchar(*name); len++; }
	return len;
}

entcmp(e1, e2)
struct entry *e1, *e2;
{
	return strcmp(e1->e_dir.d_name, e2->e_dir.d_name);
}

fdump(dir)
char *dir;
{
	struct stat sbuf;

	if(stat(dir, &sbuf)==-1) {
		errname=dir;
		return FALSE;
	} else
		if(dodir && (sbuf.st_mode&S_IFMT)==S_IFDIR) {
			if(getdir(dir))
				return dump(dir);
			else
				return FALSE;
		} else {
			setsize(sbuf.st_size);
			statout(dir, &sbuf, ".");
			return TRUE;
		}
}

dump(dir)
char *dir;
{
	int i, j;
	int chars;

	if(flags==0) {
		for(i=0; i<=nentries/5; i++) {
			chars = 0;
			for(j=0; j<5; j++) {
				if(i+j*nentries/5<nentries)
					chars +=
					pname(entries[i+j*nentries/5].e_dir.d_name);
				if(chars<8) { putchar('\t'); chars=8; }
				if(chars<16) { putchar('\t'); chars=16; }
				else { putchar(' '); chars++; }
				chars %= 16;
			}
			putchar('\n');
		}
	}
	else for(i=0; i<nentries; i++)
		statout(entries[i].e_dir.d_name,
			&entries[i].e_stat, dir);
	return TRUE;
}

statout(name, sbuf, dir)
char *name;
struct stat *sbuf;
char *dir;
{
	char *u_name(), *g_name();

	if(flags&D_DEV)
		printf("%3d,%3d%s",
			major(sbuf->st_dev),
			minor(sbuf->st_dev),
			tab);
	if(flags&D_RDEV)
		printf("%3d,%3d%s",
			major(sbuf->st_rdev),
			minor(sbuf->st_rdev),
			tab);
	if(flags&D_INO)
		printf("%5u%s", sbuf->st_ino, tab);
	if(flags&D_SMODE)
		printf("%6o%s", sbuf->st_mode, tab);
	if(flags&D_MODE) {
		int mode = sbuf->st_mode;
		if((mode&S_IFMT)==S_IFCHR) putchar('c');
		else if((mode&S_IFMT)==S_IFBLK) putchar('b');
		else if((mode&S_IFMT)==S_IFDIR) putchar('d');
		else if((mode&S_IFMT)==S_IFREG) putchar('-');
		else putchar('?');
		triad((mode>>6)&7, mode&S_ISUID, 's');
		triad((mode>>3)&7, mode&S_ISGID, 's');
		triad(mode&7, mode&S_ISVTX, 't');
		printf("%s", tab);
	}
	if(flags&D_LINKS)
		printf("%3u%s", sbuf->st_nlink, tab);
	if(flags&D_SUID)
		printf("%3d%s", sbuf->st_uid, tab);
	if(flags&D_UID)
		printf("%-8s%s", u_name(sbuf->st_uid), tab);
	if(flags&D_SGID)
		printf("%3d%s", sbuf->st_gid, tab);
	if(flags&D_GID)
		printf("%-8s%s", g_name(sbuf->st_gid), tab);
	if(flags&D_SIZE)
		printf(sizstr[0], sbuf->st_size, tab);
	if((flags&~(D_ATIME|D_MTIME|D_CTIME)) &&
	   (flags&(D_ATIME|D_MTIME|D_CTIME))) putchar(' ');
	if(flags&D_ATIME)
		printime(&sbuf->st_atime);
	if(flags&D_MTIME)
		printime(&sbuf->st_mtime);
	if(flags&D_CTIME)
		printime(&sbuf->st_ctime);
	pname(nameof(name, dir));
	putchar('\n');
}

struct idtab {
	int id_id;
	char id_name[10];
} u_list[MAXID], g_list[MAXID];
int u_ptr=0, g_ptr=0;

char *
u_name(uid)
int uid;
{
	int i;
	struct passwd *pwptr, *getpwuid();

	for(i=0; i<u_ptr; i++)
		if(u_list[i].id_id==uid)
			return u_list[i].id_name;

	u_list[u_ptr].id_id=uid;

	if(pwptr=getpwuid(uid)) {
		for(i=0; pwptr->pw_name[i]>' '; i++)
			u_list[u_ptr].id_name[i]=pwptr->pw_name[i];
		u_list[u_ptr].id_name[i]=0;
	} else
		sprintf(u_list[u_ptr].id_name, "%d", uid);

	return u_list[u_ptr++].id_name;
}

char *
g_name(gid)
int gid;
{
	int i;
	struct group *grptr, *getgrgid();

	for(i=0; i<g_ptr; i++)
		if(g_list[i].id_id==gid)
			return g_list[i].id_name;

	g_list[g_ptr].id_id=gid;

	if(grptr=getgrgid(gid)) {
		for(i=0; grptr->gr_name[i]>' '; i++)
			g_list[g_ptr].id_name[i]=grptr->gr_name[i];
		g_list[g_ptr].id_name[i]=0;
	} else
		sprintf(g_list[g_ptr].id_name, "%d", gid);

	return g_list[g_ptr++].id_name;
}

printime(clock)
long *clock;
{
	struct tm *tmbuf, *localtime();
	static char *months[12]= {
		"Jan","Feb","Mar","Apr","May","Jun",
		"Jul","Aug","Sep","Oct","Nov","Dec"
	};

	tmbuf=localtime(clock);
	printf("%2d %3s %02d %2d:%02d %s",
		tmbuf->tm_mday,
		months[tmbuf->tm_mon],
		tmbuf->tm_year,
		tmbuf->tm_hour,
		tmbuf->tm_min,
		tab);
}

header()
{
	if(flags&D_DEV)
		printf("%7s%s", "IDev", tab);
	if(flags&D_RDEV)
		printf("%7s%s", "Rdev", tab);
	if(flags&D_INO)
		printf("%5s%s", "Inode", tab);
	if(flags&D_SMODE)
		printf("%6s%s", "Mode", tab);
	if(flags&D_MODE)
		printf("%-10s%s", "Long mode", tab);
	if(flags&D_LINKS)
		printf("%3s%s", "Lnx", tab);
	if(flags&D_SUID)
		printf("%3s%s", "UID", tab);
	if(flags&D_UID)
		printf("%-8s%s", "User", tab);
	if(flags&D_SGID)
		printf("%3s%s", "GID", tab);
	if(flags&D_GID)
		printf("%-8s%s", "Group", tab);
	if(flags&D_SIZE)
		printf(sizstr[1], "Size", tab);
	if((flags&~(D_ATIME|D_MTIME|D_CTIME)) &&
	   (flags&(D_ATIME|D_MTIME|D_CTIME))) putchar(' ');
	if(flags&D_ATIME)
		printf("%-16s%s", "Access time", tab);
	if(flags&D_MTIME)
		printf("%-16s%s", "Modify time", tab);
	if(flags&D_CTIME)
		printf("%-16s%s", "Inode time", tab);
	if(flags)
		printf("%s\n", "File name");
}

triad(bits, special, code)
int bits, special;
char code;
{
	if(bits&4) putchar('r');
	else putchar('-');

	if(bits&2) putchar('w');
	else putchar('-');

	if(special) putchar(code);
	else if(bits&1) putchar('x');
	else putchar('-');
}

main(ac, av)
int ac;
char **av;
{
	int i, j;
	int exit_status = 0;
	int filed=0;

	for(i=1; i<ac; i++) {
		if(av[i][0]=='-')
			for(j=1; av[i][j]; j++)
				switch(av[i][j]) {
					case 'T':
						if(av[i][j+1])
							tab = &av[i][++j];
						else
							tab = &av[++i][j=0];
						while(av[i][j]) j++;
						j--;
						break;
					case 'N': flags = 0; break;
					case 'A': flags = -1; break;
					case 'D': dodir=!dodir; break;
					case 'd': flags ^= D_DEV; break;
					case 'i': flags ^= D_INO; break;
					case 'm': flags ^= D_MODE; break;
					case 'M': flags ^= D_SMODE; break;
					case 'l': flags ^= D_LINKS; break;
					case 'u': flags ^= D_UID; break;
					case 'U': flags ^= D_SUID; break;
					case 'g': flags ^= D_GID; break;
					case 'G': flags ^= D_SGID; break;
					case 'r': flags ^= D_RDEV; break;
					case 's': flags ^= D_SIZE; break;
					case 'a': flags ^= D_ATIME; break;
					case 't': flags ^= D_MTIME; break;
					case 'c': flags ^= D_CTIME; break;
					case 'h': dohead=!dohead; break;
					case 'H': header(); break;
					default:
						printf("Unknown option -%c.\n", av[i][j]);
						printf("\tUsage %s [-A|-N] [-diMmlUuGgrsatc] [-Ttab] [-Hh] [-D] [file]...\n", av[0]);
						break;
				}
		else {
			filed=1;
			if(!fdump(av[i])) {
				fperror(av[0], errname);
				exit_status=errno?errno:(-xerrno);
			}
		}
	}
	if(!filed)
		if(!fdump(".")) {
			fperror(av[0], errname);
			exit_status=errno;
		}

	if(dohead) header();
	exit(exit_status);
}

fperror(prog, file)
char *prog, *file;
{
	fprintf(stderr, "%s -- ", prog);
	if(errno)
		perror(file);
	else
		fprintf(stderr, "%s: %s\n", file, emesg[xerrno]);
}
-- 
	Peter da Silva (the mad Australian)
		UUCP: ...!shell!neuro1!{hyd-ptd,baylor,datafac}!peter
		MCI: PDASILVA; CIS: 70216,1076

crm@duke.UUCP (Charlie Martin) (08/19/85)

Look, folks, if you're not going to put *any* comments in the source files,
and you're not going to send along a man page, and you're not going to give
any hints on how to use it in the text before the script, could you
*at least*

   ...PLEASE!

	...give us a hint what it does in the Subject line?

Mmph.
-- 

			Charlie Martin
			(...mcnc!duke!crm)

peter@baylor.UUCP (Peter da Silva) (08/21/85)

le is an extended ls, designed to please all the people who want to get
weird info on a file. It dumps reliable info in reliable positions, for
example it won't stick the major/minor device #s in the file size feild
of the output. Let me see about the options...


Without any flags it outputs the same info as ls...

	Usage: le [-A|-N] [-diMmlUuGgrsatc] [-Ttab] [-Hh] [-D] [file]...

	-d	Toggle 'dev' flag.
	-i	Toggle 'inode' flag.
	-m	Toggle 'mode' flag.
	-M	Toggle 'short mode' (octal mode) flag.
	-l	Toggle 'links' flag.
	-u	Toggle 'user' flag.
	-U	Toggle 'short user' (userid) flag.
	-g	Toggle 'group' flag.
	-G	Toggle 'short group' (groupid) flag.
	-r	Toggle 'rdev' flag.
	-s	Toggle 'size' flag.
	-a	Toggle 'atime' flag.
	-t	Toggle 'mtime' flag.
	-c	Toggle 'ctime' flag.
	-h	Toggle header.
	-H	Force output of the header.
	-N	(No) Clear all flags.
	-A	(All) Set all flags.
	-D	Dont list directories (like ls -d).
	-Ttab	Use the string 'tab' to seperate feilds instead of space.

	The flags can be included in any order but having any of
[dimMluUgGrsatc] before [AN] is kind of useless, and having [Hh]
before [dimMluUgGrsatc] can be misleading. You can intersperse
options with files if you want to list different directories
differently. I don't see any reason to do this but I also see no
good reason to prevent this (no, I don't use getopt).

As an example: here is 'ls -al' compared with 'le'

Note that feild 7 varies between a time and a year, and that 4 varies
between a size and a device #... while this is more useful for humans
it's kind of hard on awk... le isn't a replacement for 'ls', just for
ls in shell scripts so you can keep awk and relatives happy.

% ls -al /dev
total 4
c-w--w--w- 2 root      5,  0 Aug 21 13:46 aux1
c-w--w--w- 2 root      5,  1 May 20 09:50 aux2
crw--w--w- 1 root      0,  8 Aug 20 13:52 console
...
crw-rw-rw- 1 root      1, 23 Aug 18  1982 hsix7
crw------- 1 root      2,  1 Aug 20 13:52 kmem
-rw-rw-r-- 1 root       1635 Jul 11 17:39 lp
c-w--w--w- 2 root      5,  0 Aug 21 13:46 lp1
c-w--w--w- 2 root      5,  1 May 20 09:50 lp2
...
crw--w--w- 1 root      0, 15 Jul 23 15:38 tty7
% le /dev
drwxr-xr-x   2 root        928  11 Jul 85 16:40  /dev/.
drwxr-xr-x  11 root        368  20 Aug 85 13:52  /dev/..
c-w--w--w-   2 root          0  21 Aug 85 13:46  /dev/aux1
c-w--w--w-   2 root          0  20 May 85  9:50  /dev/aux2
crw--w--w-   1 root          0  20 Aug 85 13:52  /dev/console
...
crw-rw-rw-   1 root          0  18 Aug 82 11:11  /dev/hsix7
crw-------   1 root          0  20 Aug 85 13:52  /dev/kmem
-rw-rw-r--   1 root       1635  11 Jul 85 17:39  /dev/lp
c-w--w--w-   2 root          0  21 Aug 85 13:46  /dev/lp1
c-w--w--w-   2 root          0  20 May 85  9:50  /dev/lp2
...
crw--w--w-   1 root          0  23 Jul 85 15:38  /dev/tty7

There is a problem: le expands control characters to '^X', which may cause
problems in some cases... le -N is thus not exactly the equivalent of ls -A.
This code can be easily changed if this proves a problem. Don't ask me why
/dev/lp is a regular file, I didn't create it... :-> Time to go do some sa-type
stuff.
-- 
	Peter (Made in Australia) da Silva
		UUCP: ...!shell!neuro1!{hyd-ptd,baylor,datafac}!peter
		MCI: PDASILVA; CIS: 70216,1076

peter@baylor.UUCP (Peter da Silva) (08/21/85)

> le is an extended ls, designed to please all the people who want to get
> weird info on a file...

Forgot to mention: the source was posted a few days ago. I got complaints
about posting it without an explanation, so this is the explanation. I hope
it helps someone.
-- 
	Peter (Made in Australia) da Silva
		UUCP: ...!shell!neuro1!{hyd-ptd,baylor,datafac}!peter
		MCI: PDASILVA; CIS: 70216,1076