/*--- EDIT # 0124	4 May 1982   14:38:52	DB1:[21,6]DIR.C;207  */
/*--- PREVIOUS EDIT	4 May 1982   14:28:46	DB1:[21,6]DIR.C;206  */
/*
 * DIR [-1dlptv] [dir ...]
 */
/*)BUILD	$(PROGRAM)	= dir
		$(FILES)	= { dir qksort gethdr }
		$(TKBOPTIONS)	= {
			LIBR = FCSRES:RO
			TASK = ...DIR
		}
*/

#include <stdio.h>
#include <hdr.h>

#define	FMAX	1000

/*
 * This is the format of an
 * RSX directory entry.
 */
struct	dirent
     {
   int	d_fnum;
   int	d_fseq;
   int	d_gok;
   int	d_name[4];
   int	d_ver;
};

/*
 * File table entry.
 * Saves information from the
 * directory.
 */
struct	file_str
     {
   int	f_fnum;
   int	f_fseq;
   char	f_name[12];
   int	f_ver;
};

int	eflag	0;	/* sort by extent as primary key */
int	dflag	0;
int	lflag	0;
int	oflag	0;
int	pflag	0;
int	tflag	0;
int	vflag	0;
struct	hdr	ib;
int	nfile;
int	talloc	0;
int	tused	0;
struct	file_str	file[FMAX];
extern char $$uic[1];
extern int fn_cmp();	/* compare routine for qksort */
FILE *dir;

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

   if (ftty(stdout))
      tiatt();	/* attach the terminal so CTRL/O works */
   nf = argc-1;
   for(i=1; i<argc; ++i) {
      p = argv[i];
      if(*p == '-') {
	 ++p;
	 while(c = *p++)
	    switch(c) {

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

	 case '1':
	    ++oflag;
	    break;

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

	 case 'd':
	 case 'D':
	    ++dflag;
	    break;

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

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


	 case 'e':
	 case 'E':
	    ++eflag;
	    break;

	 default:
	    usage();
	 }
	 argv[i] = 0;
	 --nf;
      }
   }
   if(nf == 0)
      list("", 0);
   else
      for(i=1; i<argc; ++i)
      if(p = argv[i])
      list(p, nf!=1);
}

usage()
{
   fprintf(stderr, "Usage: DIR [-1delptv] [dir dir ...]\n");
   exit(1);
}

list(s, b)
char *s;
{
   if(!collect(s) || nfile==0)
      return;
   if(!dflag) {
      if (nfile > 500) fprintf (stderr,
	   "Have patience. I am sorting %d filenames.\n",
	   nfile);
      qksort(file, nfile, sizeof file[0], &fn_cmp);
   }
   if(b)
      printf("\n%s:\n", s);
   output();
   fclose (dir);
}

collect(s)
char *s;
{
   register struct file_str *p;
   struct dirent db;
   char dn[40];
   char sel[40];	/* filename selector string */
   char *cp, *xp;
   int i;

   if(!dirname(dn, s, sel)) {
      err(s, "bad format");
      return(0);
   }
/*   printf("dn:'%s'\tsel:'%s'\n", dn, sel);*/
   if((dir=fopen(dn, "run")) == NULL) {
      err(s, "cannot list");
      return(0);
   }
   p = file;
   while(fget(&db, sizeof(db), dir) == sizeof(db)) {
      if((db.d_fnum == 0) || (db.d_gok != 0))
	 continue;
      if(p >= &file[FMAX]) {
	 err(s, "too many files");
	 break;
      }
      p->f_fnum = db.d_fnum;
      p->f_fseq = db.d_fseq;
      r50toa(p->f_name, db.d_name, 4);
      p->f_ver = db.d_ver;
      if (sel[0] != NULL) { /* see if the filename matches */
	 for (cp = dn, xp = p->f_name, i = 0; i < 9; i++) {
	    if ((*cp = *xp++) != ' ')  cp++;
	 }
	 *cp++ = '.';
	 for (; i < 12; i++) {
	    if ((*cp = *xp++) != ' ') cp++;
	 }
	 *cp++ = ';';	*cp = NULL;
/*	 printf("\ntry:%s\n", dn);*/
	 if (!match(dn, sel)) --p;	/* skip this one, it doesn't match */
      }
      ++p;
   }
   nfile = p-file;
   return(1);
}
/*************************************/
dirname(db, s, sel)
char *db;
register char *s;
char *sel;
{
   register char *d, *s1;
   register int c;
   int uic, dev, u, g;
   char dn[60];

   dev = 0;
   uic = 0;
   *sel = *db = NULL;
   u = $$uic[0] & 0377;
   g = $$uic[1] & 0377;
   /* LOOK FOR THE DEVICE NAME */
   s1 = s;
   while (*s1) {
      if (*s1++ == ':') {
	 dev++;
	 d = s;	s = s1;
      }
   }
/* Collect the UIC (if any) */
   if(*s == '[') {
      ++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);
   }

/* The rest is the filename selector, same as SRD:
 *	* matches 0 or more characters (different than the next one.
 *	% matches one character
 *	? matches one character
 *	; ends the specifier early
 * All selectors are assumed to be ended with '*;'.
*/

   if (*s) {
      while ((c = *s++) != NULL) {
	 *sel++ = toupper(c);
	 if (c == '*') {	/* two stars in a row collapse to just one */
	    while (*s != NULL && *s == '*') s++;
	 }
	 else if (c == '%' || c == '.' || c == ';' ||
	    isdigit(c) || isalpha(c)) {
	 }
	 else if (c == '?')
	    *(sel-1) = '%';
	 else
	    return (0);	/* bad pattern */
      }
      if (*(sel-1) != '*') *sel++ = '*';
      *sel++ = ';';
   }
   *sel == NULL;

/* build the directory filename string */
   s = db;
   if(dev)
      while((*s++ = *d++) != ':')
      ;
   d = "[0,0]";
   while(*s++ = *d++)
      ;
   --s;
   sprintf(dn, "%03o%03o.DIR", g, u);
   d = dn;
   while(*s++ = *d++)
      ;
   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);
}

/***************************************/
/* Simple pattern matcher */
match(s, t)
register char *s;	/* string */
register char *t;	/* template */
{
   while (1) {
/*      printf("str:%s	temp:%s\n", s, t);*/
      /* star matches zero or more chars,
       *  extending each time the rest fails */
      if (*t == '*') {
	 t++;
	 while (1) {
	    if (match(s, t)) return (1);/* yup, it did */
	    if (*s++ == ';') return (0);/* at end of target string, fail*/
	 }
      }

      /* Swallow some */
      while (*s == *t) {
	 if (*s++ == ';') return (1);	/* at end on both */
	 t++;
      }

      /* Wild-card single char works if not at end of target */
      if (*t != '*')
	 if (*t++ != '%' || *s++ == ';') return (0);
   }

   return(1);
}



/***************************************/
/* Compare 2 file-names, returning -1, =0 +1, same as
 * strcmp
*/
fn_cmp (f1, f2)
struct file_str *f1, *f2; /* given the file struct (YUCH!) */
{
   int c;	/* comparison result */
   if (!eflag)
      c = bufcmp(f1->f_name, f2->f_name, 12);
   else {
      c = bufcmp (&(f1->f_name[9]), &(f2->f_name[9]), 3);
      if (!c) bufcmp (f1->f_name, f2->f_name, 9);
   }
   if (!c) c = f1->f_ver - f2->f_ver;
   return (c);
}
/******* compare 2 char buffers, similar to
 * strcmp, but going by size */
bufcmp (a, b, size)
register char *a, *b;
register int size;
{
   int c;

   c = 0;
   while ((size--) && (!c)) {
      c = (*a++) - (*b++);
   }
   return (c);
}
output(nf)
{
   register struct file_str *p;
   register int c, n;
   int w;

   w = 1;
   if(!oflag && !lflag) {
      w = 4;
   }
   p = file;
   n = 0;
   while(nfile--) {
      if(n >= w) {
	 putchar('\n');
	 n = 0;
      }
      c = outname(p);
      if (c) {
	 ++n;
	 if(lflag || n<w)
	    while(c++ < (lflag?  14:18))
	    putchar(' ');
	 if(lflag)
	    longinfo(p);
      }
      ++p;
   }
   if(n)
      putchar('\n');
   if(lflag && tflag) {
      printf("\nTotal used:      %6d\n", tused);
      printf("Total allocated: %6d\n", talloc);
   }
}

outname(p)
register struct file_str *p;
{
   register char *s;
   register int c;
   char vb[8];
   struct file_str *temp;

   c = 0;
   s = p->f_name;
/* IF we are not printing version numbers but are sorting, only print the
highest version file. They are in ascending order, so only print this one
if the next one has a different name */
   temp = p;
   temp++;
   if ((!dflag) && (!vflag) && !bufcmp(p->f_name, temp->f_name, 12)) {
/*  printf ("%06o  %06o \n",p,temp); */
      return (0);
   }
   while(s<&p->f_name[9] && *s!=' ') {
      putchar(*s++);
      ++c;
   }
   s = &p->f_name[9];
   if(*s != ' ') {
      putchar('.');
      ++c;
      while(s<&p->f_name[12] && *s!=' ') {
	 putchar(*s++);
	 ++c;
      }
   }
   if(c == 0) {
      putchar('.');
      ++c;
   }
   if(vflag) {
      sprintf(vb, ";%o", p->f_ver);
      s = vb;
      while(*s) {
	 putchar(*s++);
	 ++c;
      }
   }
   return(c);
}

longinfo(p)
struct file_str *p;
{
   register char *d, *t;
   register int n;

   if((n=gethdr(&ib, p->f_fnum, p->f_fseq, dir)) < 0) {
      printf("Error (%d) reading file attributes", n);
      return;
   }
   if ((ib.h_ufat.f_ffby == 0) && (ib.h_ufat.f_efbk[1] !=0 ))
      ib.h_ufat.f_efbk[1] -= 1;
   printf("%4d%4d ", ib.h_ufat.f_efbk[1], ib.h_ufat.f_hibk[1]);
   talloc += ib.h_ufat.f_hibk[1];
   tused += ib.h_ufat.f_efbk[1];
   printf("%c%c", ib.h_ucha&UC_CON?'C':' ', ib.h_ucha&UC_DLK?'L':' ');
   if(pflag) {
      printf(" [");
      for(n=0; n<16; ++n) {
	 if(n==4 || n==8 || n==12)
	    putchar(',');
	 if((ib.h_fpro&01) == 0)
	    putchar("RWED"[n&03]);
	 else
	    putchar('-');
	 ib.h_fpro >>= 1;
      }
      putchar(']');
   }
   d = &ib.i_crdt;
   t = &ib.i_crti;
   printf(" %.2s:%.2s", t, t+2);
   printf(" %.2s-%c%c%c-%.2s", d, d[2], d[3]|' ', d[4]|' ', d+5);
   if(ib.i_rvdt[0]) {
      if ( !(tflag && pflag)) printf("   ");
      d = &ib.i_rvdt;
      t = &ib.i_rvti;
      printf(" %.2s:%.2s", t, t+2);
      printf(" %.2s-%c%c%c-%.2s", d, d[2], d[3]|' ', d[4]|' ', d+5);
   }
}
