summaryrefslogblamecommitdiff
path: root/src/grep.c
blob: 40cc078c46e538114cd8464051060cbef350bcba (plain) (tree)
1
2
3

         
                                            





































































                                                                          

                         












                                                          



                                                      



















































                                                                                  
                                                                 












































                                                                                               



                                                         









                                                                     
/*
 * grep.c
 * Implementation of POSIX 2008 grep utility
 * Copyright © 2012 Rich Felker
 * Licensed under the terms of the GNU General Public License, v2 or later
 */

#include <stdio.h>
#include <regex.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

struct pattern {
	struct pattern *next;
	char *str;
	regex_t re;
};

static struct pattern *head, *tail;

static void usage()
{
	exit(1);
}

static void addpats(const char *prog, int mode, const char *src)
{
	FILE *f;
	char *line = 0;
	ssize_t llen = 0;
	struct pattern *pat;
	f = (mode=='f') ? fopen(src, "rb")
		: fmemopen((void *)src, strlen(src), "rb");
	if (!f) {
		fprintf(stderr, "%s: ", prog);
		perror(mode=='f' ? src : 0);
		exit(1);
	}
	while ((llen = getline(&line, (size_t[]){0}, f)) >= 0) {
		if (llen && line[llen-1]=='\n')
			line[--llen] = 0;
		pat = malloc(sizeof *pat);
		if (!pat) {
			perror(prog);
			exit(1);
		}
		pat->str = line;
		pat->next = 0;
		if (tail) tail->next = pat;
		else head = tail = pat;
		line = 0;
		llen = 0;
	}
	fclose(f);
}

#define FLAG_E (1U<<0)
#define FLAG_F (1U<<1)
#define FLAG_c (1U<<2)
#define FLAG_i (1U<<7)
#define FLAG_l (1U<<8)
#define FLAG_n (1U<<9)
#define FLAG_q (1U<<10)
#define FLAG_s (1U<<11)
#define FLAG_v (1U<<12)
#define FLAG_x (1U<<13)

int main(int argc, char **argv)
{
	static const char *optpat = "EFce:f:ilnqsvx";
	FILE *f;
	char *line = 0;
	size_t lsize = 0;
	ssize_t llen;
	int b;
	int multifile;
	struct pattern *pat;
	int match, any_matches = 0;
	long long count, lineno;
	unsigned flags = 0;
	const char *filename;
	int err;

	while ((b=getopt(argc, argv, optpat))!=EOF) {
		flags |= 1U << strchr(optpat, b)-optpat;
		if (b-'e'<2U) addpats(argv[0], b, optarg);
	}
	if (!head) {
		if (!argv[optind]) usage();
		addpats(argv[0], 'e', argv[optind++]);
	}

	if (!(flags & FLAG_F)) {
		int re_opts = (flags & FLAG_E)
			? REG_NOSUB|REG_EXTENDED
			: REG_NOSUB;
		if (flags & FLAG_i) re_opts |= REG_ICASE;
		for (pat=head; pat; pat=pat->next) {
			if (flags & FLAG_x) {
				char *tmp = malloc(strlen(pat->str)+3);
				if (!tmp) {
					perror(argv[0]);
					exit(1);
				}
				sprintf(tmp, "%s%s%s",
					pat->str[0]=='^'?"":"^", pat->str,
					pat->str[strlen(pat->str)-1]=='$'?"":"$");
				free(pat->str);
				pat->str = tmp;
			}
			if ((err=regcomp(&pat->re, pat->str, re_opts))) {
				char errstr[256];
				regerror(err, &pat->re, errstr, sizeof errstr);
				fprintf(stderr, "%s: %s\n", argv[0], errstr);
				exit(1);
			}
			free(pat->str);
		}
	}

	if (!argv[optind]) {
		optind--;
		filename = "(standard input)";
		f = stdin;
		multifile = 0;
		goto process_stdin;
	}

	multifile = !!argv[optind+1];

	for (; argv[optind]; optind++) {
		filename = argv[optind];
		f = fopen(filename, "rb");
		if (!f) {
			if (!(flags & FLAG_s)) {
				fprintf(stderr, "%s: ", argv[0]);
				perror(filename);
			}
			continue;
		}
process_stdin:
		count = 0;
		lineno = 0;
		while ((llen = getline(&line, &lsize, f)) >= 0) {
			lineno++;
			if (llen && line[llen-1]=='\n')
				line[--llen] = 0;
			match = 0;
			for (pat=head; pat; pat=pat->next) {
				if (flags & FLAG_F) {
					if (flags & FLAG_x) {
						if (!strcmp(line, pat->str)) {
							match = 1;
							break;
						}
					} else if (strstr(line, pat->str)) {
						match = 1;
						break;
					}
				} else if (!regexec(&pat->re, line, 0, 0, 0)) {
					match = 1;
					break;
				}
			}
			if (flags & FLAG_v) match = !match;
			if (match) {
				any_matches = 1;
				if (flags & FLAG_q) exit(0);
				if (flags & FLAG_l) {
					puts(filename);
					break;
				}
				if (flags & FLAG_c) {
					count++;
					continue;
				}
				if (multifile) {
					if (flags & FLAG_n)
						printf("%s:%lld:%s\n", filename, lineno, line);
					else
						printf("%s:%s\n", filename, line);
				} else {
					if (flags & FLAG_n)
						printf("%lld:%s\n", lineno, line);
					else
						puts(line);
				}
			}
		}
		if (ferror(f) && !(flags & FLAG_s)) {
			fprintf(stderr, "%s: ", argv[0]);
			perror(filename);
		}
		if (flags & FLAG_c) {
			if (multifile)
				printf("%s:%lld\n", filename, count);
			else
				printf("%lld\n", count);
		}
		fclose(f);
	}
	return !any_matches;
}