/*
 * lc.
 */
#include <stdio.h>
#include <hdr.h>
#include <header.h>

#define	FMAX	100

struct	dirent
{
	int	d_fnum;
	int	d_fseq;
	int	d_gok;
	int	d_name[4];
	int	d_ver;
};

struct	file
{
	char	f_name[12];
	int	f_ver;
	int	f_alloc;
	int	f_used;
	int	f_pro;
	char	f_error;
	char	f_ucha;
	char	f_time[6];
	char	f_date[7];
} file[FMAX];

int	mflag;
int	lflag;
int	oflag;
int	pflag;
int	tflag;
int	vflag;
int	nfile;
char	dir[30];

main(argc, argv)
char *argv[];
{
	register char *p;
	register c, i;
	int nf;

	nf = argc-1;
	for (i=1; i<argc; ++i) {
		p = argv[i];
		if (*p == '-') {
			++p;
			while (c = *p++)
				switch(c) {

				case '1':
					++oflag;
					break;

				case 'l':
				case 'L':
					++lflag;
					break;

				case 'm':
				case 'M':
					++mflag;
					break;

				case 'p':
				case 'P':
					++pflag;
					break;

				case 't':
				case 'T':
					++tflag;
					break;

				case 'v':
				case 'V':
					++vflag;
					break;

				default:
					error("Usage: lc [-1lmptv] [dir].\n");
				}
			argv[i] = 0;
			--nf;
		}
	}
	if (nf == 0) 
		list("", 0);
	else
		for (i=1; i<argc; ++i)
			if (p = argv[i])
				list(p, nf!=1);
}

list(s, b)
char *s;
{
	if (collect(s) && nfile) {
		sort();
		if (b)
			printf("\n%s:\n\n", s);
		output();
		if (tflag)
			totals();
	}
}

collect(s)
char *s;
{
	register struct file *fp;
	register FILE *dfp;
	struct dirent db;
	struct hdr *hp;
	char *d, *t, dn[40];

	if (!dirname(dn, s)) {
		err(s, "bad format");
		return(0);
	}
	if ((dfp=fopen(dn, "ru")) == NULL) {
		err(s, "cannot list");
		return(0);
	}
	if (lflag || tflag)
		hp = alloc(sizeof(*hp));
	fp = file;
	while (fget(&db, sizeof(db), dfp) == sizeof(db)) {
		if (db.d_fnum == 0)
			continue;
		if (fp >= &file[FMAX]) {
			err(s, "too many files");
			break;
		}
		r50toa(fp->f_name, db.d_name, 4);
		fp->f_ver = db.d_ver;
		if (lflag || tflag) {
			if (getheader(hp, db.d_fnum, db.d_fseq, dfp)) {
				fp->f_error = 0;
				fp->f_alloc = hp->h_ufat.f_hibk[1];
				fp->f_used  = hp->h_ufat.f_efbk[1];
				if (fp->f_used > fp->f_alloc)
					fp->f_used = fp->f_alloc;
				fp->f_pro = hp->h_fpro;
				fp->f_ucha = hp->h_ucha;
				if (hp->i_rvdt[0]) {
					d = hp->i_rvdt;
					t = hp->i_rvti;
				} else {
					d = hp->i_crdt;
					t = hp->i_crti;
				}
				copy(fp->f_date, d, 7);
				copy(fp->f_time, t, 6);
			} else
				fp->f_error = 1;
		}
		++fp;
	}
	fclose(dfp);
	nfile = fp-file;
	if (lflag || tflag)
		free(hp);
	return(1);
}

dirname(db, s)
char *db;
register char *s;
{
	register char *t;
	register c;
	char *d;
	int dev, g, u, uic;

	dev = 0;
	uic = 0;
	u = getuid();
	g = getgid();
	while (*s) {
		if (*s == '[') {
			if (uic++)
				return(0);
			++s;
			g = 0;
			while ((c = *s++) && c>='0' && c<='7')
				g = (g<<3) + c-'0';
			if (c != ',')
				return(0);
			u = 0;
			while ((c = *s++) && c>='0' && c<='7')
				u = (u<<3) + c-'0';
			if (c != ']')
				return(0);
		} else {
			if (dev++)
				return(0);
			d = s;
			while ((c = *s++) && c!=':')
				;
			if (c != ':')
				return(0);
		}
	}
	s = dir;
	if (dev) {
		t = d;
		while ((*s++ = *t++) != ':')
			;
	}
	sprintf(s, "[%o,%o]", g, u);
	s = db;
	if (dev) {
		t = d;
		while ((*s++ = *t++) != ':')
			;
	}
	sprintf(s, "[0,0]%03o%03o.dir", g, u);
	return(1);
}

err(s, m)
char *s, *m;
{
	if (*s)
		fprintf(stderr, "%s: %s\n", s, m);
	else
		fprintf(stderr, "%c%s\n", *m&~' ', m+1);
}

sort()
{
	register struct file *b, *m, *p;
	struct file tf;

	for (b=&file[0]; b<&file[nfile-1]; ++b) {
		m = b;
		for (p=b+1; p<&file[nfile]; ++p)
			if (cmp(m->f_name, p->f_name))
				m = p;
		if (m != b) {
			copy(&tf, m, sizeof(tf));
			copy(m,   b, sizeof(tf));
			copy(b, &tf, sizeof(tf));
		}
	}
}

cmp(a, b)
register char *a, *b;
{
	register n;
	int ca, cb;

	n = 12;
	while (n-- && ((ca=*a++)==(cb=*b++)))
		if (ca == 0)
			break;
	return(ca > cb);
}

output()
{
	register struct file *fp;
	register c, n;
	int w;

	w = (!lflag && !mflag && !oflag) ? 4 : 1;
	n = 0;
	for (fp=&file[0]; fp<&file[nfile]; ++fp) {
		if (n >= w) {
			putchar('\n');
			n = 0;
		}
		++n;
		c = outname(fp);
		if (lflag || mflag || n<w)
			while (c++ < 18)
				putchar(' ');
		if (lflag)
			outlong(fp);
		if (mflag) {
			if (lflag)
				putchar(' ');
			outitle(fp);
		}
	}
	if (n)
		putchar('\n');
}

outname(fp)
register struct file *fp;
{
	register char *s;
	register c;
	char vb[8];

	c = 0;
	s = fp->f_name;
	while (s<&fp->f_name[9] && *s!=' ') {
		putchar(*s++);
		++c;
	}
	s = &fp->f_name[9];
	if (*s != ' ') {
		putchar('.');
		++c;
		while (s<&fp->f_name[12] && *s!=' ') {
			putchar(*s++);
			++c;
		}
	}
	if (c == 0) {
		putchar('.');
		++c;
	}
	if (vflag) {
		sprintf(vb, ";%d", fp->f_ver);
		s = vb;
		while (*s) {
			putchar(*s++);
			++c;
		}
	}
	return(c);
}

outitle(fp)
register struct file *fp;
{
	register FILE *mfp;
	register char *t;
	struct header hb;
	char fn[60];

	fullname(fn, fp);
	if ((mfp=fopen(fn, "ru")) == NULL)
		t = "god only knows";
	else {
		fget(&hb, sizeof(hb), mfp);
		if (feof(mfp) || hb.h_magic!=MAGIC)
			t = "not a music file";
		else
			t = hb.h_title;
		fclose(mfp);
	}
	printf("(%s)", t);
}

fullname(fn, fp)
char *fn;
struct file *fp;
{
	register char *p1, *p2;
	register c;

	p1 = fn;
	p2 = dir;
	while (*p1++ = *p2++)
		;
	--p1;
	p2 = fp->f_name;
	while (p2<&fp->f_name[9] && (c=*p2++)!=' ')
		*p1++ = c;
	p2 = &fp->f_name[9];
	if (*p2 != ' ') {
		*p1++ = '.';
		while (p2<&fp->f_name[12] && (c=*p2++)!=' ')
			*p1++ = c;
	}
	sprintf(p1, ";%d", fp->f_ver);
}

outlong(fp)
struct file *fp;
{
	register char *d, *t;
	register n;

	if (fp->f_error) {
		printf("Cannot read attributes");
		return;
	}
	printf("%4d%4d ", fp->f_used, fp->f_alloc);
	putchar((fp->f_ucha&UC_CON)!=0 ? 'c' : ' ');
	putchar((fp->f_ucha&UC_DLK)!=0 ? 'l' : ' ');
	if (pflag) {
		printf(" [");
		for (n=0; n<16; ++n) {
			if (n==4 || n==8 || n==12)
				putchar(',');
			if ((fp->f_pro&01) == 0)
				putchar("rwed"[n&03]);
			else
				putchar('-');
			fp->f_pro >>= 1;
		}
		putchar(']');
	}
	printf(" %.2s:%.2s", fp->f_time, fp->f_time+2);
	fp->f_date[3] |= ' ';
	fp->f_date[4] |= ' ';
	printf(" %.2s-%.3s-%.2s", fp->f_date, fp->f_date+2, fp->f_date+5);
}

totals()
{
	register struct file *fp;
	register talloc, tused;

	talloc = 0;
	tused  = 0;
	for (fp=&file[0]; fp<&file[nfile]; ++fp)
		if (fp->f_error == 0) {
			talloc += fp->f_alloc;
			tused  += fp->f_used;
		}
	printf("\nTotal allocated: %d\n", talloc);
	printf(  "Total used:      %d\n", tused);
}
