Print this page
4701 would like grep context options (-A, -B, -C)


  47 #include <regex.h>
  48 #include <limits.h>
  49 #include <sys/types.h>
  50 #include <sys/stat.h>
  51 #include <fcntl.h>
  52 #include <stdio.h>
  53 #include <locale.h>
  54 #include <wchar.h>
  55 #include <errno.h>
  56 #include <unistd.h>
  57 #include <wctype.h>
  58 #include <ftw.h>
  59 #include <sys/param.h>
  60 
  61 #define STDIN_FILENAME gettext("(standard input)")
  62 
  63 #define BSIZE           512             /* Size of block for -b */
  64 #define BUFSIZE         8192            /* Input buffer size */
  65 #define MAX_DEPTH       1000            /* how deep to recurse */
  66 




  67 #define M_CSETSIZE      256             /* singlebyte chars */
  68 static int      bmglen;                 /* length of BMG pattern */
  69 static char     *bmgpat;                /* BMG pattern */
  70 static int      bmgtab[M_CSETSIZE];     /* BMG delta1 table */
  71 
  72 typedef struct  _PATTERN        {
  73         char    *pattern;               /* original pattern */
  74         wchar_t *wpattern;              /* wide, lowercased pattern */
  75         struct  _PATTERN        *next;
  76         regex_t re;                     /* compiled pattern */
  77 } PATTERN;
  78 
  79 static PATTERN  *patterns;
  80 static char     errstr[128];            /* regerror string buffer */
  81 static int      regflags = 0;           /* regcomp options */
  82 static int      matched = 0;            /* return of the grep() */
  83 static int      errors = 0;             /* count of errors */
  84 static uchar_t  fgrep = 0;              /* Invoked as fgrep */
  85 static uchar_t  egrep = 0;              /* Invoked as egrep */
  86 static uchar_t  nvflag = 1;             /* Print matching lines */
  87 static uchar_t  cflag;                  /* Count of matches */
  88 static uchar_t  iflag;                  /* Case insensitve matching */
  89 static uchar_t  Hflag;                  /* Precede lines by file name */
  90 static uchar_t  hflag;                  /* Supress printing of filename */
  91 static uchar_t  lflag;                  /* Print file names of matches */
  92 static uchar_t  nflag;                  /* Precede lines by line number */
  93 static uchar_t  rflag;                  /* Search directories recursively */
  94 static uchar_t  bflag;                  /* Preccede matches by block number */
  95 static uchar_t  sflag;                  /* Suppress file error messages */
  96 static uchar_t  qflag;                  /* Suppress standard output */
  97 static uchar_t  wflag;                  /* Search for expression as a word */
  98 static uchar_t  xflag;                  /* Anchoring */
  99 static uchar_t  Eflag;                  /* Egrep or -E flag */
 100 static uchar_t  Fflag;                  /* Fgrep or -F flag */
 101 static uchar_t  Rflag;                  /* Like rflag, but follow symlinks */
 102 static uchar_t  outfn;                  /* Put out file name */

 103 static char     *cmdname;
 104 
 105 static int      use_wchar, use_bmg, mblocale;
 106 
 107 static size_t   outbuflen, prntbuflen;
 108 static char     *prntbuf;

 109 static wchar_t  *outline;
 110 
 111 static void     addfile(const char *fn);
 112 static void     addpattern(char *s);
 113 static void     fixpatterns(void);
 114 static void     usage(void);
 115 static int      grep(int, const char *);
 116 static void     bmgcomp(char *, int);
 117 static char     *bmgexec(char *, char *);
 118 static int      recursive(const char *, const struct stat *, int, struct FTW *);
 119 static void     process_path(const char *);
 120 static void     process_file(const char *, int);
 121 
 122 /*
 123  * mainline for grep
 124  */
 125 int
 126 main(int argc, char **argv)
 127 {
 128         char    *ap;
 129         int     c;
 130         int     fflag = 0;
 131         int     i, n_pattern = 0, n_file = 0;
 132         char    **pattern_list = NULL;
 133         char    **file_list = NULL;
 134 
 135         (void) setlocale(LC_ALL, "");
 136 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 137 #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it weren't */
 138 #endif
 139         (void) textdomain(TEXT_DOMAIN);
 140 
 141         /*
 142          * true if this is running on the multibyte locale
 143          */
 144         mblocale = (MB_CUR_MAX > 1);
 145         /*
 146          * Skip leading slashes
 147          */
 148         cmdname = argv[0];
 149         if (ap = strrchr(cmdname, '/'))
 150                 cmdname = ap + 1;
 151 
 152         ap = cmdname;
 153         /*
 154          * Detect egrep/fgrep via command name, map to -E and -F options.
 155          */
 156         if (*ap == 'e' || *ap == 'E') {
 157                 regflags |= REG_EXTENDED;
 158                 egrep++;
 159         } else {
 160                 if (*ap == 'f' || *ap == 'F') {
 161                         fgrep++;
 162                 }
 163         }
 164 
 165         while ((c = getopt(argc, argv, "vwchHilnrbse:f:qxEFIR")) != EOF) {


































 166                 switch (c) {
 167                 case 'v':       /* POSIX: negate matches */
 168                         nvflag = 0;
 169                         break;
 170 
 171                 case 'c':       /* POSIX: write count */
 172                         cflag++;
 173                         break;
 174 
 175                 case 'i':       /* POSIX: ignore case */
 176                         iflag++;
 177                         regflags |= REG_ICASE;
 178                         break;
 179 
 180                 case 'l':       /* POSIX: Write filenames only */
 181                         lflag++;
 182                         break;
 183 
 184                 case 'n':       /* POSIX: Write line numbers */
 185                         nflag++;


 245 
 246                 case 'x':       /* POSIX: full line matches */
 247                         xflag++;
 248                         regflags |= REG_ANCHOR;
 249                         break;
 250 
 251                 case 'E':       /* POSIX: Extended RE's */
 252                         regflags |= REG_EXTENDED;
 253                         Eflag++;
 254                         break;
 255 
 256                 case 'F':       /* POSIX: strings, not RE's */
 257                         Fflag++;
 258                         break;
 259 
 260                 case 'R':       /* Solaris: like rflag, but follow symlinks */
 261                         Rflag++;
 262                         rflag++;
 263                         break;
 264 











































 265                 default:
 266                         usage();
 267                 }
 268         }
 269         /*
 270          * If we're invoked as egrep or fgrep we need to do some checks
 271          */
 272 
 273         if (egrep || fgrep) {
 274                 /*
 275                  * Use of -E or -F with egrep or fgrep is illegal
 276                  */
 277                 if (Eflag || Fflag)
 278                         usage();
 279                 /*
 280                  * Don't allow use of wflag with egrep / fgrep
 281                  */
 282                 if (wflag)
 283                         usage();
 284                 /*


 525                          */
 526                         bufsiz += BUFSIZE;
 527                         if ((inbuf = realloc(inbuf, bufsiz)) == NULL) {
 528                                 (void) fprintf(stderr,
 529                                     gettext("%s: out of memory\n"),
 530                                     cmdname);
 531                                 exit(2);
 532                         }
 533                         bufp = inbuf + bufused;
 534                         continue;
 535                 }
 536                 if (bufp[buflen - 1] == '\n') {
 537                         bufp[--buflen] = '\0';
 538                 }
 539                 addpattern(inbuf);
 540 
 541                 bufp = inbuf;
 542                 bufused = 0;
 543         }
 544         free(inbuf);


 545         (void) fclose(fp);
 546 }
 547 
 548 /*
 549  * Add a string to the pattern list.
 550  */
 551 static void
 552 addpattern(char *s)
 553 {
 554         PATTERN *pp;
 555         char    *wordbuf;
 556         char    *np;
 557 
 558         for (; ; ) {
 559                 np = strchr(s, '\n');
 560                 if (np != NULL)
 561                         *np = '\0';
 562                 if ((pp = malloc(sizeof (PATTERN))) == NULL) {
 563                         (void) fprintf(stderr, gettext(
 564                             "%s: out of memory\n"),


 701                 if ((rv = regcomp(&pp->re, pp->pattern, regflags)) != 0) {
 702                         (void) regerror(rv, &pp->re, errstr, sizeof (errstr));
 703                         (void) fprintf(stderr,
 704                             gettext("%s: RE error in %s: %s\n"),
 705                             cmdname, pp->pattern, errstr);
 706                         exit(2);
 707                 }
 708                 free(pp->pattern);
 709         }
 710 
 711         /*
 712          * Decide if we are able to run the Boyer-Moore-Gosper algorithm.
 713          * Use the Boyer-Moore-Gosper algorithm if:
 714          * - fgrep                      (Fflag)
 715          * - singlebyte locale          (!mblocale)
 716          * - no ignoring case           (!iflag)
 717          * - no printing line numbers   (!nflag)
 718          * - no negating the output     (nvflag)
 719          * - only one pattern           (npatterns == 1)
 720          * - non zero length pattern    (strlen(patterns->pattern) != 0)

 721          *
 722          * It's guaranteed patterns->pattern is still alive
 723          * when Fflag && !mblocale.
 724          */
 725         use_bmg = Fflag && !mblocale && !iflag && !nflag && nvflag &&
 726             (npatterns == 1) && (strlen(patterns->pattern) != 0);
 727 }
 728 
 729 /*
 730  * Search a newline from the beginning of the string
 731  */
 732 static char *
 733 find_nl(const char *ptr, size_t len)
 734 {
 735         while (len-- != 0) {
 736                 if (*ptr++ == '\n') {
 737                         return ((char *)--ptr);
 738                 }
 739         }
 740         return (NULL);
 741 }
 742 
 743 /*
 744  * Search a newline from the end of the string
 745  */
 746 static char *


 787 
 788 /*
 789  * Do grep on a single file.
 790  * Return true in any lines matched.
 791  *
 792  * We have two strategies:
 793  * The fast one is used when we have a single pattern with
 794  * a string known to occur in the pattern. We can then
 795  * do a BMG match on the whole buffer.
 796  * This is an order of magnitude faster.
 797  * Otherwise we split the buffer into lines,
 798  * and check for a match on each line.
 799  */
 800 static int
 801 grep(int fd, const char *fn)
 802 {
 803         PATTERN *pp;
 804         off_t   data_len;       /* length of the data chunk */
 805         off_t   line_len;       /* length of the current line */
 806         off_t   line_offset;    /* current line's offset from the beginning */
 807         long long       lineno;

 808         long long       matches = 0;    /* Number of matching lines */

 809         int     newlinep;       /* 0 if the last line of file has no newline */
 810         char    *ptr, *ptrend;






 811 
 812 
 813         if (patterns == NULL)
 814                 return (0);     /* no patterns to match -- just return */
 815 
 816         pp = patterns;
 817 
 818         if (use_bmg) {
 819                 bmgcomp(pp->pattern, strlen(pp->pattern));
 820         }
 821 
 822         if (use_wchar && outline == NULL) {
 823                 outbuflen = BUFSIZE + 1;
 824                 outline = malloc(sizeof (wchar_t) * outbuflen);
 825                 if (outline == NULL) {
 826                         (void) fprintf(stderr, gettext("%s: out of memory\n"),
 827                             cmdname);
 828                         exit(2);
 829                 }
 830         }
 831 
 832         if (prntbuf == NULL) {
 833                 prntbuflen = BUFSIZE;
 834                 if ((prntbuf = malloc(prntbuflen + 1)) == NULL) {
 835                         (void) fprintf(stderr, gettext("%s: out of memory\n"),
 836                             cmdname);
 837                         exit(2);
 838                 }
 839         }
 840 
 841         line_offset = 0;









 842         lineno = 0;

 843         newlinep = 1;
 844         data_len = 0;
 845         for (; ; ) {
 846                 long    count;
 847                 off_t   offset = 0;


 848 
 849                 if (data_len == 0) {
 850                         /*
 851                          * If no data in the buffer, reset ptr
 852                          */
 853                         ptr = prntbuf;


 854                 }
 855                 if (ptr == prntbuf) {
 856                         /*
 857                          * The current data chunk starts from prntbuf.
 858                          * This means either the buffer has no data
 859                          * or the buffer has no newline.
 860                          * So, read more data from input.
 861                          */
 862                         count = read(fd, ptr + data_len, prntbuflen - data_len);
 863                         if (count < 0) {
 864                                 /* read error */
 865                                 if (cflag) {
 866                                         if (outfn && !rflag) {
 867                                                 (void) fprintf(stdout,
 868                                                     "%s:", fn);
 869                                         }
 870                                         if (!qflag && !rflag) {
 871                                                 (void) fprintf(stdout, "%lld\n",
 872                                                     matches);
 873                                         }
 874                                 }
 875                                 return (0);
 876                         } else if (count == 0) {
 877                                 /* no new data */





 878                                 if (data_len == 0) {
 879                                         /* end of file already reached */
 880                                         break;




 881                                 }

 882                                 /* last line of file has no newline */
 883                                 ptrend = ptr + data_len;
 884                                 newlinep = 0;
 885                                 goto L_start_process;
 886                         }
 887                         offset = data_len;
 888                         data_len += count;
 889                 }
 890 
 891                 /*
 892                  * Look for newline in the chunk
 893                  * between ptr + offset and ptr + data_len - offset.
 894                  */
 895                 ptrend = find_nl(ptr + offset, data_len - offset);
 896                 if (ptrend == NULL) {
 897                         /* no newline found in this chunk */
 898                         if (ptr > prntbuf) {
 899                                 /*
 900                                  * Move remaining data to the beginning
 901                                  * of the buffer.
 902                                  * Remaining data lie from ptr for
 903                                  * data_len bytes.
 904                                  */
 905                                 (void) memmove(prntbuf, ptr, data_len);
 906                         }
 907                         if (data_len == prntbuflen) {
 908                                 /*
 909                                  * No enough room in the buffer
 910                                  */
 911                                 prntbuflen += BUFSIZE;
 912                                 prntbuf = realloc(prntbuf, prntbuflen + 1);
 913                                 if (prntbuf == NULL) {
 914                                         (void) fprintf(stderr,
 915                                             gettext("%s: out of memory\n"),
 916                                             cmdname);
 917                                         exit(2);
 918                                 }
 919                         }
 920                         ptr = prntbuf;
 921                         /* read the next input */
 922                         continue;
 923                 }
 924 L_start_process:
 925 
 926                 /*
 927                  * Beginning of the chunk:      ptr
 928                  * End of the chunk:            ptr + data_len
 929                  * Beginning of the line:       ptr


1023         "%s: input file \"%s\": line %lld: invalid multibyte character\n"),
1024                                     cmdname, fn, lineno);
1025                                 /* never match a line with invalid sequence */
1026                                 goto L_skip_line;
1027                         }
1028                         outline[len] = L'\0';
1029 
1030                         if (iflag) {
1031                                 wchar_t *cp;
1032                                 for (cp = outline; *cp != '\0'; cp++) {
1033                                         *cp = towlower((wint_t)*cp);
1034                                 }
1035                         }
1036 
1037                         if (xflag) {
1038                                 for (pp = patterns; pp; pp = pp->next) {
1039                                         if (outline[0] == pp->wpattern[0] &&
1040                                             wcscmp(outline,
1041                                             pp->wpattern) == 0) {
1042                                                 /* matched */

1043                                                 break;
1044                                         }
1045                                 }
1046                         } else {
1047                                 for (pp = patterns; pp; pp = pp->next) {
1048                                         if (wcswcs(outline, pp->wpattern)
1049                                             != NULL) {
1050                                                 /* matched */

1051                                                 break;
1052                                         }
1053                                 }
1054                         }
1055                 } else if (Fflag) {
1056                         /* fgrep in byte-oriented handling */
1057                         char    *fptr;
1058                         if (iflag) {
1059                                 fptr = istrdup(ptr);
1060                         } else {
1061                                 fptr = ptr;
1062                         }
1063                         if (xflag) {
1064                                 /* fgrep -x */
1065                                 for (pp = patterns; pp; pp = pp->next) {
1066                                         if (fptr[0] == pp->pattern[0] &&
1067                                             strcmp(fptr, pp->pattern) == 0) {
1068                                                 /* matched */

1069                                                 break;
1070                                         }
1071                                 }
1072                         } else {
1073                                 for (pp = patterns; pp; pp = pp->next) {
1074                                         if (strstr(fptr, pp->pattern) != NULL) {
1075                                                 /* matched */

1076                                                 break;
1077                                         }
1078                                 }
1079                         }
1080                 } else {
1081                         /* grep or egrep */
1082                         for (pp = patterns; pp; pp = pp->next) {
1083                                 int     rv;
1084 
1085                                 rv = regexec(&pp->re, ptr, 0, NULL, 0);
1086                                 if (rv == REG_OK) {
1087                                         /* matched */
1088                                         break;
1089                                 }
1090 
1091                                 switch (rv) {
1092                                 case REG_NOMATCH:
1093                                         break;
1094                                 case REG_ECHAR:
1095                                         (void) fprintf(stderr, gettext(
1096             "%s: input file \"%s\": line %lld: invalid multibyte character\n"),
1097                                             cmdname, fn, lineno);
1098                                         break;
1099                                 default:
1100                                         (void) regerror(rv, &pp->re, errstr,
1101                                             sizeof (errstr));
1102                                         (void) fprintf(stderr, gettext(
1103             "%s: input file \"%s\": line %lld: %s\n"),
1104                                             cmdname, fn, lineno, errstr);
1105                                         exit(2);
1106                                 }
1107                         }
1108                 }
1109 











































































































1110 L_next_line:
1111                 /*
1112                  * Here, if pp points to non-NULL, something has been matched
1113                  * to the pattern.
1114                  */
1115                 if (nvflag == (pp != NULL)) {
1116                         matches++;




1117                         /*






















































1118                          * Handle q, l, and c flags.
1119                          */
1120                         if (qflag) {
1121                                 /* no need to continue */
1122                                 /*
1123                                  * End of this line is ptrend.
1124                                  * We have read up to ptr + data_len.
1125                                  */
1126                                 off_t   pos;
1127                                 pos = ptr + data_len - (ptrend + 1);
1128                                 (void) lseek(fd, -pos, SEEK_CUR);
1129                                 exit(0);
1130                         }
1131                         if (lflag) {
1132                                 (void) printf("%s\n", fn);
1133                                 break;
1134                         }
1135                         if (!cflag) {
1136                                 if (Hflag || outfn) {
1137                                         (void) printf("%s:", fn);
1138                                 }
1139                                 if (bflag) {
1140                                         (void) printf("%lld:", (offset_t)
1141                                             (line_offset / BSIZE));
1142                                 }
1143                                 if (nflag) {
1144                                         (void) printf("%lld:", lineno);

1145                                 }
1146                                 *ptrend = '\n';
1147                                 (void) fwrite(ptr, 1, line_len + 1, stdout);
1148                         }
1149                         if (ferror(stdout)) {
1150                                 return (0);
1151                         }




1152                 }





























1153 L_skip_line:
1154                 if (!newlinep)
1155                         break;
1156 
1157                 data_len -= line_len + 1;
1158                 line_offset += line_len + 1;
1159                 ptr = ptrend + 1;
1160         }
1161 

1162         if (cflag) {
1163                 if (Hflag || outfn) {
1164                         (void) printf("%s:", fn);
1165                 }
1166                 if (!qflag) {
1167                         (void) printf("%lld\n", matches);
1168                 }
1169         }
1170         return (matches != 0);
1171 }
1172 
1173 /*
1174  * usage message for grep
1175  */
1176 static void
1177 usage(void)
1178 {
1179         if (egrep || fgrep) {
1180                 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1181                 (void) fprintf(stderr,
1182                     gettext(" [-c|-l|-q] [-r|-R] [-bhHinsvx] "
1183                     "pattern_list [file ...]\n"));
1184 
1185                 (void) fprintf(stderr, "\t%s", cmdname);
1186                 (void) fprintf(stderr,
1187                     gettext(" [-c|-l|-q] [-r|-R] [-bhHinsvx] "
1188                     "[-e pattern_list]... "
1189                     "[-f pattern_file]... [file...]\n"));
1190         } else {
1191                 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1192                 (void) fprintf(stderr,
1193                     gettext(" [-c|-l|-q] [-r|-R] [-bhHinsvwx] "
1194                     "pattern_list [file ...]\n"));
1195 
1196                 (void) fprintf(stderr, "\t%s", cmdname);
1197                 (void) fprintf(stderr,
1198                     gettext(" [-c|-l|-q] [-r|-R] [-bhHinsvwx] "
1199                     "[-e pattern_list]... "
1200                     "[-f pattern_file]... [file...]\n"));
1201 
1202                 (void) fprintf(stderr, "\t%s", cmdname);
1203                 (void) fprintf(stderr,
1204                     gettext(" -E [-c|-l|-q] [-r|-R] [-bhHinsvx] "
1205                     "pattern_list [file ...]\n"));
1206 
1207                 (void) fprintf(stderr, "\t%s", cmdname);
1208                 (void) fprintf(stderr,
1209                     gettext(" -E [-c|-l|-q] [-r|-R] [-bhHinsvx] "
1210                     "[-e pattern_list]... "
1211                     "[-f pattern_file]... [file...]\n"));
1212 
1213                 (void) fprintf(stderr, "\t%s", cmdname);
1214                 (void) fprintf(stderr,
1215                     gettext(" -F [-c|-l|-q] [-r|-R] [-bhHinsvx] "
1216                     "pattern_list [file ...]\n"));
1217 
1218                 (void) fprintf(stderr, "\t%s", cmdname);
1219                 (void) fprintf(stderr,
1220                     gettext(" -F [-c|-l|-q] [-bhHinsvx] [-e pattern_list]... "

1221                     "[-f pattern_file]... [file...]\n"));
1222         }
1223         exit(2);
1224         /* NOTREACHED */
1225 }
1226 
1227 /*
1228  * Compile literal pattern into BMG tables
1229  */
1230 static void
1231 bmgcomp(char *pat, int len)
1232 {
1233         int     i;
1234         int     tlen;
1235         unsigned char   *uc = (unsigned char *)pat;
1236 
1237         bmglen = len;
1238         bmgpat = pat;
1239 
1240         for (i = 0; i < M_CSETSIZE; i++) {




  47 #include <regex.h>
  48 #include <limits.h>
  49 #include <sys/types.h>
  50 #include <sys/stat.h>
  51 #include <fcntl.h>
  52 #include <stdio.h>
  53 #include <locale.h>
  54 #include <wchar.h>
  55 #include <errno.h>
  56 #include <unistd.h>
  57 #include <wctype.h>
  58 #include <ftw.h>
  59 #include <sys/param.h>
  60 
  61 #define STDIN_FILENAME gettext("(standard input)")
  62 
  63 #define BSIZE           512             /* Size of block for -b */
  64 #define BUFSIZE         8192            /* Input buffer size */
  65 #define MAX_DEPTH       1000            /* how deep to recurse */
  66 
  67 #define AFTER   1                       /* 'After' Context */
  68 #define BEFORE  2                       /* 'Before' Context */
  69 #define CONTEXT (AFTER|BEFORE)          /* Full Context */
  70 
  71 #define M_CSETSIZE      256             /* singlebyte chars */
  72 static int      bmglen;                 /* length of BMG pattern */
  73 static char     *bmgpat;                /* BMG pattern */
  74 static int      bmgtab[M_CSETSIZE];     /* BMG delta1 table */
  75 
  76 typedef struct  _PATTERN        {
  77         char    *pattern;               /* original pattern */
  78         wchar_t *wpattern;              /* wide, lowercased pattern */
  79         struct  _PATTERN        *next;
  80         regex_t re;                     /* compiled pattern */
  81 } PATTERN;
  82 
  83 static PATTERN  *patterns;
  84 static char     errstr[128];            /* regerror string buffer */
  85 static int      regflags = 0;           /* regcomp options */
  86 static int      matched = 0;            /* return of the grep() */
  87 static int      errors = 0;             /* count of errors */
  88 static uchar_t  fgrep = 0;              /* Invoked as fgrep */
  89 static uchar_t  egrep = 0;              /* Invoked as egrep */
  90 static uchar_t  nvflag = 1;             /* Print matching lines */
  91 static uchar_t  cflag;                  /* Count of matches */
  92 static uchar_t  iflag;                  /* Case insensitve matching */
  93 static uchar_t  Hflag;                  /* Precede lines by file name */
  94 static uchar_t  hflag;                  /* Supress printing of filename */
  95 static uchar_t  lflag;                  /* Print file names of matches */
  96 static uchar_t  nflag;                  /* Precede lines by line number */
  97 static uchar_t  rflag;                  /* Search directories recursively */
  98 static uchar_t  bflag;                  /* Preccede matches by block number */
  99 static uchar_t  sflag;                  /* Suppress file error messages */
 100 static uchar_t  qflag;                  /* Suppress standard output */
 101 static uchar_t  wflag;                  /* Search for expression as a word */
 102 static uchar_t  xflag;                  /* Anchoring */
 103 static uchar_t  Eflag;                  /* Egrep or -E flag */
 104 static uchar_t  Fflag;                  /* Fgrep or -F flag */
 105 static uchar_t  Rflag;                  /* Like rflag, but follow symlinks */
 106 static uchar_t  outfn;                  /* Put out file name */
 107 static uchar_t  conflag;                /* show context of matches */
 108 static char     *cmdname;
 109 
 110 static int      use_wchar, use_bmg, mblocale;
 111 
 112 static size_t   outbuflen, prntbuflen, conbuflen;
 113 static unsigned long    conalen, conblen, conmatches;
 114 static char     *prntbuf, *conbuf;
 115 static wchar_t  *outline;
 116 
 117 static void     addfile(const char *fn);
 118 static void     addpattern(char *s);
 119 static void     fixpatterns(void);
 120 static void     usage(void);
 121 static int      grep(int, const char *);
 122 static void     bmgcomp(char *, int);
 123 static char     *bmgexec(char *, char *);
 124 static int      recursive(const char *, const struct stat *, int, struct FTW *);
 125 static void     process_path(const char *);
 126 static void     process_file(const char *, int);
 127 
 128 /*
 129  * mainline for grep
 130  */
 131 int
 132 main(int argc, char **argv)
 133 {
 134         char    *ap, *test;
 135         int     c;
 136         int     fflag = 0;
 137         int     i, n_pattern = 0, n_file = 0;
 138         char    **pattern_list = NULL;
 139         char    **file_list = NULL;
 140 
 141         (void) setlocale(LC_ALL, "");
 142 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 143 #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it weren't */
 144 #endif
 145         (void) textdomain(TEXT_DOMAIN);
 146 
 147         /*
 148          * true if this is running on the multibyte locale
 149          */
 150         mblocale = (MB_CUR_MAX > 1);
 151         /*
 152          * Skip leading slashes
 153          */
 154         cmdname = argv[0];
 155         if (ap = strrchr(cmdname, '/'))
 156                 cmdname = ap + 1;
 157 
 158         ap = cmdname;
 159         /*
 160          * Detect egrep/fgrep via command name, map to -E and -F options.
 161          */
 162         if (*ap == 'e' || *ap == 'E') {
 163                 regflags |= REG_EXTENDED;
 164                 egrep++;
 165         } else {
 166                 if (*ap == 'f' || *ap == 'F') {
 167                         fgrep++;
 168                 }
 169         }
 170 
 171         /* check for non-standard "-line-count" option */
 172         for (i = 1; i < argc; i++) {
 173                 if (strcmp(argv[i], "--") == 0)
 174                         break;
 175 
 176                 if ((argv[i][0] == '-') && isdigit(argv[i][1])) {
 177                         if (strlen(&argv[i][1]) !=
 178                             strspn(&argv[i][1], "0123456789")) {
 179                                 (void) fprintf(stderr, gettext(
 180                                     "%s: Bad number flag\n"), argv[0]);
 181                                 usage();
 182                         }
 183 
 184                         conalen = conblen = strtoul(&argv[i][1], (char **)NULL,
 185                             10);
 186 
 187                         /* isdigit() check prevents negative arguments */
 188                         if (conalen >= ULONG_MAX) {
 189                                 (void) fprintf(stderr, gettext(
 190                                     "%s: Bad context argument\n"), argv[0]);
 191                         }
 192 
 193                         if (conalen)
 194                                 conflag = CONTEXT;
 195 
 196                         while (i < argc) {
 197                                 argv[i] = argv[i + 1];
 198                                 i++;
 199                         }
 200                         argc--;
 201                 }
 202         }
 203 
 204         while ((c = getopt(argc, argv, "vwchHilnrbse:f:qxEFIRA:B:C:")) != EOF) {
 205                 unsigned long tval;
 206                 switch (c) {
 207                 case 'v':       /* POSIX: negate matches */
 208                         nvflag = 0;
 209                         break;
 210 
 211                 case 'c':       /* POSIX: write count */
 212                         cflag++;
 213                         break;
 214 
 215                 case 'i':       /* POSIX: ignore case */
 216                         iflag++;
 217                         regflags |= REG_ICASE;
 218                         break;
 219 
 220                 case 'l':       /* POSIX: Write filenames only */
 221                         lflag++;
 222                         break;
 223 
 224                 case 'n':       /* POSIX: Write line numbers */
 225                         nflag++;


 285 
 286                 case 'x':       /* POSIX: full line matches */
 287                         xflag++;
 288                         regflags |= REG_ANCHOR;
 289                         break;
 290 
 291                 case 'E':       /* POSIX: Extended RE's */
 292                         regflags |= REG_EXTENDED;
 293                         Eflag++;
 294                         break;
 295 
 296                 case 'F':       /* POSIX: strings, not RE's */
 297                         Fflag++;
 298                         break;
 299 
 300                 case 'R':       /* Solaris: like rflag, but follow symlinks */
 301                         Rflag++;
 302                         rflag++;
 303                         break;
 304 
 305                 case 'A':       /* print N lines after each match */
 306                         conalen = strtoul(optarg, &test, 10);
 307                         /* *test will be non-null if optarg is negative */
 308                         if (*test != '\0' || conalen >= ULONG_MAX) {
 309                                 (void) fprintf(stderr, gettext(
 310                                     "%s: Bad context argument\n"), argv[0]);
 311                                 exit(2);
 312                         }
 313                         if (conalen)
 314                                 conflag |= AFTER;
 315                         else
 316                                 conflag &= ~AFTER;
 317                         break;
 318                 case 'B':       /* print N lines before each match */
 319                         conblen = strtoul(optarg, &test, 10);
 320                         /* *test will be non-null if optarg is negative */
 321                         if (*test != '\0' || conblen >= ULONG_MAX) {
 322                                 (void) fprintf(stderr, gettext(
 323                                     "%s: Bad context argument\n"), argv[0]);
 324                                 exit(2);
 325                         }
 326                         if (conblen)
 327                                 conflag |= BEFORE;
 328                         else
 329                                 conflag &= ~BEFORE;
 330                         break;
 331                 case 'C':       /* print N lines around each match */
 332                         tval = strtoul(optarg, &test, 10);
 333                         /* *test will be non-null if optarg is negative */
 334                         if (*test != '\0' || tval >= ULONG_MAX) {
 335                                 (void) fprintf(stderr, gettext(
 336                                     "%s: Bad context argument\n"), argv[0]);
 337                                 exit(2);
 338                         }
 339                         if (tval) {
 340                                 if (!(conflag & BEFORE))
 341                                         conblen = tval;
 342                                 if (!(conflag & AFTER))
 343                                         conalen = tval;
 344                                 conflag = CONTEXT;
 345                         }
 346                         break;
 347 
 348                 default:
 349                         usage();
 350                 }
 351         }
 352         /*
 353          * If we're invoked as egrep or fgrep we need to do some checks
 354          */
 355 
 356         if (egrep || fgrep) {
 357                 /*
 358                  * Use of -E or -F with egrep or fgrep is illegal
 359                  */
 360                 if (Eflag || Fflag)
 361                         usage();
 362                 /*
 363                  * Don't allow use of wflag with egrep / fgrep
 364                  */
 365                 if (wflag)
 366                         usage();
 367                 /*


 608                          */
 609                         bufsiz += BUFSIZE;
 610                         if ((inbuf = realloc(inbuf, bufsiz)) == NULL) {
 611                                 (void) fprintf(stderr,
 612                                     gettext("%s: out of memory\n"),
 613                                     cmdname);
 614                                 exit(2);
 615                         }
 616                         bufp = inbuf + bufused;
 617                         continue;
 618                 }
 619                 if (bufp[buflen - 1] == '\n') {
 620                         bufp[--buflen] = '\0';
 621                 }
 622                 addpattern(inbuf);
 623 
 624                 bufp = inbuf;
 625                 bufused = 0;
 626         }
 627         free(inbuf);
 628         free(prntbuf);
 629         free(conbuf);
 630         (void) fclose(fp);
 631 }
 632 
 633 /*
 634  * Add a string to the pattern list.
 635  */
 636 static void
 637 addpattern(char *s)
 638 {
 639         PATTERN *pp;
 640         char    *wordbuf;
 641         char    *np;
 642 
 643         for (; ; ) {
 644                 np = strchr(s, '\n');
 645                 if (np != NULL)
 646                         *np = '\0';
 647                 if ((pp = malloc(sizeof (PATTERN))) == NULL) {
 648                         (void) fprintf(stderr, gettext(
 649                             "%s: out of memory\n"),


 786                 if ((rv = regcomp(&pp->re, pp->pattern, regflags)) != 0) {
 787                         (void) regerror(rv, &pp->re, errstr, sizeof (errstr));
 788                         (void) fprintf(stderr,
 789                             gettext("%s: RE error in %s: %s\n"),
 790                             cmdname, pp->pattern, errstr);
 791                         exit(2);
 792                 }
 793                 free(pp->pattern);
 794         }
 795 
 796         /*
 797          * Decide if we are able to run the Boyer-Moore-Gosper algorithm.
 798          * Use the Boyer-Moore-Gosper algorithm if:
 799          * - fgrep                      (Fflag)
 800          * - singlebyte locale          (!mblocale)
 801          * - no ignoring case           (!iflag)
 802          * - no printing line numbers   (!nflag)
 803          * - no negating the output     (nvflag)
 804          * - only one pattern           (npatterns == 1)
 805          * - non zero length pattern    (strlen(patterns->pattern) != 0)
 806          * - no context required        (!conflag)
 807          *
 808          * It's guaranteed patterns->pattern is still alive
 809          * when Fflag && !mblocale.
 810          */
 811         use_bmg = Fflag && !mblocale && !iflag && !nflag && nvflag &&
 812             (npatterns == 1) && (strlen(patterns->pattern) != 0) && !conflag;
 813 }
 814 
 815 /*
 816  * Search a newline from the beginning of the string
 817  */
 818 static char *
 819 find_nl(const char *ptr, size_t len)
 820 {
 821         while (len-- != 0) {
 822                 if (*ptr++ == '\n') {
 823                         return ((char *)--ptr);
 824                 }
 825         }
 826         return (NULL);
 827 }
 828 
 829 /*
 830  * Search a newline from the end of the string
 831  */
 832 static char *


 873 
 874 /*
 875  * Do grep on a single file.
 876  * Return true in any lines matched.
 877  *
 878  * We have two strategies:
 879  * The fast one is used when we have a single pattern with
 880  * a string known to occur in the pattern. We can then
 881  * do a BMG match on the whole buffer.
 882  * This is an order of magnitude faster.
 883  * Otherwise we split the buffer into lines,
 884  * and check for a match on each line.
 885  */
 886 static int
 887 grep(int fd, const char *fn)
 888 {
 889         PATTERN *pp;
 890         off_t   data_len;       /* length of the data chunk */
 891         off_t   line_len;       /* length of the current line */
 892         off_t   line_offset;    /* current line's offset from the beginning */
 893         off_t   blkoffset;      /* line_offset but context-compatible */
 894         long long       lineno, linenum;
 895         long long       matches = 0;    /* Number of matching lines */
 896         long long       conacnt = 0, conbcnt = 0;       /* context line count */
 897         int     newlinep;       /* 0 if the last line of file has no newline */
 898         char    *ptr, *ptrend, *prntptr, *prntptrend;
 899         char    *nextptr = NULL, *nextend = NULL;
 900         char    *conptr = NULL, *conptrend = NULL;
 901         char    *matchptr = NULL;
 902         int     conaprnt = 0, conbprnt = 0, lastmatch = 0;
 903         int     nearmatch = conmatches ? 1 : 0; /* w/in N+1 of last match */
 904         size_t  prntlen;
 905 

 906         if (patterns == NULL)
 907                 return (0);     /* no patterns to match -- just return */
 908 
 909         pp = patterns;
 910 
 911         if (use_bmg) {
 912                 bmgcomp(pp->pattern, strlen(pp->pattern));
 913         }
 914 
 915         if (use_wchar && outline == NULL) {
 916                 outbuflen = BUFSIZE + 1;
 917                 outline = malloc(sizeof (wchar_t) * outbuflen);
 918                 if (outline == NULL) {
 919                         (void) fprintf(stderr, gettext("%s: out of memory\n"),
 920                             cmdname);
 921                         exit(2);
 922                 }
 923         }
 924 
 925         if (prntbuf == NULL) {
 926                 prntbuflen = BUFSIZE;
 927                 if ((prntbuf = malloc(prntbuflen + 1)) == NULL) {
 928                         (void) fprintf(stderr, gettext("%s: out of memory\n"),
 929                             cmdname);
 930                         exit(2);
 931                 }
 932         }
 933 
 934         if (conflag && (conbuf == NULL)) {
 935                 conbuflen = BUFSIZE;
 936                 if ((conbuf = malloc(BUFSIZE+1)) == NULL) {
 937                         (void) fprintf(stderr, gettext("%s: out of memory\n"),
 938                             cmdname);
 939                         exit(2);
 940                 }
 941         }
 942 
 943         blkoffset = line_offset = 0;
 944         lineno = 0;
 945         linenum = 1;
 946         newlinep = 1;
 947         data_len = 0;
 948         for (; ; ) {
 949                 long    count;
 950                 off_t   offset = 0;
 951                 int     eof = 0, rv = REG_NOMATCH;
 952                 char    separate;
 953 
 954                 if (data_len == 0) {
 955                         /*
 956                          * If no data in the buffer, reset ptr
 957                          */
 958                         ptr = prntbuf;
 959                         if (conptr == NULL)
 960                                 conptrend = conptr = conbuf;
 961                 }
 962                 if (ptr == prntbuf) {
 963                         /*
 964                          * The current data chunk starts from prntbuf.
 965                          * This means either the buffer has no data
 966                          * or the buffer has no newline.
 967                          * So, read more data from input.
 968                          */
 969                         count = read(fd, ptr + data_len, prntbuflen - data_len);
 970                         if (count < 0) {
 971                                 /* read error */
 972                                 if (cflag) {
 973                                         if (outfn && !rflag) {
 974                                                 (void) fprintf(stdout,
 975                                                     "%s:", fn);
 976                                         }
 977                                         if (!qflag && !rflag) {
 978                                                 (void) fprintf(stdout, "%lld\n",
 979                                                     matches);
 980                                         }
 981                                 }
 982                                 return (0);
 983                         } else if (count == 0) {
 984                                 /* no new data */
 985                                 eof = 1;
 986 
 987                                 /* we never want to match EOF */
 988                                 pp = (PATTERN *) !nvflag;
 989 
 990                                 if (data_len == 0) {
 991                                         /* end of file already reached */
 992                                         if (conflag) {
 993                                                 *conptrend = '\n';
 994                                                 goto L_next_line;
 995                                         } else {
 996                                                 goto out;
 997                                         }
 998                                 }
 999                                 /* last line of file has no newline */
1000                                 ptrend = ptr + data_len;
1001                                 newlinep = 0;
1002                                 goto L_start_process;
1003                         }
1004                         offset = data_len;
1005                         data_len += count;
1006                 }
1007 
1008                 /*
1009                  * Look for newline in the chunk
1010                  * between ptr + offset and ptr + data_len - offset.
1011                  */
1012                 ptrend = find_nl(ptr + offset, data_len - offset);
1013                 if (ptrend == NULL) {
1014                         /* no newline found in this chunk */
1015                         if (ptr > prntbuf) {
1016                                 /*
1017                                  * Move remaining data to the beginning
1018                                  * of the buffer.
1019                                  * Remaining data lie from ptr for
1020                                  * data_len bytes.
1021                                  */
1022                                 (void) memmove(prntbuf, ptr, data_len);
1023                         }
1024                         if (data_len == prntbuflen) {
1025                                 /*
1026                                  * Not enough room in the buffer
1027                                  */
1028                                 prntbuflen += BUFSIZE;
1029                                 prntbuf = realloc(prntbuf, prntbuflen + 1);
1030                                 if (prntbuf == NULL) {
1031                                         (void) fprintf(stderr,
1032                                             gettext("%s: out of memory\n"),
1033                                             cmdname);
1034                                         exit(2);
1035                                 }
1036                         }
1037                         ptr = prntbuf;
1038                         /* read the next input */
1039                         continue;
1040                 }
1041 L_start_process:
1042 
1043                 /*
1044                  * Beginning of the chunk:      ptr
1045                  * End of the chunk:            ptr + data_len
1046                  * Beginning of the line:       ptr


1140         "%s: input file \"%s\": line %lld: invalid multibyte character\n"),
1141                                     cmdname, fn, lineno);
1142                                 /* never match a line with invalid sequence */
1143                                 goto L_skip_line;
1144                         }
1145                         outline[len] = L'\0';
1146 
1147                         if (iflag) {
1148                                 wchar_t *cp;
1149                                 for (cp = outline; *cp != '\0'; cp++) {
1150                                         *cp = towlower((wint_t)*cp);
1151                                 }
1152                         }
1153 
1154                         if (xflag) {
1155                                 for (pp = patterns; pp; pp = pp->next) {
1156                                         if (outline[0] == pp->wpattern[0] &&
1157                                             wcscmp(outline,
1158                                             pp->wpattern) == 0) {
1159                                                 /* matched */
1160                                                 rv = REG_OK;
1161                                                 break;
1162                                         }
1163                                 }
1164                         } else {
1165                                 for (pp = patterns; pp; pp = pp->next) {
1166                                         if (wcswcs(outline, pp->wpattern)
1167                                             != NULL) {
1168                                                 /* matched */
1169                                                 rv = REG_OK;
1170                                                 break;
1171                                         }
1172                                 }
1173                         }
1174                 } else if (Fflag) {
1175                         /* fgrep in byte-oriented handling */
1176                         char    *fptr;
1177                         if (iflag) {
1178                                 fptr = istrdup(ptr);
1179                         } else {
1180                                 fptr = ptr;
1181                         }
1182                         if (xflag) {
1183                                 /* fgrep -x */
1184                                 for (pp = patterns; pp; pp = pp->next) {
1185                                         if (fptr[0] == pp->pattern[0] &&
1186                                             strcmp(fptr, pp->pattern) == 0) {
1187                                                 /* matched */
1188                                                 rv = REG_OK;
1189                                                 break;
1190                                         }
1191                                 }
1192                         } else {
1193                                 for (pp = patterns; pp; pp = pp->next) {
1194                                         if (strstr(fptr, pp->pattern) != NULL) {
1195                                                 /* matched */
1196                                                 rv = REG_OK;
1197                                                 break;
1198                                         }
1199                                 }
1200                         }
1201                 } else {
1202                         /* grep or egrep */
1203                         for (pp = patterns; pp; pp = pp->next) {


1204                                 rv = regexec(&pp->re, ptr, 0, NULL, 0);
1205                                 if (rv == REG_OK) {
1206                                         /* matched */
1207                                         break;
1208                                 }
1209 
1210                                 switch (rv) {
1211                                 case REG_NOMATCH:
1212                                         break;
1213                                 case REG_ECHAR:
1214                                         (void) fprintf(stderr, gettext(
1215             "%s: input file \"%s\": line %lld: invalid multibyte character\n"),
1216                                             cmdname, fn, lineno);
1217                                         break;
1218                                 default:
1219                                         (void) regerror(rv, &pp->re, errstr,
1220                                             sizeof (errstr));
1221                                         (void) fprintf(stderr, gettext(
1222             "%s: input file \"%s\": line %lld: %s\n"),
1223                                             cmdname, fn, lineno, errstr);
1224                                         exit(2);
1225                                 }
1226                         }
1227                 }
1228 
1229                 /*
1230                  * Context is set up as follows:
1231                  * For a 'Before' context, we maintain a set of pointers
1232                  * containing 'N' lines of context. If the current number of
1233                  * lines contained is greater than N, and N isn't a match, the
1234                  * start pointer is moved forward to the next newline.
1235                  *
1236                  * If we ever find a match, we print out immediately.
1237                  * 'nearmatch' tells us if we're within N+1 lines of the last
1238                  * match ; if we are, and we find another match, we don't
1239                  * separate the matches. 'nearmatch' becomes false when
1240                  * a line gets rotated out of the context.
1241                  *
1242                  * For an 'After' context, we simply wait until we've found a
1243                  * match, then create a context N+1 lines big. If we don't find
1244                  * a match within the context, we print out the current context.
1245                  * Otherwise, we save a reference to the new matching line,
1246                  * print out the other context, and reset our context pointers
1247                  * to the new matching line.
1248                  *
1249                  * 'nearmatch' becomes false when we find a non-matching line
1250                  * that isn't a part of any context.
1251                  *
1252                  * A full-context is implemented as a combination of the
1253                  * 'Before' and 'After' context logic. Before we find a match,
1254                  * we follow the Before logic. When we find a match, we
1255                  * follow the After logic. 'nearmatch' is handled by the Before
1256                  * logic.
1257                  */
1258 
1259                 if (!conflag)
1260                         goto L_next_line;
1261 
1262                 if (line_len + (conptrend - conbuf) > conbuflen) {
1263                         char *oldconbuf = conbuf;
1264                         char *oldconptr = conptr;
1265                         long tmp = matchptr - conptr;
1266 
1267                         conbuflen += BUFSIZE;
1268                         conbuf = realloc(conbuf, conbuflen + 1);
1269                         if (conbuf == NULL) {
1270                                 (void) fprintf(stderr,
1271                                     gettext("%s: out of memory\n"),
1272                                     cmdname);
1273                                 exit(2);
1274                         }
1275 
1276                         conptr = conbuf + (conptr - oldconbuf);
1277                         conptrend = conptr + (conptrend - oldconptr);
1278                         if (matchptr)
1279                                 matchptr = conptr + tmp;
1280                 }
1281                 (void) memcpy((conptrend > conptr) ?
1282                     conptrend + 1 : conptrend, ptr, line_len);
1283                 conptrend += line_len + (conptrend > conptr);
1284                 *conptrend = '\n';
1285 
1286                 if (!nvflag == rv) {
1287                         /* matched */
1288                         if (lastmatch) {
1289                                 if (conflag & AFTER) {
1290                                         conaprnt = 1;
1291                                         nextend = conptrend;
1292                                         conptrend = conptr + lastmatch;
1293                                         nextptr = conptrend + 1;
1294                                         *nextend = '\n';
1295                                 }
1296                         } else {
1297                                 if (conflag == AFTER) {
1298                                         conptr = conptrend - (line_len);
1299                                         linenum = lineno;
1300                                         blkoffset = line_offset;
1301                                 }
1302                                 blkoffset = line_offset -
1303                                     (conptrend - conptr - line_len);
1304                         }
1305 
1306                         if (conflag == BEFORE)
1307                                 conbprnt = 1;
1308 
1309                         lastmatch = conptrend - conptr;
1310                         goto L_next_line;
1311                 }
1312 
1313                 if (!lastmatch) {
1314                         if (conflag & BEFORE) {
1315                                 if (conbcnt >= conblen) {
1316                                         char *tmp = conptr;
1317                                         conptr = find_nl(conptr,
1318                                             conptrend - conptr) + 1;
1319                                         if (bflag)
1320                                                 blkoffset += conptr - tmp;
1321                                         linenum++;
1322                                         nearmatch = 1;
1323                                 } else {
1324                                         conbcnt++;
1325                                 }
1326                         }
1327                         if (conflag == AFTER)
1328                                 nearmatch = 1;
1329                 } else  {
1330                         if (++conacnt >= conalen && !conaprnt && conalen)
1331                                 conaprnt = 1;
1332                         else
1333                                 lastmatch = conptrend - conptr;
1334                 }
1335 
1336 L_next_line:
1337                 /*
1338                  * Here, if pp points to non-NULL, something has been matched
1339                  * to the pattern.
1340                  */
1341                 if (nvflag == (pp != NULL)) {
1342                         matches++;
1343                         if (!nextend)
1344                                 matchptr = conflag ? conptrend : ptrend;
1345                 }
1346 
1347                 /*
1348                  * Set up some print context so that we can treat
1349                  * single-line matches as a zero-N context.
1350                  * Apply CLI flags to each line of the context.
1351                  *
1352                  * For context, we only print if we both have a match and are
1353                  * either at the end of the data stream, or we've previously
1354                  * declared that we want to print for a particular context.
1355                  */
1356                 if (lastmatch && (eof || conaprnt || conbprnt)) {
1357 
1358                         /*
1359                          * We'd normally do this earlier, but we had to
1360                          * escape early because we reached the end of the data.
1361                          */
1362                         if (eof && nextptr)
1363                                 conptrend = nextend;
1364 
1365                         prntlen = conptrend - conptr + 1;
1366                         prntptrend = prntptr = conptr;
1367                         if (conmatches++ && nearmatch && !cflag)
1368                                 (void) fwrite("--\n", 1, 3, stdout);
1369                 } else if (!conflag && nvflag == (pp != NULL)) {
1370                         *ptrend = '\n';
1371                         prntlen = line_len + 1;
1372                         prntptrend = prntptr = ptr;
1373                         linenum = lineno;
1374                         blkoffset = line_offset;
1375                 } else if (eof) {
1376                         /* No match and no more data */
1377                         goto out;
1378                 } else {
1379                         /* No match, or we're not done building context */
1380                         goto L_skip_line;
1381                 }
1382 
1383                 while ((prntptrend = find_nl(prntptrend+1, prntlen)) != NULL) {
1384 
1385                         /*
1386                          * GNU grep uses '-' for context lines and ':' for
1387                          * matching lines, so replicate that here.
1388                          */
1389                         if (prntptrend == matchptr) {
1390                                 if (eof && nextptr) {
1391                                         matchptr = nextend;
1392                                         nextptr = NULL;
1393                                 } else {
1394                                         matchptr = NULL;
1395                                 }
1396                                 separate = ':';
1397                         } else {
1398                                 separate = '-';
1399                         }
1400 
1401                         /*
1402                          * Handle q, l, and c flags.
1403                          */
1404                         if (qflag) {
1405                                 /* no need to continue */
1406                                 /*
1407                                  * End of this line is ptrend.
1408                                  * We have read up to ptr + data_len.
1409                                  */
1410                                 off_t   pos;
1411                                 pos = ptr + data_len - (ptrend + 1);
1412                                 (void) lseek(fd, -pos, SEEK_CUR);
1413                                 exit(0);
1414                         }
1415                         if (lflag) {
1416                                 (void) printf("%s\n", fn);
1417                                 goto out;
1418                         }
1419                         if (!cflag) {
1420                                 if (Hflag || outfn) {
1421                                         (void) printf("%s%c", fn, separate);
1422                                 }
1423                                 if (bflag) {
1424                                         (void) printf("%lld%c", (offset_t)
1425                                             (blkoffset / BSIZE), separate);
1426                                 }
1427                                 if (nflag) {
1428                                         (void) printf("%lld%c", linenum,
1429                                             separate);
1430                                 }
1431                                 (void) fwrite(prntptr, 1,
1432                                     prntptrend - prntptr + 1, stdout);
1433                         }
1434                         if (ferror(stdout)) {
1435                                 return (0);
1436                         }
1437                         linenum++;
1438                         prntlen -= prntptrend - prntptr + 1;
1439                         blkoffset += prntptrend - prntptr + 1;
1440                         prntptr = prntptrend + 1;
1441                 }
1442 
1443                 if (eof)
1444                         goto out;
1445 
1446                 /*
1447                  * Update context buffer and variables post-print
1448                  */
1449                 if (conflag) {
1450                         conptr = conbuf;
1451                         conaprnt = conbprnt = 0;
1452                         nearmatch = 0;
1453                         conacnt = conbcnt = 0;
1454 
1455                         if (nextptr) {
1456                                 (void) memmove(conbuf, nextptr,
1457                                     nextend - nextptr + 1);
1458                                 blkoffset += nextptr - conptrend - 1;
1459                                 conptrend = conptr + (nextend - nextptr);
1460                                 matchptr = conptrend;
1461                                 linenum = lineno;
1462                                 lastmatch = conptrend - conptr;
1463                         } else {
1464                                 conptrend = conptr;
1465                                 conacnt = 0;
1466                                 lastmatch = 0;
1467                         }
1468                         nextptr = nextend = NULL;
1469                 }
1470 
1471 L_skip_line:
1472                 if (!newlinep)
1473                         break;
1474 
1475                 data_len -= line_len + 1;
1476                 line_offset += line_len + 1;
1477                 ptr = ptrend + 1;
1478         }
1479 
1480 out:
1481         if (cflag) {
1482                 if (Hflag || outfn) {
1483                         (void) printf("%s:", fn);
1484                 }
1485                 if (!qflag) {
1486                         (void) printf("%lld\n", matches);
1487                 }
1488         }
1489         return (matches != 0);
1490 }
1491 
1492 /*
1493  * usage message for grep
1494  */
1495 static void
1496 usage(void)
1497 {
1498         if (egrep || fgrep) {
1499                 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1500                 (void) fprintf(stderr,
1501                     gettext(" [-c|-l|-q] [-r|-R] [-A #|-B #|-C #|-#] "
1502                     "[-bhHinsvx] pattern_list [file ...]\n"));
1503 
1504                 (void) fprintf(stderr, "\t%s", cmdname);
1505                 (void) fprintf(stderr,
1506                     gettext(" [-c|-l|-q] [-r|-R] [-A #|-B #|-C #|-#] "
1507                     "[-bhHinsvx] [-e pattern_list]... "
1508                     "[-f pattern_file]... [file...]\n"));
1509         } else {
1510                 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1511                 (void) fprintf(stderr,
1512                     gettext(" [-c|-l|-q] [-r|-R] [-A #|-B #|-C #|-#] "
1513                     "[-bhHinsvx] pattern_list [file ...]\n"));
1514 
1515                 (void) fprintf(stderr, "\t%s", cmdname);
1516                 (void) fprintf(stderr,
1517                     gettext(" [-c|-l|-q] [-r|-R] [-A #|-B #|-C #|-#] "
1518                     "[-bhHinsvx] [-e pattern_list]... "
1519                     "[-f pattern_file]... [file...]\n"));
1520 
1521                 (void) fprintf(stderr, "\t%s", cmdname);
1522                 (void) fprintf(stderr,
1523                     gettext(" -E [-c|-l|-q] [-r|-R] [-A #|-B #|-C #|-#] "
1524                     "[-bhHinsvx] pattern_list [file ...]\n"));
1525 
1526                 (void) fprintf(stderr, "\t%s", cmdname);
1527                 (void) fprintf(stderr,
1528                     gettext(" -E [-c|-l|-q] [-r|-R] [-A #|-B #|-C #|-#] "
1529                     "[-bhHinsvx] [-e pattern_list]... "
1530                     "[-f pattern_file]... [file...]\n"));
1531 
1532                 (void) fprintf(stderr, "\t%s", cmdname);
1533                 (void) fprintf(stderr,
1534                     gettext(" -F [-c|-l|-q] [-r|-R] [-A #|-B #|-C #|-#] "
1535                     "[-bhHinsvx] pattern_list [file ...]\n"));
1536 
1537                 (void) fprintf(stderr, "\t%s", cmdname);
1538                 (void) fprintf(stderr,
1539                     gettext(" -F [-c|-l|-q] [-A #|-B #|-C #|-#] "
1540                     "[-bhHinsvx] [-e pattern_list]... "
1541                     "[-f pattern_file]... [file...]\n"));
1542         }
1543         exit(2);
1544         /* NOTREACHED */
1545 }
1546 
1547 /*
1548  * Compile literal pattern into BMG tables
1549  */
1550 static void
1551 bmgcomp(char *pat, int len)
1552 {
1553         int     i;
1554         int     tlen;
1555         unsigned char   *uc = (unsigned char *)pat;
1556 
1557         bmglen = len;
1558         bmgpat = pat;
1559 
1560         for (i = 0; i < M_CSETSIZE; i++) {