/* asdf.c * Something like grep * By Feky, November-December 2007 */ #include #include #include #include #include #define VERSION "asdf 0.15" #define OPTS "d:t:s:e:rnih" #define COLOR "\e[0;32m" /* green */ #define NO_COLOR "\e[0m" #define UCHAR_MAX 255 /* didn't feel like including limits.h only for this */ #define LINE_BUFFER 255 typedef enum {NO, YES} BOOL; typedef const unsigned char cuchar_t; struct opt_st { /* structure of options */ char *t; char *s; int e; BOOL n; BOOL r; }; void help (void); /* Boyer-Moore-Horspool algorithm for fast string search * from http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm */ cuchar_t *bmh (cuchar_t *, cuchar_t *); BOOL search (const char *, struct opt_st); void help (void) { printf("* %s *\nExample 1: asdf -t \'return\' -d ~/stuff -e 10\n" "Example 2: asdf -t \'int main\' -r -n\n" "Options:\n" " -d [DIRECTORY] Specify directory\n" " -t [TEXT] Text to search\n" " -s [TEXT] Skip files that contain this string in their name\n" " -n Print line number in output\n" " -r Recursive subdirectory search\n" " -e [INT] Stop after a number of matches\n" " -h Show help\n", VERSION); } cuchar_t *bmh (cuchar_t *haystack, cuchar_t *needle) { size_t hlen = strlen(haystack); size_t nlen = strlen(needle); size_t scan = 0; size_t bad_char_skip[UCHAR_MAX + 1]; if (nlen <= 0 || !haystack || !needle) return NULL; for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) bad_char_skip[scan] = nlen; size_t last = nlen - 1; for (scan = 0; scan < last; scan = scan + 1) bad_char_skip[needle[scan]] = last - scan; while (hlen >= nlen) { for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) if (scan == 0) return haystack; hlen -= bad_char_skip[haystack[last]]; haystack += bad_char_skip[haystack[last]]; } return NULL; } BOOL search (const char *d, struct opt_st opt) { struct dirent *en; struct stat statbuf; FILE *tmp; BOOL i = YES; int exit = opt.e; int j = 0; char fbuff[LINE_BUFFER]; DIR *dir = opendir(d); #ifdef DEBUG printf("%s, ", d); printf("e: %d, %d\n", opt.e, exit); #endif if(dir == NULL) { perror("opendir"); return YES; } chdir(d); while((en = readdir(dir)) != NULL) { stat(en->d_name, &statbuf); if(S_ISDIR(statbuf.st_mode) && opt.r == YES) { /* check subdirs ? */ if(!strcmp(en->d_name,"..") || !strcmp(en->d_name, ".")) /* skip useless crap */ continue; search(en->d_name, opt); /* recursion */ } if(!bmh(en->d_name, opt.s)) /* skip this file if needed */ if(S_ISREG(statbuf.st_mode)) { /* if it's a regular file */ tmp = fopen(en->d_name, "r"); #ifdef DEBUG printf("f: %s\n", en->d_name); #endif if(tmp == NULL) { /* something went wrong; we die */ perror("fopen"); return YES; } j = 0; while(!feof(tmp)) { j++; fgets(fbuff, LINE_BUFFER, tmp); if(bmh(fbuff, opt.t) != NULL && (--exit >= 0 || opt.e <= 0)) { if(opt.n == YES) printf("%s%s/%s (line %d)%s: %s", COLOR, d, en->d_name, j, NO_COLOR, fbuff); else printf("%s%s/%s%s: %s", COLOR, d, en->d_name, NO_COLOR, fbuff); if(i == YES) i = NO; } } fclose(tmp); } } chdir(".."); closedir(dir); return i; } int main (int argc, char **argv) { int c; struct opt_st opt; char *d = "."; while((c = getopt(argc, argv, OPTS)) != -1) { switch(c) { case 'd': d = optarg; break; case 't': opt.t = optarg; break; case 's': opt.s = optarg; break; case 'e': opt.e = atoi(optarg); break; case 'r': opt.r = YES; break; case 'n': opt.n = YES; break; case 'h': help(); return 0; break; } } if(search(d, opt)) { printf("Not found or error.\n"); return 1; } return 0; }