xv6, line by line
user/grep.c

user/grep.c

C · 108 lines · annotated 100% · user program / library · upstream

About this file

grep pattern [file ...] prints every line of its input that matches a regular expression. grep ^xv6 README prints the lines of README that start with xv6; ls | grep sh filters the output of ls. With no file names it reads standard input, so it works as a filter in a pipeline.

The file has two independent halves:

  • grep() and main() (lines 8–63) deal with I/O: reading input in chunks with read, cutting it into lines, and writing matching lines to standard output. This is the interesting systems part: read returns arbitrary chunks, not lines, so the program must handle lines that straddle two reads.
  • match, matchhere and matchstar (lines 65–108) are a complete regular expression matcher in about 30 lines, from Kernighan and Pike. It supports c (a literal character), . (any character), * (zero or more of the previous character), ^ (start of line) and $ (end of line). There are no character classes and no escapes: there is no way to match only a literal . (it always matches any character), and * is literal only where it cannot repeat anything: at the start of the pattern or right after another c*.

The shell does not strip quotes, so write patterns without them: grep x.*v6 README, not grep 'x.*v6' README (the quotes would become part of the pattern).

Read before: user/cat.c (the read loop). Read next: user/wc.c.

1// Simple grep. Only supports ^ . * $ operators.
8char buf[1024];
9int match(char *, char *);
11void
12grep(char *pattern, int fd)
14 int n, m;
15 char *p, *q;
17 m = 0;
18 while ((n = read(fd, buf + m, sizeof(buf) - m - 1)) > 0) {
19 m += n;
20 buf[m] = '\0';
21 p = buf;
22 while ((q = strchr(p, '\n')) != 0) {
23 *q = 0;
24 if (match(pattern, p)) {
25 *q = '\n';
26 write(1, p, q + 1 - p);
27 }
28 p = q + 1;
29 }
30 if (m > 0) {
31 m -= p - buf;
33 }
34 }
37int
38main(int argc, char *argv[])
40 int fd, i;
41 char *pattern;
43 if (argc <= 1) {
44 fprintf(2, "usage: grep pattern [file ...]\n");
45 exit(1);
46 }
49 if (argc <= 2) {
51 exit(0);
52 }
54 for (i = 2; i < argc; i++) {
55 if ((fd = open(argv[i], O_RDONLY)) < 0) {
56 printf("grep: cannot open %s\n", argv[i]);
57 exit(1);
58 }
61 }
62 exit(0);
65// Regexp matcher from Kernighan & Pike,
66// The Practice of Programming, Chapter 9, or
67// https://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
69int matchhere(char *, char *);
70int matchstar(int, char *, char *);
72int
73match(char *re, char *text)
75 if (re[0] == '^')
76 return matchhere(re + 1, text);
77 do { // must look at empty string
79 return 1;
80 } while (*text++ != '\0');
81 return 0;
84// matchhere: search for re at beginning of text
85int
86matchhere(char *re, char *text)
88 if (re[0] == '\0')
89 return 1;
90 if (re[1] == '*')
91 return matchstar(re[0], re + 2, text);
92 if (re[0] == '$' && re[1] == '\0')
93 return *text == '\0';
94 if (*text != '\0' && (re[0] == '.' || re[0] == *text))
95 return matchhere(re + 1, text + 1);
96 return 0;
99// matchstar: search for c*re at beginning of text
100int
101matchstar(int c, char *re, char *text)
103 do { // a * matches zero or more instances
105 return 1;
106 } while (*text != '\0' && (*text++ == c || c == '.'));
107 return 0;