Print this page
3047 grep support for -r would be useful

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/grep_xpg4/grep.c
          +++ new/usr/src/cmd/grep_xpg4/grep.c
↓ open down ↓ 16 lines elided ↑ open up ↑
  17   17   * fields enclosed by brackets "[]" replaced with your own identifying
  18   18   * information: Portions Copyright [yyyy] [name of copyright owner]
  19   19   *
  20   20   * CDDL HEADER END
  21   21   */
  22   22  /*
  23   23   * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
  25   25   */
  26   26  
  27      -#pragma ident   "%Z%%M% %I%     %E% SMI"
  28      -
  29   27  /*
  30   28   * grep - pattern matching program - combined grep, egrep, and fgrep.
  31   29   *      Based on MKS grep command, with XCU & Solaris mods.
  32   30   */
  33   31  
  34   32  /*
  35   33   * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
  36   34   *
  37   35   */
  38   36  
       37 +/* Copyright 2012 Nexenta Systems, Inc.  All rights reserved. */
       38 +
  39   39  #include <string.h>
  40   40  #include <stdlib.h>
  41   41  #include <ctype.h>
  42   42  #include <stdarg.h>
  43   43  #include <regex.h>
  44   44  #include <limits.h>
  45   45  #include <sys/types.h>
  46   46  #include <sys/stat.h>
  47   47  #include <fcntl.h>
  48   48  #include <stdio.h>
  49   49  #include <locale.h>
  50   50  #include <wchar.h>
  51   51  #include <errno.h>
  52   52  #include <unistd.h>
  53   53  #include <wctype.h>
       54 +#include <ftw.h>
       55 +#include <sys/param.h>
  54   56  
  55   57  #define BSIZE           512             /* Size of block for -b */
  56   58  #define BUFSIZE         8192            /* Input buffer size */
       59 +#define MAX_DEPTH       1000            /* how deep to recurse */
  57   60  
  58   61  #define M_CSETSIZE      256             /* singlebyte chars */
  59   62  static int      bmglen;                 /* length of BMG pattern */
  60   63  static char     *bmgpat;                /* BMG pattern */
  61   64  static int      bmgtab[M_CSETSIZE];     /* BMG delta1 table */
  62   65  
  63   66  typedef struct  _PATTERN        {
  64   67          char    *pattern;               /* original pattern */
  65   68          wchar_t *wpattern;              /* wide, lowercased pattern */
  66   69          struct  _PATTERN        *next;
  67   70          regex_t re;                     /* compiled pattern */
  68   71  } PATTERN;
  69   72  
  70   73  static PATTERN  *patterns;
  71   74  static char     errstr[128];            /* regerror string buffer */
  72   75  static int      regflags = 0;           /* regcomp options */
       76 +static int      matched = 0;            /* return of the grep() */
       77 +static int      errors = 0;             /* count of errors */
  73   78  static uchar_t  fgrep = 0;              /* Invoked as fgrep */
  74   79  static uchar_t  egrep = 0;              /* Invoked as egrep */
  75   80  static uchar_t  nvflag = 1;             /* Print matching lines */
  76   81  static uchar_t  cflag;                  /* Count of matches */
  77   82  static uchar_t  iflag;                  /* Case insensitve matching */
  78   83  static uchar_t  hflag;                  /* Supress printing of filename */
  79   84  static uchar_t  lflag;                  /* Print file names of matches */
  80   85  static uchar_t  nflag;                  /* Precede lines by line number */
       86 +static uchar_t  rflag;                  /* Search directories recursively */
  81   87  static uchar_t  bflag;                  /* Preccede matches by block number */
  82   88  static uchar_t  sflag;                  /* Suppress file error messages */
  83   89  static uchar_t  qflag;                  /* Suppress standard output */
  84   90  static uchar_t  wflag;                  /* Search for expression as a word */
  85   91  static uchar_t  xflag;                  /* Anchoring */
  86   92  static uchar_t  Eflag;                  /* Egrep or -E flag */
  87   93  static uchar_t  Fflag;                  /* Fgrep or -F flag */
       94 +static uchar_t  Rflag;                  /* Like rflag, but follow symlinks */
  88   95  static uchar_t  outfn;                  /* Put out file name */
  89   96  static char     *cmdname;
  90   97  
  91   98  static int      use_wchar, use_bmg, mblocale;
  92   99  
  93  100  static size_t   outbuflen, prntbuflen;
  94  101  static char     *prntbuf;
  95  102  static wchar_t  *outline;
  96  103  
  97      -static void     addfile(char *fn);
      104 +static void     addfile(const char *fn);
  98  105  static void     addpattern(char *s);
  99  106  static void     fixpatterns(void);
 100  107  static void     usage(void);
 101      -static int      grep(int, char *);
      108 +static int      grep(int, const char *);
 102  109  static void     bmgcomp(char *, int);
 103  110  static char     *bmgexec(char *, char *);
      111 +static int      recursive(const char *, const struct stat *, int, struct FTW *);
      112 +static void     process_path(const char *);
      113 +static void     process_file(const char *, int);
 104  114  
 105  115  /*
 106  116   * mainline for grep
 107  117   */
 108  118  int
 109  119  main(int argc, char **argv)
 110  120  {
 111  121          char    *ap;
 112      -        int     matched = 0;
 113  122          int     c;
 114  123          int     fflag = 0;
 115      -        int     errors = 0;
 116  124          int     i, n_pattern = 0, n_file = 0;
 117  125          char    **pattern_list = NULL;
 118  126          char    **file_list = NULL;
 119  127  
 120  128          (void) setlocale(LC_ALL, "");
 121  129  #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 122  130  #define TEXT_DOMAIN     "SYS_TEST"      /* Use this only if it weren't */
 123  131  #endif
 124  132          (void) textdomain(TEXT_DOMAIN);
 125  133  
↓ open down ↓ 14 lines elided ↑ open up ↑
 140  148           */
 141  149          if (*ap == 'e' || *ap == 'E') {
 142  150                  regflags |= REG_EXTENDED;
 143  151                  egrep++;
 144  152          } else {
 145  153                  if (*ap == 'f' || *ap == 'F') {
 146  154                          fgrep++;
 147  155                  }
 148  156          }
 149  157  
 150      -        while ((c = getopt(argc, argv, "vwchilnbse:f:qxEFI")) != EOF) {
      158 +        while ((c = getopt(argc, argv, "vwchilnrbse:f:qxEFIR")) != EOF) {
 151  159                  switch (c) {
 152  160                  case 'v':       /* POSIX: negate matches */
 153  161                          nvflag = 0;
 154  162                          break;
 155  163  
 156  164                  case 'c':       /* POSIX: write count */
 157  165                          cflag++;
 158  166                          break;
 159  167  
 160  168                  case 'i':       /* POSIX: ignore case */
↓ open down ↓ 2 lines elided ↑ open up ↑
 163  171                          break;
 164  172  
 165  173                  case 'l':       /* POSIX: Write filenames only */
 166  174                          lflag++;
 167  175                          break;
 168  176  
 169  177                  case 'n':       /* POSIX: Write line numbers */
 170  178                          nflag++;
 171  179                          break;
 172  180  
      181 +                case 'r':       /* Solaris: search recursively */
      182 +                        rflag++;
      183 +                        break;
      184 +
 173  185                  case 'b':       /* Solaris: Write file block numbers */
 174  186                          bflag++;
 175  187                          break;
 176  188  
 177  189                  case 's':       /* POSIX: No error msgs for files */
 178  190                          sflag++;
 179  191                          break;
 180  192  
 181  193                  case 'e':       /* POSIX: pattern list */
 182  194                          n_pattern++;
↓ open down ↓ 40 lines elided ↑ open up ↑
 223  235  
 224  236                  case 'E':       /* POSIX: Extended RE's */
 225  237                          regflags |= REG_EXTENDED;
 226  238                          Eflag++;
 227  239                          break;
 228  240  
 229  241                  case 'F':       /* POSIX: strings, not RE's */
 230  242                          Fflag++;
 231  243                          break;
 232  244  
      245 +                case 'R':       /* Solaris: like rflag, but follow symlinks */
      246 +                        Rflag++;
      247 +                        rflag++;
      248 +                        break;
      249 +
 233  250                  default:
 234  251                          usage();
 235  252                  }
 236  253          }
 237  254          /*
 238  255           * If we're invoked as egrep or fgrep we need to do some checks
 239  256           */
 240  257  
 241  258          if (egrep || fgrep) {
 242  259                  /*
↓ open down ↓ 84 lines elided ↑ open up ↑
 327  344           */
 328  345          fixpatterns();
 329  346  
 330  347          /* Process all files: stdin, or rest of arg list */
 331  348          if (argc < 2) {
 332  349                  matched = grep(0, gettext("(standard input)"));
 333  350          } else {
 334  351                  if (argc > 2 && hflag == 0)
 335  352                          outfn = 1;      /* Print filename on match line */
 336  353                  for (argv++; *argv != NULL; argv++) {
 337      -                        int     fd;
 338      -
 339      -                        if ((fd = open(*argv, O_RDONLY)) == -1) {
 340      -                                errors = 1;
 341      -                                if (sflag)
 342      -                                        continue;
 343      -                                (void) fprintf(stderr, gettext(
 344      -                                    "%s: can't open \"%s\"\n"),
 345      -                                    cmdname, *argv);
 346      -                                continue;
 347      -                        }
 348      -                        matched |= grep(fd, *argv);
 349      -                        (void) close(fd);
 350      -                        if (ferror(stdout))
 351      -                                break;
      354 +                        process_path(*argv);
 352  355                  }
 353  356          }
 354  357          /*
 355  358           * Return() here is used instead of exit
 356  359           */
 357  360  
 358  361          (void) fflush(stdout);
 359  362  
 360  363          if (errors)
 361  364                  return (2);
 362  365          return (matched ? 0 : 1);
 363  366  }
 364  367  
      368 +static void
      369 +process_path(const char *path)
      370 +{
      371 +        struct  stat st;
      372 +        int     walkflags = FTW_CHDIR;
      373 +        char    *buf = NULL;
      374 +
      375 +        if (rflag) {
      376 +                if (stat(path, &st) != -1 &&
      377 +                    (st.st_mode & S_IFMT) == S_IFDIR) {
      378 +                        outfn = 1; /* Print filename */
      379 +
      380 +                        /*
      381 +                         * Add trailing slash if arg
      382 +                         * is directory, to resolve symlinks.
      383 +                         */
      384 +                        if (path[strlen(path) - 1] != '/') {
      385 +                                (void) asprintf(&buf, "%s/", path);
      386 +                                if (buf != NULL)
      387 +                                        path = buf;
      388 +                        }
      389 +
      390 +                        /*
      391 +                         * Search through subdirs if path is directory.
      392 +                         * Don't follow symlinks if Rflag is not set.
      393 +                         */
      394 +                        if (!Rflag)
      395 +                                walkflags |= FTW_PHYS;
      396 +
      397 +                        if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
      398 +                                if (!sflag)
      399 +                                        (void) fprintf(stderr,
      400 +                                            gettext("%s: can't open \"%s\"\n"),
      401 +                                            cmdname, path);
      402 +                                errors = 1;
      403 +                        }
      404 +                        return;
      405 +                }
      406 +        }
      407 +        process_file(path, 0);
      408 +}
      409 +
 365  410  /*
      411 + * Read and process all files in directory recursively.
      412 + */
      413 +static int
      414 +recursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
      415 +{
      416 +        /*
      417 +         * Process files and follow symlinks if Rflag set.
      418 +         */
      419 +        if (info != FTW_F) {
      420 +                /* Report broken symlinks and unreadable files */
      421 +                if (!sflag &&
      422 +                    (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
      423 +                        (void) fprintf(stderr,
      424 +                            gettext("%s: can't open \"%s\"\n"), cmdname, name);
      425 +                }
      426 +                return (0);
      427 +        }
      428 +
      429 +
      430 +        /* Skip devices and pipes if Rflag is not set */
      431 +        if (!Rflag && !S_ISREG(statp->st_mode))
      432 +                return (0);
      433 +        /* Pass offset to relative name from FTW_CHDIR */
      434 +        process_file(name, ftw->base);
      435 +        return (0);
      436 +}
      437 +
      438 +/*
      439 + * Opens file and call grep function.
      440 + */
      441 +static void
      442 +process_file(const char *name, int base)
      443 +{
      444 +        int fd;
      445 +
      446 +        if ((fd = open(name + base, O_RDONLY)) == -1) {
      447 +                errors = 1;
      448 +                if (!sflag) /* Silent mode */
      449 +                        (void) fprintf(stderr, gettext(
      450 +                            "%s: can't open \"%s\"\n"),
      451 +                            cmdname, name);
      452 +                return;
      453 +        }
      454 +        matched |= grep(fd, name);
      455 +        (void) close(fd);
      456 +
      457 +        if (ferror(stdout)) {
      458 +                (void) fprintf(stderr, gettext(
      459 +                    "%s: error writing to stdout\n"),
      460 +                    cmdname);
      461 +                (void) fflush(stdout);
      462 +                exit(2);
      463 +        }
      464 +
      465 +}
      466 +
      467 +/*
 366  468   * Add a file of strings to the pattern list.
 367  469   */
 368  470  static void
 369      -addfile(char *fn)
      471 +addfile(const char *fn)
 370  472  {
 371  473          FILE    *fp;
 372  474          char    *inbuf;
 373  475          char    *bufp;
 374  476          size_t  bufsiz, buflen, bufused;
 375  477  
 376  478          /*
 377  479           * Open the pattern file
 378  480           */
 379  481          if ((fp = fopen(fn, "r")) == NULL) {
↓ open down ↓ 141 lines elided ↑ open up ↑
 521  623  
 522  624                  if (Fflag) {
 523  625                          if (use_wchar) {
 524  626                                  /*
 525  627                                   * Fflag && mblocale && iflag
 526  628                                   * Fflag && mblocale && !xflag
 527  629                                   */
 528  630                                  size_t  n;
 529  631                                  n = strlen(pp->pattern) + 1;
 530  632                                  if ((pp->wpattern =
 531      -                                        malloc(sizeof (wchar_t) * n)) == NULL) {
      633 +                                    malloc(sizeof (wchar_t) * n)) == NULL) {
 532  634                                          (void) fprintf(stderr,
 533  635                                              gettext("%s: out of memory\n"),
 534  636                                              cmdname);
 535  637                                          exit(2);
 536  638                                  }
 537  639                                  if (mbstowcs(pp->wpattern, pp->pattern, n) ==
 538  640                                      (size_t)-1) {
 539  641                                          (void) fprintf(stderr,
 540  642                                              gettext("%s: failed to convert "
 541      -                                                "\"%s\" to wide-characters\n"),
      643 +                                            "\"%s\" to wide-characters\n"),
 542  644                                              cmdname, pp->pattern);
 543  645                                          exit(2);
 544  646                                  }
 545  647                                  if (iflag) {
 546  648                                          wchar_t *wp;
 547  649                                          for (wp = pp->wpattern; *wp != L'\0';
 548  650                                              wp++) {
 549  651                                                  *wp = towlower((wint_t)*wp);
 550  652                                          }
 551  653                                  }
↓ open down ↓ 20 lines elided ↑ open up ↑
 572  674  
 573  675                  /*
 574  676                   * For non-fgrep, compile the regular expression,
 575  677                   * give an informative error message, and exit if
 576  678                   * it didn't compile.
 577  679                   */
 578  680                  if ((rv = regcomp(&pp->re, pp->pattern, regflags)) != 0) {
 579  681                          (void) regerror(rv, &pp->re, errstr, sizeof (errstr));
 580  682                          (void) fprintf(stderr,
 581  683                              gettext("%s: RE error in %s: %s\n"),
 582      -                                cmdname, pp->pattern, errstr);
      684 +                            cmdname, pp->pattern, errstr);
 583  685                          exit(2);
 584  686                  }
 585  687                  free(pp->pattern);
 586  688          }
 587  689  
 588  690          /*
 589  691           * Decide if we are able to run the Boyer-Moore-Gosper algorithm.
 590  692           * Use the Boyer-Moore-Gosper algorithm if:
 591  693           * - fgrep                      (Fflag)
 592  694           * - singlebyte locale          (!mblocale)
↓ open down ↓ 75 lines elided ↑ open up ↑
 668  770   *
 669  771   * We have two strategies:
 670  772   * The fast one is used when we have a single pattern with
 671  773   * a string known to occur in the pattern. We can then
 672  774   * do a BMG match on the whole buffer.
 673  775   * This is an order of magnitude faster.
 674  776   * Otherwise we split the buffer into lines,
 675  777   * and check for a match on each line.
 676  778   */
 677  779  static int
 678      -grep(int fd, char *fn)
      780 +grep(int fd, const char *fn)
 679  781  {
 680  782          PATTERN *pp;
 681  783          off_t   data_len;       /* length of the data chunk */
 682  784          off_t   line_len;       /* length of the current line */
 683  785          off_t   line_offset;    /* current line's offset from the beginning */
 684  786          long long       lineno;
 685  787          long long       matches = 0;    /* Number of matching lines */
 686  788          int     newlinep;       /* 0 if the last line of file has no newline */
 687  789          char    *ptr, *ptrend;
 688  790  
↓ open down ↓ 44 lines elided ↑ open up ↑
 733  835                          /*
 734  836                           * The current data chunk starts from prntbuf.
 735  837                           * This means either the buffer has no data
 736  838                           * or the buffer has no newline.
 737  839                           * So, read more data from input.
 738  840                           */
 739  841                          count = read(fd, ptr + data_len, prntbuflen - data_len);
 740  842                          if (count < 0) {
 741  843                                  /* read error */
 742  844                                  if (cflag) {
 743      -                                        if (outfn) {
      845 +                                        if (outfn && !rflag) {
 744  846                                                  (void) fprintf(stdout,
 745  847                                                      "%s:", fn);
 746  848                                          }
 747      -                                        if (!qflag) {
      849 +                                        if (!qflag && !rflag) {
 748  850                                                  (void) fprintf(stdout, "%lld\n",
 749  851                                                      matches);
 750  852                                          }
 751  853                                  }
 752  854                                  return (0);
 753  855                          } else if (count == 0) {
 754  856                                  /* no new data */
 755  857                                  if (data_len == 0) {
 756  858                                          /* end of file already reached */
 757  859                                          break;
↓ open down ↓ 94 lines elided ↑ open up ↑
 852  954                           * Using this result.
 853  955                           */
 854  956                          *ptrend = '\0';
 855  957                          line_len = ptrend - ptr;
 856  958  
 857  959                          /*
 858  960                           * before jumping to L_next_line,
 859  961                           * need to handle xflag if specified
 860  962                           */
 861  963                          if (xflag && (line_len != bmglen ||
 862      -                                strcmp(bmgpat, ptr) != 0)) {
      964 +                            strcmp(bmgpat, ptr) != 0)) {
 863  965                                  /* didn't match */
 864  966                                  pp = NULL;
 865  967                          } else {
 866  968                                  pp = patterns; /* to make it happen */
 867  969                          }
 868  970                          goto L_next_line;
 869  971                  }
 870  972                  lineno++;
 871  973                  /*
 872  974                   * Line starts from ptr and ends at ptrend.
↓ open down ↓ 35 lines elided ↑ open up ↑
 908 1010                                  wchar_t *cp;
 909 1011                                  for (cp = outline; *cp != '\0'; cp++) {
 910 1012                                          *cp = towlower((wint_t)*cp);
 911 1013                                  }
 912 1014                          }
 913 1015  
 914 1016                          if (xflag) {
 915 1017                                  for (pp = patterns; pp; pp = pp->next) {
 916 1018                                          if (outline[0] == pp->wpattern[0] &&
 917 1019                                              wcscmp(outline,
 918      -                                                pp->wpattern) == 0) {
     1020 +                                            pp->wpattern) == 0) {
 919 1021                                                  /* matched */
 920 1022                                                  break;
 921 1023                                          }
 922 1024                                  }
 923 1025                          } else {
 924 1026                                  for (pp = patterns; pp; pp = pp->next) {
 925 1027                                          if (wcswcs(outline, pp->wpattern)
 926 1028                                              != NULL) {
 927 1029                                                  /* matched */
 928 1030                                                  break;
↓ open down ↓ 120 lines elided ↑ open up ↑
1049 1151  
1050 1152  /*
1051 1153   * usage message for grep
1052 1154   */
1053 1155  static void
1054 1156  usage(void)
1055 1157  {
1056 1158          if (egrep || fgrep) {
1057 1159                  (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1058 1160                  (void) fprintf(stderr,
1059      -                    gettext(" [-c|-l|-q] [-bhinsvx] "
1060      -                        "pattern_list [file ...]\n"));
     1161 +                    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvx] "
     1162 +                    "pattern_list [file ...]\n"));
1061 1163  
1062 1164                  (void) fprintf(stderr, "\t%s", cmdname);
1063 1165                  (void) fprintf(stderr,
1064      -                    gettext(" [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
1065      -                        "[-f pattern_file]... [file...]\n"));
     1166 +                    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvx] "
     1167 +                    "[-e pattern_list]... "
     1168 +                    "[-f pattern_file]... [file...]\n"));
1066 1169          } else {
1067 1170                  (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1068 1171                  (void) fprintf(stderr,
1069      -                    gettext(" [-c|-l|-q] [-bhinsvwx] "
1070      -                        "pattern_list [file ...]\n"));
     1172 +                    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvwx] "
     1173 +                    "pattern_list [file ...]\n"));
1071 1174  
1072 1175                  (void) fprintf(stderr, "\t%s", cmdname);
1073 1176                  (void) fprintf(stderr,
1074      -                    gettext(" [-c|-l|-q] [-bhinsvwx] [-e pattern_list]... "
1075      -                        "[-f pattern_file]... [file...]\n"));
     1177 +                    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvwx] "
     1178 +                    "[-e pattern_list]... "
     1179 +                    "[-f pattern_file]... [file...]\n"));
1076 1180  
1077 1181                  (void) fprintf(stderr, "\t%s", cmdname);
1078 1182                  (void) fprintf(stderr,
1079      -                    gettext(" -E [-c|-l|-q] [-bhinsvx] "
1080      -                        "pattern_list [file ...]\n"));
     1183 +                    gettext(" -E [-c|-l|-q] [-r|-R] [-bhinsvx] "
     1184 +                    "pattern_list [file ...]\n"));
1081 1185  
1082 1186                  (void) fprintf(stderr, "\t%s", cmdname);
1083 1187                  (void) fprintf(stderr,
1084      -                    gettext(" -E [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
1085      -                        "[-f pattern_file]... [file...]\n"));
     1188 +                    gettext(" -E [-c|-l|-q] [-r|-R] [-bhinsvx] "
     1189 +                    "[-e pattern_list]... "
     1190 +                    "[-f pattern_file]... [file...]\n"));
1086 1191  
1087 1192                  (void) fprintf(stderr, "\t%s", cmdname);
1088 1193                  (void) fprintf(stderr,
1089      -                    gettext(" -F [-c|-l|-q] [-bhinsvx] "
1090      -                        "pattern_list [file ...]\n"));
     1194 +                    gettext(" -F [-c|-l|-q] [-r|-R] [-bhinsvx] "
     1195 +                    "pattern_list [file ...]\n"));
1091 1196  
1092 1197                  (void) fprintf(stderr, "\t%s", cmdname);
1093 1198                  (void) fprintf(stderr,
1094 1199                      gettext(" -F [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
1095      -                        "[-f pattern_file]... [file...]\n"));
     1200 +                    "[-f pattern_file]... [file...]\n"));
1096 1201          }
1097 1202          exit(2);
1098 1203          /* NOTREACHED */
1099 1204  }
1100 1205  
1101 1206  /*
1102 1207   * Compile literal pattern into BMG tables
1103 1208   */
1104 1209  static void
1105 1210  bmgcomp(char *pat, int len)
↓ open down ↓ 48 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX