grep.c (4866B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <regex.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <strings.h> 7 8 #include "queue.h" 9 #include "util.h" 10 11 enum { Match = 0, NoMatch = 1, Error = 2 }; 12 13 static void addpattern(const char *); 14 static void addpatternfile(FILE *); 15 static int grep(FILE *, const char *); 16 17 static int Eflag; 18 static int Fflag; 19 static int Hflag; 20 static int eflag; 21 static int fflag; 22 static int hflag; 23 static int iflag; 24 static int sflag; 25 static int vflag; 26 static int wflag; 27 static int xflag; 28 static int many; 29 static int mode; 30 31 struct pattern { 32 regex_t preg; 33 SLIST_ENTRY(pattern) entry; 34 char pattern[]; 35 }; 36 37 static SLIST_HEAD(phead, pattern) phead; 38 39 static void 40 addpattern(const char *pattern) 41 { 42 struct pattern *pnode; 43 size_t patlen; 44 45 patlen = strlen(pattern); 46 47 pnode = enmalloc(Error, sizeof(*pnode) + patlen + 9); 48 SLIST_INSERT_HEAD(&phead, pnode, entry); 49 50 if (Fflag || (!xflag && !wflag)) { 51 memcpy(pnode->pattern, pattern, patlen + 1); 52 } else { 53 sprintf(pnode->pattern, "%s%s%s%s%s", 54 xflag ? "^" : "\\<", 55 Eflag ? "(" : "\\(", 56 pattern, 57 Eflag ? ")" : "\\)", 58 xflag ? "$" : "\\>"); 59 } 60 } 61 62 static void 63 addpatternfile(FILE *fp) 64 { 65 static char *buf = NULL; 66 static size_t size = 0; 67 ssize_t len = 0; 68 69 while ((len = getline(&buf, &size, fp)) > 0) { 70 if (buf[len - 1] == '\n') 71 buf[len - 1] = '\0'; 72 addpattern(buf); 73 } 74 if (ferror(fp)) 75 enprintf(Error, "read error:"); 76 } 77 78 static int 79 grep(FILE *fp, const char *str) 80 { 81 static char *buf = NULL; 82 static size_t size = 0; 83 ssize_t len = 0; 84 long c = 0, n; 85 struct pattern *pnode; 86 int match, result = NoMatch; 87 88 for (n = 1; (len = getline(&buf, &size, fp)) > 0; n++) { 89 /* Remove the trailing newline if one is present. */ 90 if (buf[len - 1] == '\n') 91 buf[len - 1] = '\0'; 92 match = 0; 93 SLIST_FOREACH(pnode, &phead, entry) { 94 if (Fflag) { 95 if (xflag) { 96 if (!(iflag ? strcasecmp : strcmp)(buf, pnode->pattern)) { 97 match = 1; 98 break; 99 } 100 } else { 101 if ((iflag ? strcasestr : strstr)(buf, pnode->pattern)) { 102 match = 1; 103 break; 104 } 105 } 106 } else { 107 if (regexec(&pnode->preg, buf, 0, NULL, 0) == 0) { 108 match = 1; 109 break; 110 } 111 } 112 } 113 if (match != vflag) { 114 result = Match; 115 switch (mode) { 116 case 'c': 117 c++; 118 break; 119 case 'l': 120 puts(str); 121 goto end; 122 case 'q': 123 exit(Match); 124 default: 125 if (!hflag && (many || Hflag)) 126 printf("%s:", str); 127 if (mode == 'n') 128 printf("%ld:", n); 129 puts(buf); 130 break; 131 } 132 } 133 } 134 if (mode == 'c') 135 printf("%ld\n", c); 136 end: 137 if (ferror(fp)) { 138 weprintf("%s: read error:", str); 139 result = Error; 140 } 141 return result; 142 } 143 144 static void 145 usage(void) 146 { 147 enprintf(Error, "usage: %s [-EFHchilnqsvwx] [-e pattern] [-f file] " 148 "[pattern] [file ...]\n", argv0); 149 } 150 151 int 152 main(int argc, char *argv[]) 153 { 154 struct pattern *pnode; 155 int m, flags = REG_NOSUB, match = NoMatch; 156 FILE *fp; 157 char *arg; 158 159 SLIST_INIT(&phead); 160 161 ARGBEGIN { 162 case 'E': 163 Eflag = 1; 164 Fflag = 0; 165 flags |= REG_EXTENDED; 166 break; 167 case 'F': 168 Fflag = 1; 169 Eflag = 0; 170 flags &= ~REG_EXTENDED; 171 break; 172 case 'H': 173 Hflag = 1; 174 hflag = 0; 175 break; 176 case 'e': 177 arg = EARGF(usage()); 178 if (!(fp = fmemopen(arg, strlen(arg) + 1, "r"))) 179 eprintf("fmemopen:"); 180 addpatternfile(fp); 181 efshut(fp, arg); 182 eflag = 1; 183 break; 184 case 'f': 185 arg = EARGF(usage()); 186 fp = fopen(arg, "r"); 187 if (!fp) 188 enprintf(Error, "fopen %s:", arg); 189 addpatternfile(fp); 190 efshut(fp, arg); 191 fflag = 1; 192 break; 193 case 'h': 194 hflag = 1; 195 Hflag = 0; 196 break; 197 case 'c': 198 case 'l': 199 case 'n': 200 case 'q': 201 mode = ARGC(); 202 break; 203 case 'i': 204 flags |= REG_ICASE; 205 iflag = 1; 206 break; 207 case 's': 208 sflag = 1; 209 break; 210 case 'v': 211 vflag = 1; 212 break; 213 case 'w': 214 wflag = 1; 215 break; 216 case 'x': 217 xflag = 1; 218 break; 219 default: 220 usage(); 221 } ARGEND 222 223 if (argc == 0 && !eflag && !fflag) 224 usage(); /* no pattern */ 225 226 /* just add literal pattern to list */ 227 if (!eflag && !fflag) { 228 if (!(fp = fmemopen(argv[0], strlen(argv[0]) + 1, "r"))) 229 eprintf("fmemopen:"); 230 addpatternfile(fp); 231 efshut(fp, argv[0]); 232 argc--; 233 argv++; 234 } 235 236 if (!Fflag) 237 /* Compile regex for all search patterns */ 238 SLIST_FOREACH(pnode, &phead, entry) 239 enregcomp(Error, &pnode->preg, pnode->pattern, flags); 240 many = (argc > 1); 241 if (argc == 0) { 242 match = grep(stdin, "<stdin>"); 243 } else { 244 for (; *argv; argc--, argv++) { 245 if (!strcmp(*argv, "-")) { 246 *argv = "<stdin>"; 247 fp = stdin; 248 } else if (!(fp = fopen(*argv, "r"))) { 249 if (!sflag) 250 weprintf("fopen %s:", *argv); 251 match = Error; 252 continue; 253 } 254 m = grep(fp, *argv); 255 if (m == Error || (match != Error && m == Match)) 256 match = m; 257 if (fp != stdin && fshut(fp, *argv)) 258 match = Error; 259 } 260 } 261 262 if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>")) 263 match = Error; 264 265 return match; 266 }