/**********************************************************************/ /* */ /* find - Amiga Version 1.2 */ /* */ /* Copyright (c) 1988,89 - Rodney Lewis */ /* */ /* This program is freely copyable and distributable. All copyrights */ /* are reserved by the author. You may give copies of this program to */ /* anyone you wish but you may not sell it. */ /* */ /* Pattern matching routine taken from Matt Dillon's csh program; */ /* reproduced by permission. */ /* */ /* Changes from Version 1.0: */ /* */ /* - dynamic allocation of file name space when recursing down */ /* the directory tree and when executing commands. */ /* */ /* - changed comparison to assignment on line 652 of find.c. */ /* Though this makes no difference to the program, (i.e the */ /* line doesn't really need to be there in the first place), */ /* it was incorrect code. Thanks to Hume Smith, Acadia U. */ /* */ /* - added support for the 1.3 hidden, script, pure and archive */ /* bits in "-perm" primary. */ /* */ /* - added extra primary "-prot" to test for indiviual protection */ /* bits. "-perm" tests only for an exact permissions match. This */ /* allows the you (for example) to search for all files that have */ /* not been archived using "! -prot a". */ /* */ /* - changed find so the path name used for each file is relative */ /* to the current directory; which is as it should be. In V1.0, */ /* the full path name of each file from the root of the device */ /* was always used. */ /* */ /**********************************************************************/ /**********************************************************************/ /* */ /* find - searches the directory hierachy looking for files that */ /* match a given boolean expression. Based on the U**X */ /* find command. */ /* */ /**********************************************************************/ #include #include #include #include #include /* define new 1.3 protection bits */ #ifndef FIBB_HIDDEN #define FIBB_HIDDEN 7L #define FIBB_SCRIPT 6L #define FIBB_PURE 5L #define FIBF_HIDDEN (1L << FIBB_HIDDEN) #define FIBF_SCRIPT (1L << FIBB_SCRIPT) #define FIBF_PURE (1L << FIBB_PURE) #endif #define MAXARGS 50 #define NULL_PRIM (struct primary *) NULL #define EQ(x,y) (strcmp(x, y) == 0) #define PROTECTION (FIBF_READ | FIBF_WRITE | FIBF_EXECUTE | FIBF_DELETE) #define STATUS (FIBF_HIDDEN | FIBF_SCRIPT | FIBF_PURE | FIBF_ARCHIVE | PROTECTION) /* boolean expression node structure */ struct node { unsigned long type; struct node *first; struct node *second; }; /* Node types - must be different from primary node types (below) */ #define OR 0x000000ff #define AND 0x0000ff00 #define NOT 0x00ff0000 /* structure to hold interpreted primary information */ struct primary { unsigned long type; unsigned long size; char *data[MAXARGS]; }; /* start of compiled expression tree */ struct node *node_head; /* Primary types */ #define PRINT 0x00000001 #define NAME 0x00000002 #define SIZE 0x00000004 #define TYPE 0x00000008 #define EXEC 0x00000010 #define NEWER 0x00000020 #define MTIME 0x00000040 #define PERM 0x00000080 #define PROT 0x00000100 #define PRIMS 0x0000ffff /* type qualifiers */ #define DIRECT 0x00010000 /* directory for -type */ #define PLAIN 0x00020000 /* plain file for -type */ #define PROMPT 0x00040000 /* prompt for EXEC */ #define LT 0x00080000 /* greater than */ #define GT 0x00100000 /* less than */ #define CHAR 0x00200000 /* use characters in -size check */ #define QUALS 0xffff0000 int breakflag = FALSE; char *path = NULL; /* memory to hold full path name */ int p_alloc = 500; struct DateStamp date; /* manx releases the memory allocated by calloc when you call exit() */ extern char *calloc(); extern char *malloc(); main(argc, argv) int argc; char *argv[]; { register struct FileLock *start; register i; extern struct node *compile(); DateStamp(&date); /* must be at least three arguments */ if (argc < 3) { fprintf(stderr, "Usage: find \n"); exit(1); } /* find the start of the boolean expression */ for (i = 1 ; argv[i][0] != '-' && argv[i][0] != '!' && argv[i][0] != '(' ; i++); if (i == 1) { /* no path name list */ fprintf(stderr, "Usage: find \n"); exit(1); } /* compile the boolean expression */ if (node_head = compile(argc - i, &argv[i])) { /* search each path-name specified */ for (i = 1 ; argv[i][0] != '-' && argv[i][0] != '!' && argv[i][0] != '(' ; ++i) { start = Lock(argv[i], ACCESS_READ); if (start == NULL) { fprintf(stderr, "can't access '%s'\n", argv[i]); continue; } search(start, argv[i]); UnLock(start); if (path) { free(path); path = (char *) NULL; p_alloc = 500; } } } exit(0); } /* search the given directory and for each file * execute the boolean expression. */ search(lock, name) register struct FileLock *lock; register char *name; { register struct FileInfoBlock *fib; register struct FileLock *nlock; char *prev = NULL, *file; int ret; fib = (struct FileInfoBlock *) AllocMem((long) sizeof(struct FileInfoBlock), MEMF_CLEAR); if (fib == NULL) { fprintf(stderr, "can't allocate file info block\n"); return(0); } /* save current position in full path name */ if (path) prev = path + strlen(path); /* examine initial path name */ if (Examine(lock, fib)) { /* execute the expression on the inital path */ execute(node_head, fib, name); if (name) ret = add_pname(name); else ret = add_pname(fib->fib_FileName); if (ret == 0) { fprintf(stderr, "out of memory\n"); FreeMem(fib, (long) sizeof(struct FileInfoBlock)); return(0); } if (fib->fib_DirEntryType <= 0) { /* if initial path name is not a directory then we just return */ if (prev) *prev = '\0'; FreeMem(fib, (long) sizeof(struct FileInfoBlock)); return(0); } /* examine directory contents */ while(ExNext(lock, fib)) { if (breakflag) break; /* recurse if we have found a directory */ if (fib->fib_DirEntryType > 0) { file = malloc(strlen(path) + strlen(fib->fib_FileName) + 1); if (file == NULL) { fprintf(stderr, "out of memory\n"); breakflag = TRUE; break; } strcpy(file, path); strcat(file, fib->fib_FileName); nlock = Lock(file, ACCESS_READ); if (nlock == NULL) fprintf(stderr, "locking error - %s\n", file); else { search(nlock, NULL); UnLock(nlock); } free(file); } else execute(node_head, fib, NULL); if (SetSignal(0L, 0L) & SIGBREAKF_CTRL_C) { breakflag = TRUE; break; } } } if (prev) *prev = '\0'; FreeMem(fib, (long) sizeof(struct FileInfoBlock)); } add_pname(name) register char *name; { register char *tmp; register char c; if (path == NULL) { path = calloc(1, p_alloc); if (path == NULL) return(0); } while (strlen(name) + strlen(path) + 1 > p_alloc) { p_alloc *= 2; tmp = calloc(1, p_alloc); if (tmp == NULL) return(0); strcpy(tmp, path); free(path); path = tmp; } if (strlen(path) && (c = path[strlen(path) - 1]) != ':' && c != '/') strcat(path, "/"); strcat(path, name); if (strlen(path) && (c = path[strlen(path) - 1]) != ':' && c != '/') strcat(path, "/"); return(1); } /* execute the boolean expression on the given file */ execute(cnode, fib, name) register struct node *cnode; register struct FileInfoBlock *fib; char *name; { register struct primary *prim; register long checksize; register j; register struct DateStamp *ds; char *file, ok[10]; char *av[MAXARGS + 1]; /* check node type */ if (cnode->type == AND) if (execute(cnode->first, fib, name)) return(execute(cnode->second, fib, name)); else return(0); else if (cnode->type == OR) if (execute(cnode->first, fib, name)) return(1); else return(execute(cnode->second, fib, name)); else if (cnode->type == NOT) return(!execute(cnode->first, fib, name)); else { /* we have an actual primary */ if (name == NULL) name = fib->fib_FileName; prim = (struct primary *) cnode; switch (prim->type & PRIMS) { case PRINT: if (*path) printf("%s%s\n", path, name); else printf("%s\n", name); return(1); case NAME: if (compare_ok(prim->data[0], name)) return(1); else return(0); case SIZE: if (prim->type & CHAR) checksize = fib->fib_Size; else checksize = fib->fib_NumBlocks; if (((prim->type & GT) && (checksize > prim->size) ) || ((prim->type & LT) && (checksize < prim->size) ) || ((prim->type & (GT | LT)) == 0 && (checksize == prim->size))) return(1); else return(0); case TYPE: switch (prim->type & QUALS | (fib->fib_DirEntryType > 0)) { case DIRECT: case (PLAIN | 1): return(0); default: return(1); } case EXEC: for (j = 0 ; prim->data[j] ; j++) if (EQ("{}", prim->data[j])) { file = malloc(strlen(path) + strlen(name) + 1); if (file == NULL) { fprintf(stderr, "out of memory\n"); return(0); } strcpy(file, path); strcat(file, name); av[j] = file; } else av[j] = prim->data[j]; av[j] = NULL; if (!(prim->type & PROMPT) || (pr_cmd(av) && gets(ok) && ((ok[0] == 'y') || (ok[0] == 'Y')))) { if (fexecv(av[0], av) == -1) return(0); else if (wait()) return(0); else return(1); } return(0); case NEWER: ds = (struct DateStamp *) prim->data[0]; if (fib->fib_Date.ds_Days > ds->ds_Days) return(1); else if (fib->fib_Date.ds_Days == ds->ds_Days && fib->fib_Date.ds_Minute > ds->ds_Minute) return(1); else if (fib->fib_Date.ds_Days == ds->ds_Days && fib->fib_Date.ds_Minute == ds->ds_Minute && fib->fib_Date.ds_Tick > ds->ds_Tick) return(1); else return(0); case MTIME: checksize = date.ds_Days - fib->fib_Date.ds_Days; if (((prim->type & GT) && (checksize > prim->size) ) || ((prim->type & LT) && (checksize < prim->size) ) || ((prim->type & (GT | LT)) == 0 && (checksize == prim->size))) return(1); else return(0); case PERM: if ((fib->fib_Protection & STATUS) == prim->size) return(1); else return(0); case PROT: if ((fib->fib_Protection & prim->size) == prim->size) return(1); else return(0); } return(0); } } /* print the command to be executed on the screen */ pr_cmd(av) char *av[]; { register j; printf("< "); for (j = 0 ; av[j] ; j++) printf("%s ", av[j]); printf("> ? "); return(1); } /* compile the boolean expression: returns a pointer to the start * of the compiled expression tree, or NULL if a failure occurs. */ struct node * compile(argc, argv) int argc; char *argv[]; { register i, j, scan; register struct primary *prim; register struct node *node_head = (struct node *) NULL, *tmp_node; for(i = 0 ; i < argc ; i++) { prim = (struct primary *) calloc(1, sizeof(struct primary)); if (prim == NULL_PRIM) { fprintf(stderr, "out memory in primary interpretation\n"); exit(5); } if (EQ("-o", argv[i])) { free(prim); /* -o cannot be the first argument */ if (node_head == NULL_PRIM) { fprintf(stderr, "misplaced 'or' operator ... ignored\n"); continue; } else { /* create OR node */ tmp_node = (struct node *) calloc(1, sizeof(struct node)); if (tmp_node == NULL) { fprintf(stderr, "out of memory in expression compilation"); exit(5); } tmp_node->type = OR; tmp_node->first = node_head; /* compile rest of expression and attach it to OR node */ if ((tmp_node->second = compile(argc - i - 1, argv + i + 1)) == NULL) { free(tmp_node); return(node_head); } else return(tmp_node); } } else if (EQ("(", argv[i])) { free(prim); /* scan to matching brackets */ for (j = 0, scan = 0 ; ++i < argc && (!EQ(")", argv[i]) || scan != 0) ; j++) { if (EQ("(", argv[i])) scan++; if (EQ(")", argv[i])) scan--; } if (i >= argc) { fprintf(stderr, "unmatched bracket\n"); exit(5); } if (j == 0) { fprintf(stderr, "empty brackets ... ignored\n"); continue; } /* compile what is in the brackets */ if ((prim = (struct primary *) compile(j, argv + i - j)) == NULL) continue; } else if (EQ("!", argv[i])) { if (++i >= argc) { fprintf(stderr, "trailing '!' ignored\n"); continue; } if (EQ("-o", argv[i])) { fprintf(stderr, "illegal 'or' operator placement\n"); exit(5); } tmp_node = (struct node *) calloc(1, sizeof(struct node)); if (tmp_node == NULL) { fprintf(stderr, "out of memory in expression compilation\n"); exit(5); } tmp_node->type = NOT; if (EQ("(", argv[i])) { /* scan to matching bracket */ for (j = 0, scan = 0 ; ++i < argc && (!EQ(")", argv[i]) || scan != 0) ; j++) { if (EQ("(", argv[i])) scan++; if (EQ(")", argv[i])) scan--; } if (i >= argc) { fprintf(stderr, "unmatched bracket\n"); exit(5); } if (j == 0) { fprintf(stderr, "empty brackets ... ignored\n"); free(tmp_node); continue; } /* compile what is in the brackets */ if ((tmp_node->first = compile(j, argv + i - j)) == NULL) continue; } else { tmp_node->first = (struct node *) prim; i += interpret(prim, argc - i, argv + i); } prim = (struct primary *) tmp_node; } else i += interpret(prim, argc - i, argv + i); /* attach interpreted primary to expression tree */ if (node_head == NULL) node_head = (struct node *) prim; else { tmp_node = (struct node *) calloc(1, sizeof(struct node)); if (tmp_node == NULL) { fprintf(stderr, "out of memory in expression compilation\n"); exit(5); } tmp_node->type = AND; tmp_node->first = node_head; tmp_node->second = (struct node *) prim; node_head = tmp_node; } } return(node_head); } /* interpret a primary */ interpret(prim, argc, argv) struct primary *prim; char *argv[]; { register i, j; register struct FileLock *lock; register struct FileInfoBlock *fib; register struct DateStamp *ds; char *numstr; extern unsigned long atol(); for (i = 0 ; i < argc ; i++) { if (EQ("-print", argv[i])) prim->type = PRINT; else if (EQ("-name", argv[i])) { prim->type = NAME; prim->data[0] = argv[++i]; } else if (EQ("-size", argv[i])) { prim->type = SIZE; /* get required size */ numstr = argv[++i]; if (*numstr == '+') { prim->type |= GT; numstr++; } else if (*numstr == '-') { prim->type |= LT; numstr++; } if (numstr[strlen(numstr) - 1] == 'c') { prim->type |= CHAR; numstr[strlen(numstr) - 1] = '\0'; } prim->size = atol(numstr); } else if (EQ("-type", argv[i])) { prim->type = TYPE; if (EQ(argv[++i], "d")) prim->type |= DIRECT; else if (EQ(argv[i], "f")) prim->type |= PLAIN; else { fprintf(stderr, "illegal file type specified\n"); exit(5); } } else if (EQ("-exec", argv[i])) { prim->type = EXEC; /* scan to ending ';', saving pointers to arguments */ for (j=0 ; (jdata[j] = argv[i]; if (i >= argc) { fprintf(stderr, "no ending ';' on command\n"); exit(5); } else if (j >= MAXARGS) { fprintf(stderr, "command too long\n"); exit(5); } else argv[j] = NULL; } else if (EQ("-ok", argv[i])) { prim->type = EXEC | PROMPT; /* scan to ending ';', saving pointers to arguments */ for (j=0 ; (jdata[j] = argv[i]; if (i >= argc) { fprintf(stderr, "no ending ';' on command\n"); exit(5); } else if (j >= MAXARGS) { fprintf(stderr, "command too long\n"); exit(5); } else argv[j] = NULL; } else if (EQ("-newer", argv[i])) { prim->type = NEWER; if (lock = Lock(argv[++i])) { fib = (struct FileInfoBlock *) AllocMem((long) sizeof(struct FileInfoBlock), MEMF_CLEAR); if (fib == NULL) { fprintf(stderr, "no mem for -newer test\n"); UnLock(lock); exit(5); } if (Examine(lock, fib) == 0) { fprintf(stderr, "could not examine %s\n", argv[i]); FreeMem(fib, (long) sizeof(struct FileInfoBlock)); UnLock(lock); exit(5); } /* save date stamp of given file */ ds = (struct DateStamp *) calloc(1, sizeof(struct DateStamp)); if (ds == NULL) { fprintf(stderr, "no mem for DateStamp on %s\n", argv[i]); FreeMem(fib, (long) sizeof(struct FileInfoBlock)); UnLock(lock); exit(5); } prim->data[0] = (char *) ds; *ds = fib->fib_Date; FreeMem(fib, (long) sizeof(struct FileInfoBlock)); UnLock(lock); } else { fprintf(stderr, "unable to access %s\n", argv[i]); exit(5); } } else if (EQ("-mtime", argv[i])) { prim->type = MTIME; /* get required number of days */ numstr = argv[++i]; if (*numstr == '+') { prim->type |= GT; numstr++; } else if (*numstr == '-') { prim->type |= LT; numstr++; } prim->size = atol(numstr); } else if (EQ("-perm", argv[i])) { prim->type = PERM; prim->size = PROTECTION; /* assemble desired protection bits */ for(i++, j = 0 ; argv[i][j] ; j++) { switch(argv[i][j]) { case 'n': if (prim->size != PROTECTION) fprintf(stderr, "'n' permission code overriding other given codes \n"); prim->size = PROTECTION; return(i); case 'h': prim->size |= FIBF_HIDDEN; break; case 's': prim->size |= FIBF_SCRIPT; break; case 'p': prim->size |= FIBF_PURE; break; case 'a': prim->size |= FIBF_ARCHIVE; break; case 'r': prim->size &= ~FIBF_READ; break; case 'w': prim->size &= ~FIBF_WRITE; break; case 'e': prim->size &= ~FIBF_EXECUTE; break; case 'd': prim->size &= ~FIBF_DELETE; break; default: fprintf(stderr, "unknown permission code '%c' ... ignored\n", argv[i][j]); break; } } } else if (EQ("-prot", argv[i])) { prim->type = PROT; prim->size = 0; /* assemble desired protection bits */ for(i++, j = 0 ; argv[i][j] ; j++) { switch(argv[i][j]) { case 'h': prim->size |= FIBF_HIDDEN; break; case 's': prim->size |= FIBF_SCRIPT; break; case 'p': prim->size |= FIBF_PURE; break; case 'a': prim->size |= FIBF_ARCHIVE; break; case 'r': prim->size |= FIBF_READ; break; case 'w': prim->size |= FIBF_WRITE; break; case 'e': prim->size |= FIBF_EXECUTE; break; case 'd': prim->size |= FIBF_DELETE; break; default: fprintf(stderr, "unknown protection code '%c' ... ignored\n", argv[i][j]); break; } } } else { fprintf(stderr, "unknown primary: %s\n", argv[i]); exit(5); } return(i); } } /* * Compare a wild card name with a normal name. * Taken from Matt Dillon's csh program. */ #define MAXB 8 compare_ok(wild, name) char *wild, *name; { register char *w = wild; register char *n = name; char *back[MAXB][2]; register char s1, s2; int bi = 0; while (*n || *w) { switch (*w) { case '*': if (bi == MAXB) { fprintf(stderr,"Too many levels of '*'\n"); return (0); } back[bi][0] = w; back[bi][1] = n; ++bi; ++w; continue; goback: --bi; while (bi >= 0 && *back[bi][1] == '\0') --bi; if (bi < 0) return (0); w = back[bi][0] + 1; n = ++back[bi][1]; ++bi; continue; case '?': if (!*n) { if (bi) goto goback; return (0); } break; default: s1 = (*n >= 'A' && *n <= 'Z') ? *n - 'A' + 'a' : *n; s2 = (*w >= 'A' && *w <= 'Z') ? *w - 'A' + 'a' : *w; if (s1 != s2) { if (bi) goto goback; return (0); } break; } if (*n) ++n; if (*w) ++w; } return (1); }