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