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