morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base
Log | Files | Refs

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 }