[comp.sources.amiga] v02i038: Disk Cataloger

peter@sugar.UUCP (Peter da Silva) (10/06/87)

    Here is a disk cataloger!  I personally have been needing this
for quite some time!
    Thanx Peter!
	Doc

: This archive contains the following files...
: 'catalog.man'
: 'catalog.c'
: 'PatMatch.c'
: 'Makefile'
: To extract them, run the following through /bin/sh
echo x - catalog.man
sed 's/^X//' > catalog.man << '//END'
XNAME
X
X	CATALOG -- build file catalog.
X
X
XSYNOPSIS
X
X	CATALOG [TO|IN catalog] [ADD] [MULTI] volumes [FIND patterns] [LIST]
X
X
XDESCRIPTION
X
X	This is a CLI-based program that builds a useful little catalog of
X	all the files on your disks. The catalog itself is kept in text
X	format, so you can edit it if need be. The syntax of the command is:
X
X	TO|IN catalog		Specify the name of the file to read the catalog
X					from and/or write it to.
X
X	[ADD] volumes		Specify the volumes to search for files. The
X					keyword ADD is optional.
X
X	MULTI				Search the specified volumes multiple times.
X					generally you want to use a device name for the
X					volume name when you do this. e.g. "MULTI df1:".
X
X	FIND patterns		Search the catalog file for the specified patterns.
X					Patterns are a superset of AmigaDOS patterns: you can
X					specify, for example, (#?.h)|(#?.c) to search for a
X					file ending in .h or .c.
X
X	LIST				List the contents of the catalog in a convenient
X					format.
X
X
XFILES
X
X	disk.catalog	for default catalog file.
X
X
XBUGS
X
X	None known.
X
X
XAUTHOR
X
X	Peter da Silva.
X	Pattern Matching code by Jeff Lydiatt
X
X
XNOTES
X
X		At some point this will be updated to use a variation of my
X	STDFILE/INSTANT-APPLICATION interface. It would also be desirable
X	to be able to DELETE based on a pattern, allowing such things as:
X
X	CATALOG ADD MULTI df0: DELETE #?.(info)|(zing)
X
X	Also, sorting or searching by directory might be desirable.
X
X	Insertion into the catalog is slow. A faster implementation using
X	a binary tree instead of a linked list would definitely be desirable.
//END
echo x - catalog.c
sed 's/^X//' > catalog.c << '//END'
X/* catalog -- cli-based disk catalog -- Peter da Silva */
X
X/* Usage:
X *
X * Catalog [TO|IN catalog] [ADD] [MULTI] volumes [FIND patterns] [LIST]
X */
X#include <intuition/intuition.h>
X#include <exec/memory.h>
X#include <libraries/dos.h>
X#include <stdio.h>
X#include <ctype.h>
X#define BPTR long
X
Xchar *catalog = 0;
X#define DEFCATALOG "disk.catalog"
X
Xint loaded;
Xint modified;
Xint mode;
Xint multi;
X#define ADD 0
X#define FIND 1
X
XUBYTE *AllocMem();
Xchar *malloc();
Xchar *strchr();
X
Xmain(ac, av)
Xint ac;
Xchar **av;
X{
X	int i;
X
X	if(ac<=1) {
X		printf("Usage:\n");
X		printf("Catalog [TO|IN catalog] [ADD] [MULTI] volumes [FIND patterns] [LIST]\n\n");
X		printf("Default catalog=\"%s\"\n", DEFCATALOG);
X		exit(0);
X	}
X	mode = ADD;
X	loaded = 0;
X	modified = 0;
X	multi = 0;
X
X	for(i = 1; i < ac; i++) {
X		if(strmatch(av[i], "TO") || strmatch(av[i], "IN")) {
X			catalog = av[++i];
X		} else if(strmatch(av[i], "FIND")) {
X			mode = FIND;
X		} else if(strmatch(av[i], "ADD")) {
X			mode = ADD;
X		} else if(strmatch(av[i], "LIST")) {
X			list();
X		} else if(strmatch(av[i], "MULTI")) {
X			multi = 1;
X		} else {
X			switch(mode) {
X				case ADD: add(av[i]); break;
X				case FIND: find(av[i]); break;
X			}
X		}
X	}
X	if(modified)
X		dump_files();
X}
X
Xstruct _f {
X	char *file;
X	char *path;
X	long size;
X	char *note;
X	struct _f *next, *prev;
X} *filelist = 0;
X
Xfreenode(n)
Xstruct _f *n;
X{
X	if(n->file)
X		free(n->file);
X	if(n->path)
X		free(n->path);
X	if(n->note)
X		free(n->note);
X	free(n);
X}
X
Xprintnode(n)
Xstruct _f *n;
X{
X	printf("%-24s %8ld %s\n", n->file, n->size, n->path);
X	if(n->note)
X		printf("%24s %s\n", "", n->note);
X}
X
Xadd_file(filename, pathname, filesize, filenote)
Xchar *filename;
Xchar *pathname;
Xlong filesize;
Xchar *filenote;
X{
X	struct _f *tmp, *ptr;
X	int cmp;
X
X	tmp = malloc(sizeof(struct _f));
X	if(!tmp) return 0;
X	tmp->next = tmp;
X	tmp->prev = tmp;
X	tmp->size = filesize;
X	tmp->file = 0;
X	tmp->path = 0;
X	tmp->note = 0;
X	tmp->file = malloc(strlen(filename)+1);
X	if(!tmp->file) {
X		freenode(tmp);
X		return 0;
X	}
X	strcpy(tmp->file, filename);
X
X	tmp->path = malloc(strlen(pathname)+1);
X	if(!tmp->path) {
X		freenode(tmp);
X		return 0;
X	}
X	strcpy(tmp->path, pathname);
X
X	if(filenote && filenote[0]) {
X		tmp->note = malloc(strlen(filenote)+1);
X		if(!tmp->note) {
X			freenode(tmp);
X			return 0;
X		}
X		strcpy(tmp->note, filenote);
X	}
X
X	if(!filelist) {
X		filelist = tmp;
X	} else {
X		ptr = filelist;
X
X		/* first check if you can insert it at the end of the list */
X		/* if so, don't bother scanning. This should improve the */
X		/* performance on inserting sorted lists */
X		if((cmp = order(ptr->prev, tmp)) >= 0) {
X			do {
X				if((cmp=order(ptr, tmp)) >= 0)
X					break;
X				ptr = ptr->next;
X			} while(ptr != filelist);
X		}
X
X		if(cmp==0) {
X			freenode(tmp);
X		} else {
X			tmp->prev = ptr->prev;
X			tmp->next = ptr;
X			ptr->prev->next = tmp;
X			ptr->prev = tmp;
X			if(ptr==filelist && cmp>=0)
X				filelist = tmp;
X		}
X	}
X	return 1;
X}
X
Xorder(n1, n2)
Xstruct _f *n1, *n2;
X{
X	int cmp;
X
X	cmp = compare(n1->file, n2->file);
X	if(cmp==0)
X		cmp = compare(n1->path, n2->path);
X	return cmp;
X}
X
Xdel_vol(vol)
Xchar *vol;
X{
X	char *ptr, *nxt;
X	int just_moved;
X
X	if(!filelist)
X		return 1;
X
X	ptr = filelist;
X	do {
X		nxt = ptr->next;
X		just_moved = 0;
X		if(strncmp(vol, ptr->path, strlen(vol))) {
X			ptr->prev->next = ptr->next;
X			ptr->next->prev = ptr->prev;
X			freenode(ptr);
X			if(ptr==filelist) {
X				if(filelist==nxt) {
X					filelist = 0;
X					break;
X				}
X				filelist = nxt;
X				just_moved = 1;
X			}
X		}
X		ptr = nxt;
X	} while(!just_moved && ptr != filelist);
X	return 1;
X}
X
Xdump_files()
X{
X	FILE *fp;
X	struct _f *ptr;
X
X	if(catalog==0)
X		catalog = DEFCATALOG;
X	if(!(fp = fopen(catalog, "w"))) {
X		perror(catalog);
X		return 0;
X	}
X
X	if(filelist) {
X		ptr = filelist;
X		do {
X			if(ptr->note)
X				fprintf(fp, "%s:%ld:%s:%s\n",
X					ptr->file, ptr->size, ptr->path, ptr->note);
X			else
X				fprintf(fp, "%s:%ld:%s\n",
X					ptr->file, ptr->size, ptr->path);
X			ptr = ptr->next;
X		} while(ptr != filelist);
X	}
X
X	fclose(fp);
X	return 1;
X}
X
Xint loaded_files, files, directories;
X
Xload_files()
X{
X	FILE *fp;
X	char *p;
X	char buffer[BUFSIZ];
X	long filesize;
X	char *filename, *pathname, *filenote;
X
X	if(catalog==0)
X		catalog = DEFCATALOG;
X
X	if(!(fp = fopen(catalog, "r"))) {
X		perror(catalog);
X		return 0;
X	}
X
X	loaded_files = 0;
X	while(fgets(buffer, BUFSIZ, fp)) {
X		buffer[strlen(buffer)-1] = 0;
X		filename = buffer;
X		p = strchr(buffer, ':');
X		if(p) {
X			*p++ = 0;
X			filesize = atoi(p);
X			p = strchr(p, ':');
X			if(p) {
X				*p++ = 0;
X				pathname = p;
X				p = strchr(p, ':'); /* skip ':' in path name */
X				if(p) p = strchr(p+1, ':');
X				if(p) {
X					*p++ = 0;
X					if(*p)
X						filenote = p;
X					else
X						filenote = 0;
X				} else
X					filenote = 0;
X				add_file(filename, pathname, filesize, filenote);
X				loaded_files++;
X			}
X		}
X	}
X
X	printf("%s: %d files\n", catalog, loaded_files);
X	fclose(fp);
X}
X
Xchar pathname[BUFSIZ];
X
Xscan_disk()
X{
X	struct FileInfoBlock *FIB;
X	BPTR lock;
X	int len;
X
X	if(!(lock=Lock(pathname, ACCESS_READ))) {
X		fprintf(stderr, "Can't access %s: error %d\n",
X			pathname, IoErr());
X		return 0;
X	}
X	FIB = (struct FileInfoBlock *)AllocMem(
X		sizeof(struct FileInfoBlock),
X		MEMF_CLEAR);
X	if(!FIB) {
X		UnLock(lock);
X		return 0;
X	}
X	if(!Examine(lock, FIB)) {
X		fprintf(stderr, "Can't examine %s: error %d\n",
X			pathname, IoErr());
X		UnLock(lock);
X		FreeMem(FIB, sizeof(struct FileInfoBlock));
X		return 0;
X	}
X	if(FIB->fib_DirEntryType < 0) {
X		fprintf(stderr, "%s: is not a directory.\n");
X		UnLock(lock);
X		FreeMem(FIB, sizeof(struct FileInfoBlock));
X		return 0;
X	}
X	while(ExNext(lock, FIB)) {
X		if(FIB->fib_DirEntryType < 0) {
X			add_file(
X				FIB->fib_FileName,
X				pathname,
X				FIB->fib_Size,
X				FIB->fib_Comment);
X			files++;
X		} else {
X			len = strlen(pathname);
X			if(pathname[len-1] == ':')
X				strcpy(&pathname[len], FIB->fib_FileName);
X			else
X				sprintf(&pathname[len], "/%s", FIB->fib_FileName);
X			if(!scan_disk()) {
X				UnLock(lock);
X				FreeMem(FIB, sizeof(struct FileInfoBlock));
X				return 0;
X			}
X			pathname[len] = 0;
X			directories++;
X		}
X	}
X	UnLock(lock);
X	FreeMem(FIB, sizeof(struct FileInfoBlock));
X	return 1;
X}
X
Xadd(vol)
Xchar *vol;
X{
X	if(multi == 0)
X		_add(vol);
X	else {
X		while(1) {
X			char tmp[BUFSIZ];
X			printf("Scan %s [Y]? ", vol);
X			gets(tmp);
X			if(tmp[0]=='n' || tmp[0]=='N')
X				break;
X			_add(vol);
X		}
X	}
X}
X
X_add(vol)
Xchar *vol;
X{
X	struct FileInfoBlock *FIB;
X	BPTR lock;
X	int len;
X
X	if(!(lock=Lock(vol, ACCESS_READ))) {
X		fprintf(stderr, "Can't access %s: error %d\n",
X			vol, IoErr());
X		return 0;
X	}
X	FIB = (struct FileInfoBlock *)AllocMem(
X		sizeof(struct FileInfoBlock),
X		MEMF_CLEAR);
X	if(!FIB) {
X		UnLock(lock);
X		return 0;
X	}
X	if(!Examine(lock, FIB)) {
X		fprintf(stderr, "Can't examine %s: error %d\n",
X			vol, IoErr());
X		UnLock(lock);
X		FreeMem(FIB, sizeof(struct FileInfoBlock));
X		return 0;
X	}
X	sprintf(pathname, "%s:", FIB->fib_FileName);
X	UnLock(lock);
X	FreeMem(FIB, sizeof(struct FileInfoBlock));
X
X	if(!loaded) {
X		load_files();
X		loaded = 1;
X	}
X	del_vol(vol);
X	files = 0;
X	directories = 1;
X	if(scan_disk()) {
X		modified = 1;
X		printf("%s %d files %d directories.\n",
X			pathname, files, directories);
X	}
X}
X
Xfind(s)
Xchar *s;
X{
X	struct _f *ptr;
X	static WORD PatCode[128];
X
X	if(!loaded) {
X		load_files();
X		loaded = 1;
X	}
X
X	CmplPat(s, PatCode);
X	if(ptr = filelist) {
X		do {
X			if(Match(s, PatCode, ptr->file))
X				printnode(ptr);
X			ptr = ptr->next;
X		} while(ptr != filelist);
X	}
X}
X
Xlist()
X{
X	struct _f *ptr;
X
X	if(!loaded) {
X		load_files();
X		loaded = 1;
X	}
X
X	if(ptr = filelist) {
X		do {
X			printnode(ptr);
X			ptr = ptr->next;
X		} while(ptr != filelist);
X	}
X}
X
Xstrmatch(s1, s2)
Xchar *s1, *s2;
X{
X	while(*s1) {
X		if(*s1!=*s2 &&
X		   ((islower(*s1) && toupper(*s1)!=*s2) ||
X		    (islower(*s2) && *s1!=toupper(*s2)) )
X		  ) {
X			return 0;
X		}
X		s1++;
X		s2++;
X	}
X	return !*s2;
X}
X
Xcompare(s1, s2)
XUBYTE *s1, *s2;
X{
X	UBYTE c1, c2;
X
X	while(*s1 && *s2) {
X		c1 = *s1;
X		c2 = *s2;
X		if(isalpha(c1)) {
X			if(isalpha(c2)) {
X				if(isupper(c1))
X					c1 = tolower(c1);
X				if(isupper(c2))
X					c2 = tolower(c2);
X			} else
X				return 1;
X		} else if(isalpha(c2))
X			return -1;
X		if(c1 > c2)
X			return 1;
X		if(c1 < c2)
X			return -1;
X		s1++;
X		s2++;
X	}
X	if(*s1)
X		return 1;
X	if(*s2)
X		return -1;
X	return 0;
X}
//END
echo x - PatMatch.c
sed 's/^X//' > PatMatch.c << '//END'
X/* PatMatch.c - Implements AmigaDos Regular Expression Pattern Matching.
X**
X**  This program will test whether a string is an AmigaDos  regular expression
X**  It may be used to implement wild expressions such as:
X**
X**    "copy #?.c to <dir>" to copy any file ending in .c
X**
X**  The program has two entry points: CmplPat, and Match.
X**
X**    CmplPat - takes a pattern and returns an auxilliary integer vector
X**              which is used by Match.  The pattern is not modified in
X**              any way.  CmplPat returns 1 if no errors were detected
X**              while compiling the pattern; otherwise it returns 0;
X**
X**    Match   - takes the pattern, the auxilliary vector, and the string
X**              to be matched.  It returns 1 if the string matches the
X**              pattern; otherwise it returns 0;
X**
X**  Translated from BCPL by:
X**              Jeff Lydiatt
X**              Richmond B.C. Canada
X**              16 May 1986.
X**
X**  Source: "A Compact Function for Regular Expression Pattern Matching",
X**           Software - Practice and Experience, September 1979.
X**
X**  Useage:
X**     To test if "file.c" matches the regular expression "#?.c"
X**     char *Pat = "#?.c";
X**     char *Str = "file.c";
X**     WORD Aux[128];
X**
X**     if ( CmplPat( Pat, Aux ) == 0 )
X**        {
X**           printf("Bad Wildcard Expression\n");
X**           exit(1);
X**        }
X**     if ( Match( Pat, Aux, Str ) == 1 )
X**        printf("String matches the pattern\n");
X**     else
X**        printf("String does NOT match the pattern\n");
X**/
X
X/*--- Included files ----*/
X
X#include <stdio.h>
X#include <exec/types.h>
X#include <ctype.h>
X
X#define  EOS '\0'
X
X/*--- Global Variables  ---*/
X
Xstatic char     Ch;      /* The current character in Pattern */
Xstatic char     *Pat;    /* Pointer to the Pattern */
Xstatic int      *Aux;    /* Pointer to returned auxilliary vector */
Xstatic int      PatP;    /* Current position in Pat */
Xstatic int      Patlen;  /* strlen(pat) */
Xstatic BOOL     Errflag; /* TRUE if error */
Xstatic int      *Work;   /* Pointer to Active work area */
Xstatic int      Wp;      /* Current position in work */
Xstatic BOOL     Succflag;/* True if "str" matches "pat" */
X
X/*----------------------------------------------------------------*/
X/*                   The Interpreter                              */
X/*----------------------------------------------------------------*/
X
Xstatic void Put(N)
Xint N;
X{
X   register int *ip;
X   register int *to;
X
X   if ( N == 0 )
X      Succflag = TRUE;
X   else
X      {
X	for ( ip = &Work[ 1 ], to = &Work[ Wp ]; ip <= to; ip++)
X	   if ( *ip == N )
X	      return;
X	Work[ ++Wp ] = N;
X      }
X}
X
Xint Match( Pat, Aux, Str )
Xchar Pat[];
Xint  Aux[];
Xchar Str[];
X{
X   int W[ 128 ];
X   int  S = 0;
X   int  I, N, Q, P, Strlength;
X   char K;
X   int  strlen();
X   void Put();
X
X   Work = W;
X   Wp = 0;
X   Succflag = FALSE;
X   Strlength = strlen( Str );
X   Put( 1 );
X
X   if ( Aux[ 0 ] != 0 )
X      Put( Aux[ 0 ] );
X
X   for(;;)
X      {
X        /* First complete the closure */
X        for( N=1; N <= Wp; N++ )
X          {
X	     P = Work[ N ];
X	     K = Pat[ P-1 ];
X	     Q = Aux[ P ];
X	     switch( K )
X	   	{
X		  case '#': Put( P + 1 );
X		  case '%': Put( Q );
X		  default : break;
X		  case '(':
X		  case '|': Put( P + 1);
X			    if ( Q != 0 )
X			       Put( Q );
X		}
X	   }
X
X	if ( S >= Strlength )
X	   return Succflag;
X	if ( Wp == 0 )
X	   return FALSE;
X	Ch = Str[ S++ ];
X
X	/* Now deal with the match items */
X
X	N = Wp;
X	Wp = 0;
X	Succflag = FALSE;
X
X	for ( I = 1; I <= N; I++)
X	  {
X	     P = Work[ I ];
X	     K = Pat[ P - 1 ];
X	     switch( K )
X	       {
X		 case '#':
X		 case '|':
X		 case '%':
X		 case '(': break;
X		 case '\'': K = Pat[ P ];
X		 default : if ( _toupper( Ch ) != _toupper( K ) )
X			      break;
X		 case '?': /* Successful match */
X		 	   Put ( Aux[ P ] );
X		} /* End Switch */
X	  } /* End For I */
X     } /* End for(;;) */
X}
X
X
X/*----------------------------------------------------------------*/
X/*                     The Compiler                               */
X/*----------------------------------------------------------------*/
X
Xstatic void  Rch() /* Read next character from Pat */
X{
X   if ( PatP >= Patlen )
X      Ch = EOS;
X   else
X      Ch = Pat[ PatP++ ];
X}
X
Xstatic void Nextitem() /* Get next char from Pat; recognize the ' escape char */
X{
X   if ( Ch == '\'' )
X      Rch();
X   Rch();
X}
X
Xstatic void Setexits( List, Val )
Xint List;
Xint Val;
X{
X   int A;
X
X   do
X     {
X	A = Aux[ List ];
X	Aux[ List ] = Val;
X	List = A;
X     }
X	while ( List != 0 );
X}
X
Xstatic int Join( A, B )
Xint A, B;
X{
X    int T = A;
X
X    if ( A == 0 )
X	return B;
X    while ( Aux[ A ] != 0 )
X	A = Aux[ A ];
X    Aux[ A ] = B;
X    return T;
X}
X
Xstatic int Prim()      /* Parse a Prim symbol */
X{
X   int   A = PatP;
X   char Op = Ch;
X   int  Exp();
X   void Setexits(), Nextitem();
X
X   Nextitem();
X   switch( Op )
X     {
X        case EOS:
X        case ')':
X        case '|': Errflag = TRUE;
X        default : return A;
X        case '#': Setexits( Prim(), A ); return A;
X        case '(': A = Exp( A );
X		  if ( Ch != ')' )
X		    {
X			Errflag = TRUE;
X		    }
X		  Nextitem();
X		  return A;
X     }
X}
X
Xstatic int Exp( AltP )    /* Parse an expression */
Xint AltP;
X{
X   int Exits = 0;
X   int A;
X   int Prim(), Exits(), Join();
X   void Nextitem(), Setexits();
X
X   for (;;)
X	{
X	   A = Prim();
X	   if ( Ch == '|' || Ch == ')' || Ch == EOS )
X	      {
X		Exits = Join( Exits, A );
X		if ( Ch != '|' )
X		   return Exits;
X		Aux[ AltP ] = PatP;
X		AltP = PatP;
X		Nextitem();
X	      }
X	   else
X	      Setexits( A, PatP );
X	}
X}
X
Xint CmplPat( Pattern, CmplPattern)
Xchar Pattern[];
Xint  CmplPattern[];
X{
X   int i, strlen();
X   void Rch(), Setexits();
X
X   Pat = Pattern;
X   Aux = CmplPattern;
X   PatP = 0;
X   Patlen = strlen( Pat );
X   Errflag = FALSE;
X
X   for ( i = 0; i <= Patlen; i++ )
X      Aux[ i ] = 0;
X   Rch();
X   Setexits( Exp(0), 0 );
X   return (!Errflag);
X}
//END
echo x - Makefile
sed 's/^X//' > Makefile << '//END'
XOFILES= pr.o instant.o PatMatch.o VolList.o menu.o
XCFILES= pr.c instant.c PatMatch.c VolList.c menu.c shar.c catalog.c
XTEXT= Makefile readme
XOTHERS= $(TEXT) pr
X
X.SUFFIXES: .c .o
X
X.c.o:
X	@-ram:c/del $*.o
X	cc +P -S -B -DAMIGA $*.c
X
Xall: pr
X
Xpr: $(OFILES)
X	@-ram:c/del pr
X	ln -o pr $(OFILES) -lcl32
X
Xinstant.arc: $(CFILES) $(OTHERS)
X	@-ram:c/del instant.arc
X	arc a instant $(CFILES) $(OTHERS)
X
Xcatalog: catalog.o PatMatch.o
X	@-ram:c/del catalog
X	ln -o catalog catalog.o PatMatch.o -lcl32
X
Xcatalog.arc: catalog.c PatMatch.c catalog catalog.man Makefile
X	@-ram:c/del catalog.arc
X	arc a catalog catalog catalog.c PatMatch.c Makefile catalog.man
X
Xshar: shar.o
X	@-ram:c/del shar
X	ln -o shar shar.o -lcl32
X
Xinstant.shar: shar $(CFILES) $(TEXT)
X	@-ram:c/del instant.shar
X	shar >instant.shar $(CFILES) $(TEXT)
X
Xcatalog.shar: shar catalog.c PatMatch.c catalog.man Makefile
X	@-ram:c/del catalog.shar
X	shar >catalog.shar catalog.man catalog.c PatMatch.c Makefile
X
Xprint: $(CFILES) $(TEXT)
X	pr $(CFILES) $(TEXT)
//END
: end of archive.
exit 0


-- 
-- Peter da Silva `-_-' ...!hoptoad!academ!uhnix1!sugar!peter
--                 'U`      ^^^^^^^^^^^^^^ Not seismo!soma (blush)