text: /*
   * This file is part of the UCB release of Plan 9. It is subject to the license
   * terms in the LICENSE file found in the top-level directory of this
   * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   * part of the UCB release of Plan 9, including this file, may be copied,
   * modified, propagated, or distributed except according to the terms contained
   * in the LICENSE file.
   */
  
  #define	EXTERN
  #include	"grep.h"
  
  char *validflags = "bchiLlnsv";
  void
  usage(void)
  {
  	fprint(2, "usage: grep [-%s] [-e pattern] [-f patternfile] [file ...]\n",
  		   validflags);
  	exits("usage");
  }
  
  void
  main(int argc, char *argv[])
  {
  	int i, status;
  
  	ARGBEGIN {
  default:
  		if (utfrune(validflags, ARGC()) == nil)
  			usage();
  		flags[ARGC()]++;
  		break;
  
  case 'e':
  		flags['e']++;
  		lineno = 0;
  		str2top(EARGF(usage()));
  		break;
  
  case 'f':
  		flags['f']++;
  		filename = EARGF(usage());
  		rein = Bopen(filename, OREAD);
  		if (rein == 0) {
  			fprint(2, "grep: can't open %s: %r\n", filename);
  			exits("open");
  		}
  		lineno = 1;
  		str2top(filename);
  		break;
  	}
  	ARGEND if (flags['f'] == 0 && flags['e'] == 0) {
  		if (argc <= 0)
  			usage();
  		str2top(argv[0]);
  		argc--;
  		argv++;
  	}
  
  	follow = mal(maxfollow * sizeof(*follow));
  	state0 = initstate(topre.beg);
  
  	Binit(&bout, 1, OWRITE);
  	switch (argc) {
  		case 0:
  			status = search(0, 0);
  			break;
  		case 1:
  			status = search(argv[0], 0);
  			break;
  		default:
  			status = 0;
  			for (i = 0; i < argc; i++)
  				status |= search(argv[i], Hflag);
  			break;
  	}
  	if (status)
  		exits(0);
  	exits("no matches");
  }
  
  int
  search(char *file, int flag)
  {
  	State *s, *ns;
  	int c, fid, eof, nl, empty;
  	int32_t count, lineno, n;
  	uint8_t *elp, *lp, *bol;
  
  	if (file == 0) {
  		file = "stdin";
  		fid = 0;
  		flag |= Bflag;
  	} else
  		fid = open(file, OREAD);
  
  	if (fid < 0) {
  		fprint(2, "grep: can't open %s: %r\n", file);
  		return 0;
  	}
  
  	if (flags['b'])
  		flag ^= Bflag;	/* dont buffer output */
  	if (flags['c'])
  		flag |= Cflag;	/* count */
  	if (flags['h'])
  		flag &= ~Hflag;	/* do not print file name in output */
  	if (flags['i'])
  		flag |= Iflag;	/* fold upper-lower */
  	if (flags['l'])
  		flag |= Llflag;	/* print only name of file if any match */
  	if (flags['L'])
  		flag |= LLflag;	/* print only name of file if any non match */
  	if (flags['n'])
  		flag |= Nflag;	/* count only */
  	if (flags['s'])
  		flag |= Sflag;	/* status only */
  	if (flags['v'])
  		flag |= Vflag;	/* inverse match */
  
  	s = state0;
  	lineno = 0;
  	count = 0;
  	eof = 0;
  	empty = 1;
  	nl = 0;
  	lp = u.buf;
  	bol = lp;
  
  loop0:
  	n = lp - bol;
  	if (n > sizeof(u.pre))
  		n = sizeof(u.pre);
  	memmove(u.buf - n, bol, n);
  	bol = u.buf - n;
  	n = read(fid, u.buf, sizeof(u.buf));
  	/* if file has no final newline, simulate one to emit matches to last line */
  	if (n > 0) {
  		empty = 0;
  		nl = u.buf[n - 1] == '\n';
  	} else {
  		if (n < 0) {
  			fprint(2, "grep: read error on %s: %r\n", file);
  			return count != 0;
  		}
  		if (!eof && !nl && !empty) {
  			u.buf[0] = '\n';
  			n = 1;
  			eof = 1;
  		}
  	}
  	if (n <= 0) {
  		close(fid);
  		if (flag & Cflag) {
  			if (flag & Hflag)
  				Bprint(&bout, "%s:", file);
  			Bprint(&bout, "%ld\n", count);
  		}
  		if (((flag & Llflag) && count != 0) || ((flag & LLflag) && count == 0))
  			Bprint(&bout, "%s\n", file);
  		Bflush(&bout);
  		return count != 0;
  	}
  	lp = u.buf;
  	elp = lp + n;
  	if (flag & Iflag)
  		goto loopi;
  
  /*
   * normal character loop
   */
  loop:
  	c = *lp;
  	ns = s->next[c];
  	if (ns == 0) {
  		increment(s, c);
  		goto loop;
  	}
  //  if(flags['2'])
  //      if(s->match)
  //          print("%d: %.2x**\n", s, c);
  //      else
  //          print("%d: %.2x\n", s, c);
  	lp++;
  	s = ns;
  	if (c == '\n') {
  		lineno++;
  		if (! !s->match == !(flag & Vflag)) {
  			count++;
  			if (flag & (Cflag | Sflag | Llflag | LLflag))
  				goto cont;
  			if (flag & Hflag)
  				Bprint(&bout, "%s:", file);
  			if (flag & Nflag)
  				Bprint(&bout, "%ld: ", lineno);
  			/* suppress extra newline at EOF unless we are labeling matches with file name */
  			Bwrite(&bout, bol, lp - bol - (eof && !(flag & Hflag)));
  			if (flag & Bflag)
  				Bflush(&bout);
  		}
  		if ((lineno & Flshcnt) == 0)
  			Bflush(&bout);
  cont:
  		bol = lp;
  	}
  	if (lp != elp)
  		goto loop;
  	goto loop0;
  
  /*
   * character loop for -i flag
   * for speed
   */
  loopi:
  	c = *lp;
  	if (c >= 'A' && c <= 'Z')
  		c += 'a' - 'A';
  	ns = s->next[c];
  	if (ns == 0) {
  		increment(s, c);
  		goto loopi;
  	}
  	lp++;
  	s = ns;
  	if (c == '\n') {
  		lineno++;
  		if (! !s->match == !(flag & Vflag)) {
  			count++;
  			if (flag & (Cflag | Sflag | Llflag | LLflag))
  				goto conti;
  			if (flag & Hflag)
  				Bprint(&bout, "%s:", file);
  			if (flag & Nflag)
  				Bprint(&bout, "%ld: ", lineno);
  			/* suppress extra newline at EOF unless we are labeling matches with file name */
  			Bwrite(&bout, bol, lp - bol - (eof && !(flag & Hflag)));
  			if (flag & Bflag)
  				Bflush(&bout);
  		}
  		if ((lineno & Flshcnt) == 0)
  			Bflush(&bout);
  conti:
  		bol = lp;
  	}
  	if (lp != elp)
  		goto loopi;
  	goto loop0;
  }
  
  State *
  initstate(Re * r)
  {
  	State *s;
  	int i;
  
  	addcase(r);
  	if (flags['1'])
  		reprint("r", r);
  	nfollow = 0;
  	gen++;
  	fol1(r, Cbegin);
  	follow[nfollow++] = r;
  	qsort(follow, nfollow, sizeof(*follow), fcmp);
  
  	s = sal(nfollow);
  	for (i = 0; i < nfollow; i++)
  		s->re[i] = follow[i];
  	return s;
  }
  