ahh@j.cc.purdue.edu (Brent L. Woods) (02/28/88)
Program Name: find Submitted By: munnari!bhpese.oz.au!rodney@uunet.uu.net (Rodney Lewis) Summary: A file-finding program (like the UNIX program of the same name). Poster Boy: Brent Woods (ahh@j.cc.purdue.edu) Tested. Part 1 of 1 NOTES: Works as advertised. The documentation is in the posting in comp.binaries.amiga (and is rather important, too). Brent Woods, Co-Moderator, comp.{sources,binaries}.amiga USENET: ...!j.cc.purdue.edu!ahh ARPANET: ahh@j.cc.purdue.edu BITNET: PODUM@PURCCVM PHONE: +1 (317) 743-8421 USNAIL: 320 Brown St., #406 / West Lafayette, IN 47906 ================================================================ This is a "find" program for the Amiga. This is the source and makefile, the uuencoded binary and doc files are in another posting. Rodney Lewis ACSnet: rodney@bhpese.oz # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # README # find.c # makefile # This archive created: Wed Jan 27 08:12:12 1988 echo shar: extracting README sed 's/^XX//' << \SHAR_EOF > README XX XX find - Version 1.0 - a program to find files based on a XX set of various criterion. (see doc file for usage). XX XX It compiles under Manx 3.4b but will also compile under XX 3.4a which will give a few warning messages that can be XX ignored. XX XX Any bug reports, suggestions to: XX XX ACSnet: rodney@bhpese.oz SHAR_EOF if test 299 -ne "`wc -c README`" then echo shar: error transmitting README '(should have been 299 characters)' fi echo shar: extracting find.c sed 's/^XX//' << \SHAR_EOF > find.c XX/**********************************************************************/ XX/* */ XX/* find - Version 1.0 */ XX/* */ XX/* Copyright (c) 1988 - Rodney Lewis */ XX/* This program is freely copyable and distributable. All copyrights */ XX/* are reserved by the author. You may give copies of this program to */ XX/* anyone you wish but you may not sell it. */ XX/* */ XX/* Pattern matching routine taken from Matt Dillon's csh program; */ XX/* reproduced by permission. */ XX/* */ XX/**********************************************************************/ XX XX/**********************************************************************/ XX/* */ XX/* find - searches the directory hierachy looking for files that */ XX/* match a given boolean expression. Based on the U**X */ XX/* find command. */ XX/* */ XX/**********************************************************************/ XX XX#include <exec/types.h> XX#include <exec/memory.h> XX#include <libraries/dosextens.h> XX#include <stdio.h> XX#include <functions.h> XX XX#define MAXARGS 20 XX#define NULL_PRIM (struct primary *) NULL XX#define EQ(x,y) (strcmp(x, y) == 0) XX#define PROTECTION (FIBF_READ | FIBF_WRITE | FIBF_EXECUTE | FIBF_DELETE) XX XX/* boolean expression node structure */ XX XXstruct node { XX unsigned long type; XX struct node *first; XX struct node *second; XX}; XX XX/* Node types - must be different from primary node types (below) */ XX XX#define OR 0x000000ff XX#define AND 0x0000ff00 XX#define NOT 0x00ff0000 XX XX/* structure to hold interpreted primary information */ XX XXstruct primary { XX unsigned long type; XX unsigned long size; XX char *data[MAXARGS]; XX}; XX XX/* start of compiled expression tree */ XX XXstruct node *node_head; XX XX/* Primary types */ XX XX#define PRINT 0x00000001 XX#define NAME 0x00000002 XX#define SIZE 0x00000004 XX#define TYPE 0x00000008 XX#define EXEC 0x00000010 XX#define NEWER 0x00000020 XX#define MTIME 0x00000040 XX#define PERM 0x00000080 XX#define PRIMS 0x0000ffff XX XX/* type qualifiers */ XX XX#define DIRECT 0x00010000 /* directory for -type */ XX#define PLAIN 0x00020000 /* plain file for -type */ XX#define PROMPT 0x00040000 /* prompt for EXEC */ XX#define LT 0x00080000 /* greater than */ XX#define GT 0x00100000 /* less than */ XX#define CHAR 0x00200000 /* use characters in -size check */ XX#define QUALS 0xffff0000 XX XXint breakflag = FALSE; XX XXchar path[80] = ""; /* memory to hold full path name */ XXstruct DateStamp date; XX XX/* manx releases the memory allocated by calloc when you call exit() */ XX XXextern char *calloc(); XX XXmain(argc, argv) XXint argc; XXchar *argv[]; XX{ XX register struct FileLock *start; XX register i; XX extern struct node *compile(); XX XX DateStamp(&date); XX XX /* must be at least three arguments */ XX XX if (argc < 3) { XX fprintf(stderr, "Usage: find <path-list> <expression>\n"); XX exit(1); XX } XX XX /* find the start of the boolean expression */ XX XX for (i = 1 ; argv[i][0] != '-' && argv[i][0] != '!' && argv[i][0] != '(' ; i++); XX if (i == 1) { XX /* no path name list */ XX fprintf(stderr, "Usage: find <path-list> <expression>\n"); XX exit(1); XX } XX XX /* compile the boolean expression */ XX XX if (node_head = compile(argc - i, &argv[i])) { XX XX /* search each path-name specified */ XX XX for (i = 1 ; argv[i][0] != '-' && argv[i][0] != '!' && argv[i][0] != '(' ; ++i) { XX start = Lock(argv[i], ACCESS_READ); XX if (start == NULL) { XX fprintf(stderr, "can't access '%s'\n", argv[i]); XX continue; XX } XX XX search(start); XX UnLock(start); XX } XX } XX XX exit(0); XX} XX XX/* search the given directory and for each file XX * execute the boolean expression. XX */ XX XXsearch(lock) XXregister struct FileLock *lock; XX{ XX register struct FileInfoBlock *fib; XX register struct FileLock *nlock; XX char *prev, file[80]; XX XX fib = (struct FileInfoBlock *) AllocMem((long) sizeof(struct FileInfoBlock), MEMF_CLEAR); XX if (fib == NULL) { XX fprintf(stderr, "can't allocate file info block\n"); XX return(0); XX } XX XX /* save current position in full path name */ XX XX prev = path + strlen(path); XX XX if (*path == '\0' && pwd(ParentDir(lock)) == 0) { XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX return(0); XX } XX XX /* examine initial path name */ XX XX if (Examine(lock, fib)) { XX XX /* execute the expression on the inital path */ XX XX execute(node_head, fib); XX XX if (fib->fib_DirEntryType > 0) { XX XX /* set up printable path name */ XX XX if (*path) { XX strcat(path, fib->fib_FileName); XX strcat(path, "/"); XX } XX else if (pwd(lock) == 0) { XX *prev = '\0'; XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX return(0); XX } XX } XX XX else { XX XX /* if initial path name is not a directory then we just return */ XX XX *prev = '\0'; XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX return(0); XX } XX XX /* examine directory contents */ XX XX while(ExNext(lock, fib)) { XX if (breakflag) break; XX XX /* recurse if we have found a directory */ XX XX if (fib->fib_DirEntryType > 0) { XX strcpy(file, path); XX strcat(file, fib->fib_FileName); XX nlock = Lock(file, ACCESS_READ); XX if (nlock == NULL) XX fprintf(stderr, "locking error - %s\n", file); XX else { XX search(nlock); XX UnLock(nlock); XX } XX } XX else XX execute(node_head, fib); XX XX if (SetSignal(0L, 0L) & SIGBREAKF_CTRL_C) { XX breakflag = TRUE; XX break; XX } XX } XX } XX XX *prev = '\0'; XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX} XX XX/* execute the boolean expression on the given file */ XX XXexecute(cnode, fib) XXstruct node *cnode; XXstruct FileInfoBlock *fib; XX{ XX register struct primary *prim; XX register long checksize; XX register j; XX register struct DateStamp *ds; XX char file[80], ok[10]; XX char *av[MAXARGS]; XX XX /* check node type */ XX XX if (cnode->type == AND) XX if (execute(cnode->first, fib)) XX return(execute(cnode->second, fib)); XX else XX return(0); XX XX else if (cnode->type == OR) XX if (execute(cnode->first, fib)) XX return(1); XX else XX return(execute(cnode->second, fib)); XX XX else if (cnode->type == NOT) XX return(!execute(cnode->first, fib)); XX XX else { XX XX /* we have an actual primary */ XX XX prim = (struct primary *) cnode; XX switch (prim->type & PRIMS) { XX XX case PRINT: XX XX if (*path) XX printf("%s%s\n", path, fib->fib_FileName); XX else XX printf("%s:\n", fib->fib_FileName); XX return(1); XX XX case NAME: XX XX if (compare_ok(prim->data[0], fib->fib_FileName)) XX return(1); XX else XX return(0); XX XX case SIZE: XX XX if (prim->type & CHAR) XX checksize = fib->fib_Size; XX else XX checksize = fib->fib_NumBlocks; XX XX if (((prim->type & GT) && (checksize > prim->size) ) || XX ((prim->type & LT) && (checksize < prim->size) ) || XX ((prim->type & (GT | LT)) == 0 && (checksize == prim->size))) XX return(1); XX else XX return(0); XX XX case TYPE: XX XX switch (prim->type & QUALS | (fib->fib_DirEntryType > 0)) { XX XX case DIRECT: XX case (PLAIN | 1): XX return(0); XX XX default: XX return(1); XX } XX XX case EXEC: XX XX for (j = 0 ; prim->data[j] ; j++) XX if (EQ("{}", prim->data[j])) { XX strcpy(file, path); XX strcat(file, fib->fib_FileName); XX av[j] = file; XX } XX else XX av[j] = prim->data[j]; XX av[j] = NULL; XX XX if (!(prim->type & PROMPT) || (pr_cmd(av) && gets(ok) && XX ((ok[0] == 'y') || (ok[0] == 'Y')))) { XX if (fexecv(av[0], av) == -1) XX return(0); XX else if (wait()) XX return(0); XX else XX return(1); XX } XX XX case NEWER: XX XX ds = (struct DateStamp *) prim->data[0]; XX if (fib->fib_Date.ds_Days > ds->ds_Days) XX return(1); XX else if (fib->fib_Date.ds_Days == ds->ds_Days && XX fib->fib_Date.ds_Minute > ds->ds_Minute) XX return(1); XX else if (fib->fib_Date.ds_Days == ds->ds_Days && XX fib->fib_Date.ds_Minute == ds->ds_Minute && XX fib->fib_Date.ds_Tick > ds->ds_Tick) XX return(1); XX else XX return(0); XX XX case MTIME: XX XX checksize = date.ds_Days - fib->fib_Date.ds_Days; XX XX if (((prim->type & GT) && (checksize > prim->size) ) || XX ((prim->type & LT) && (checksize < prim->size) ) || XX ((prim->type & (GT | LT)) == 0 && (checksize == prim->size))) XX return(1); XX else XX return(0); XX XX case PERM: XX XX if ((fib->fib_Protection & PROTECTION) == prim->size) XX return(1); XX else XX return(0); XX XX } XX return(0); XX } XX} XX XX/* print the command to be executed on the screen */ XX XXpr_cmd(av) XXchar *av[]; XX{ XX register j; XX XX printf("< "); XX for (j = 0 ; av[j] ; j++) printf("%s ", av[j]); XX printf("> ? "); XX return(1); XX} XX XX/* compile the boolean expression: returns a pointer to the start XX * of the compiled expression tree, or NULL if a failure occurs. XX */ XX XXstruct node * XXcompile(argc, argv) XXint argc; XXchar *argv[]; XX{ XX register i, j, scan; XX register struct primary *prim; XX register struct node *node_head = (struct node *) NULL, *tmp_node; XX XX for(i = 0 ; i < argc ; i++) { XX XX prim = (struct primary *) calloc(1, sizeof(struct primary)); XX if (prim == NULL_PRIM) { XX fprintf(stderr, "out memory in primary interpretation\n"); XX exit(5); XX } XX XX XX if (EQ("-o", argv[i])) { XX free(prim); XX XX /* -o cannot be the first argument */ XX XX if (node_head == NULL_PRIM) { XX fprintf(stderr, "misplaced 'or' operator ... ignored\n"); XX continue; XX } XX XX else { XX XX /* create OR node */ XX XX tmp_node = (struct node *) calloc(1, sizeof(struct node)); XX if (tmp_node == NULL) { XX fprintf(stderr, "out of memory in expression compilation"); XX exit(5); XX } XX tmp_node->type = OR; XX tmp_node->first = node_head; XX XX /* compile rest of expression and attach it to OR node */ XX XX if ((tmp_node->second = compile(argc - i - 1, argv + i + 1)) == NULL) { XX free(tmp_node); XX return(node_head); XX } XX else XX return(tmp_node); XX } XX } XX XX else if (EQ("(", argv[i])) { XX free(prim); XX XX /* scan to matching brackets */ XX XX for (j = 0, scan = 0 ; ++i < argc && (!EQ(")", argv[i]) || scan != 0) ; j++) { XX if (EQ("(", argv[i])) scan++; XX if (EQ(")", argv[i])) scan--; XX } XX XX if (i >= argc) { XX fprintf(stderr, "unmatched bracket\n"); XX exit(5); XX } XX XX if (j == 0) { XX fprintf(stderr, "empty brackets ... ignored\n"); XX continue; XX } XX XX /* compile what is in the brackets */ XX XX if ((prim = (struct primary *) compile(j, argv + i - j)) == NULL) XX continue; XX } XX XX else if (EQ("!", argv[i])) { XX if (++i >= argc) { XX fprintf(stderr, "trailing '!' ignored\n"); XX continue; XX } XX if (EQ("-o", argv[i])) { XX fprintf(stderr, "illegal 'or' operator placement\n"); XX exit(5); XX } XX XX tmp_node = (struct node *) calloc(1, sizeof(struct node)); XX if (tmp_node == NULL) { XX fprintf(stderr, "out of memory in expression compilation\n"); XX exit(5); XX } XX tmp_node->type = NOT; XX XX if (EQ("(", argv[i])) { XX XX /* scan to matching bracket */ XX XX for (j = 0, scan = 0 ; ++i < argc && (!EQ(")", argv[i]) || scan != 0) ; j++) { XX if (EQ("(", argv[i])) scan++; XX if (EQ(")", argv[i])) scan--; XX } XX XX if (i >= argc) { XX fprintf(stderr, "unmatched bracket\n"); XX exit(5); XX } XX XX if (j == 0) { XX fprintf(stderr, "empty brackets ... ignored\n"); XX free(tmp_node); XX continue; XX } XX XX /* compile what is in the brackets */ XX XX if ((tmp_node->first = compile(j, argv + i - j)) == NULL) XX continue; XX } XX else { XX tmp_node->first = (struct node *) prim; XX i += interpret(prim, argc - i, argv + i); XX } XX prim = (struct primary *) tmp_node; XX } XX XX else XX i += interpret(prim, argc - i, argv + i); XX XX /* attach interpreted primary to expression tree */ XX XX if (node_head == NULL) XX node_head = (struct node *) prim; XX else { XX tmp_node = (struct node *) calloc(1, sizeof(struct node)); XX if (tmp_node == NULL) { XX fprintf(stderr, "out of memory in expression compilation\n"); XX exit(5); XX } XX tmp_node->type = AND; XX tmp_node->first = node_head; XX tmp_node->second = (struct node *) prim; XX node_head = tmp_node; XX } XX } XX XX return(node_head); XX} XX XX/* interpret a primary */ XX XXinterpret(prim, argc, argv) XXstruct primary *prim; XXchar *argv[]; XX{ XX register i, j; XX register struct FileLock *lock; XX register struct FileInfoBlock *fib; XX register struct DateStamp *ds; XX char *numstr; XX extern unsigned long atol(); XX XX for (i = 0 ; i < argc ; i++) { XX XX if (EQ("-print", argv[i])) XX prim->type = PRINT; XX XX else if (EQ("-name", argv[i])) { XX prim->type = NAME; XX prim->data[0] = argv[++i]; XX } XX XX else if (EQ("-size", argv[i])) { XX prim->type = SIZE; XX XX /* get required size */ XX XX numstr = argv[++i]; XX XX if (*numstr == '+') { XX prim->type |= GT; XX numstr++; XX } XX XX else if (*numstr == '-') { XX prim->type |= LT; XX numstr++; XX } XX XX if (numstr[strlen(numstr) - 1] == 'c') { XX prim->type |= CHAR; XX numstr[strlen(numstr) - 1] == '\0'; XX } XX XX prim->size = atol(numstr); XX } XX XX else if (EQ("-type", argv[i])) { XX prim->type = TYPE; XX if (EQ(argv[++i], "d")) XX prim->type |= DIRECT; XX else if (EQ(argv[i], "f")) XX prim->type |= PLAIN; XX else { XX fprintf(stderr, "illegal file type specified\n"); XX exit(5); XX } XX } XX XX else if (EQ("-exec", argv[i])) { XX prim->type = EXEC; XX XX /* scan to ending ';', saving pointers to arguments */ XX XX for (j=0 ; (j<MAXARGS) && (++i < argc) && !EQ(";",argv[i]) ; j++) XX prim->data[j] = argv[i]; XX XX if (i >= argc) { XX fprintf(stderr, "no ending ';' on command\n"); XX exit(5); XX } XX XX else if (j >= MAXARGS) { XX fprintf(stderr, "command too long\n"); XX exit(5); XX } XX XX else XX argv[j] = NULL; XX } XX XX else if (EQ("-ok", argv[i])) { XX prim->type = EXEC | PROMPT; XX XX /* scan to ending ';', saving pointers to arguments */ XX XX for (j=0 ; (j<MAXARGS) && (++i < argc) && !EQ(";",argv[i]) ; j++) XX prim->data[j] = argv[i]; XX XX if (i >= argc) { XX fprintf(stderr, "no ending ';' on command\n"); XX exit(5); XX } XX XX else if (j >= MAXARGS) { XX fprintf(stderr, "command too long\n"); XX exit(5); XX } XX XX else XX argv[j] = NULL; XX } XX XX else if (EQ("-newer", argv[i])) { XX prim->type = NEWER; XX XX if (lock = Lock(argv[++i])) { XX fib = (struct FileInfoBlock *) AllocMem((long) sizeof(struct FileInfoBlock), MEMF_CLEAR); XX if (fib == NULL) { XX fprintf(stderr, "no mem for -newer test\n"); XX UnLock(lock); XX exit(5); XX } XX XX if (Examine(lock, fib) == 0) { XX fprintf(stderr, "could not examine %s\n", argv[i]); XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX UnLock(lock); XX exit(5); XX } XX XX /* save date stamp of given file */ XX XX ds = (struct DateStamp *) calloc(1, sizeof(struct DateStamp)); XX if (ds == NULL) { XX fprintf(stderr, "no mem for DateStamp on %s\n", argv[i]); XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX UnLock(lock); XX exit(5); XX } XX XX prim->data[0] = (char *) ds; XX *ds = fib->fib_Date; XX XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX UnLock(lock); XX } XX XX else { XX fprintf(stderr, "unable to access %s\n", argv[i]); XX exit(5); XX } XX } XX XX else if (EQ("-mtime", argv[i])) { XX prim->type = MTIME; XX XX /* get required number of days */ XX XX numstr = argv[++i]; XX XX if (*numstr == '+') { XX prim->type |= GT; XX numstr++; XX } XX else if (*numstr == '-') { XX prim->type |= LT; XX numstr++; XX } XX XX prim->size = atol(numstr); XX } XX XX else if (EQ("-perm", argv[i])) { XX prim->type = PERM; XX prim->size = PROTECTION; XX XX /* assemble desired protection bits */ XX XX for(i++, j = 0 ; argv[i][j] ; j++) { XX switch(argv[i][j]) { XX XX case 'n': XX prim->size = PROTECTION; XX return(i); XX XX case 'r': XX prim->size &= ~FIBF_READ; XX break; XX XX case 'w': XX prim->size &= ~FIBF_WRITE; XX break; XX XX case 'e': XX prim->size &= ~FIBF_EXECUTE; XX break; XX XX case 'd': XX prim->size &= ~FIBF_DELETE; XX break; XX XX default: XX fprintf(stderr, "unknown code '%c' ... ignored\n", argv[i][j]); XX break; XX } XX } XX } XX XX else { XX fprintf(stderr, "unknown primary: %s\n", argv[i]); XX exit(5); XX } XX XX return(i); XX } XX} XX XX/* find the full path name of the given lock */ XX XXpwd(dir) XXregister struct FileLock *dir; XX{ XX register struct FileLock *par; XX register struct FileInfoBlock *fib; XX XX if (dir == NULL) { XX *path = '\0'; XX return(1); XX } XX XX fib = (struct FileInfoBlock *) AllocMem((long) sizeof(struct FileInfoBlock), MEMF_CLEAR); XX if (fib == NULL) { XX fprintf(stderr, "pwd: can't allocate a FileInfoBlock\n"); XX return(0); XX } XX XX if (!Examine(dir, fib)) { XX fprintf(stderr, "pwd: examine failed\n"); XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX return(0); XX } XX XX if (par = ParentDir(dir)) { XX XX /* find full path name of parent */ XX XX if (pwd(par) == 0) { XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX return(0); XX } XX XX /* add current name */ XX XX strcat(path, fib->fib_FileName); XX strcat(path, "/"); XX } XX else { XX XX /* found the root name */ XX XX strcpy(path, fib->fib_FileName); XX strcat(path, ":"); XX } XX XX FreeMem(fib, (long) sizeof(struct FileInfoBlock)); XX return(1); XX} XX XX/* XX * Compare a wild card name with a normal name. XX * Taken from Matt Dillon's csh program. XX */ XX XX#define MAXB 8 XX XXcompare_ok(wild, name) XXchar *wild, *name; XX{ XX register char *w = wild; XX register char *n = name; XX char *back[MAXB][2]; XX register char s1, s2; XX int bi = 0; XX XX while (*n || *w) { XX switch (*w) { XX case '*': XX if (bi == MAXB) { XX fprintf(stderr,"Too many levels of '*'\n"); XX return (0); XX } XX back[bi][0] = w; XX back[bi][1] = n; XX ++bi; XX ++w; XX continue; XXgoback: XX --bi; XX while (bi >= 0 && *back[bi][1] == '\0') XX --bi; XX if (bi < 0) XX return (0); XX w = back[bi][0] + 1; XX n = ++back[bi][1]; XX ++bi; XX continue; XX case '?': XX if (!*n) { XX if (bi) XX goto goback; XX return (0); XX } XX break; XX default: XX s1 = (*n >= 'A' && *n <= 'Z') ? *n - 'A' + 'a' : *n; XX s2 = (*w >= 'A' && *w <= 'Z') ? *w - 'A' + 'a' : *w; XX if (s1 != s2) { XX if (bi) XX goto goback; XX return (0); XX } XX break; XX } XX if (*n) ++n; XX if (*w) ++w; XX } XX return (1); XX} SHAR_EOF if test 18071 -ne "`wc -c find.c`" then echo shar: error transmitting find.c '(should have been 18071 characters)' fi echo shar: extracting makefile sed 's/^XX//' << \SHAR_EOF > makefile XXOBJ = find.o XX XX.c.o: XX cc -E200 $*.c XX XXfind: Makefile $(OBJ) XX ln -o find $(OBJ) -lc XX XXfind.o: find.c XX XXshar: XX shar -a README find.c makefile > find.shar1 XX shar -a find.doc find.uue > find.shar2 SHAR_EOF if test 189 -ne "`wc -c makefile`" then echo shar: error transmitting makefile '(should have been 189 characters)' fi # End of shell archive exit 0