1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  28 /*        All Rights Reserved   */
  29 
  30 /*      Copyright (c) 1987, 1988 Microsoft Corporation  */
  31 /*        All Rights Reserved   */
  32 
  33 /* Copyright 2012 Nexenta Systems, Inc.  All rights reserved. */
  34 
  35 /*
  36  * Copyright 2013 Damian Bogel. All rights reserved.
  37  * Copyright (c) 2013 Andrew Stormont.  All rights reserved.
  38  */
  39 
  40 /*
  41  * grep -- print lines matching (or not matching) a pattern
  42  *
  43  *      status returns:
  44  *              0 - ok, and some matches
  45  *              1 - ok, but no matches
  46  *              2 - some error
  47  */
  48 
  49 #include <sys/types.h>
  50 
  51 #include <ctype.h>
  52 #include <fcntl.h>
  53 #include <locale.h>
  54 #include <memory.h>
  55 #include <regexpr.h>
  56 #include <stdio.h>
  57 #include <stdlib.h>
  58 #include <string.h>
  59 #include <unistd.h>
  60 #include <ftw.h>
  61 #include <limits.h>
  62 #include <sys/param.h>
  63 
  64 static const char *errstr[] = {
  65         "Range endpoint too large.",
  66         "Bad number.",
  67         "``\\digit'' out of range.",
  68         "No remembered search string.",
  69         "\\( \\) imbalance.",
  70         "Too many \\(.",
  71         "More than 2 numbers given in \\{ \\}.",
  72         "} expected after \\.",
  73         "First number exceeds second in \\{ \\}.",
  74         "[ ] imbalance.",
  75         "Regular expression overflow.",
  76         "Illegal byte sequence.",
  77         "Unknown regexp error code!!",
  78         NULL
  79 };
  80 
  81 #define STDIN_FILENAME  gettext("(standard input)")
  82 
  83 #define errmsg(msg, arg)        (void) fprintf(stderr, gettext(msg), arg)
  84 #define BLKSIZE 512
  85 #define GBUFSIZ 8192
  86 #define MAX_DEPTH       1000
  87 
  88 static int      temp;
  89 static long long        lnum;
  90 static char     *linebuf;
  91 static char     *prntbuf = NULL;
  92 static long     fw_lPrntBufLen = 0;
  93 static int      nflag;
  94 static int      bflag;
  95 static int      lflag;
  96 static int      cflag;
  97 static int      rflag;
  98 static int      Rflag;
  99 static int      vflag;
 100 static int      sflag;
 101 static int      iflag;
 102 static int      wflag;
 103 static int      hflag;
 104 static int      Hflag;
 105 static int      qflag;
 106 static int      oflag;
 107 static int      errflg;
 108 static int      nfile;
 109 static long long        tln;
 110 static int      nsucc;
 111 static int      outfn = 0;
 112 static int      nlflag;
 113 static char     *ptr, *ptrend;
 114 static char     *expbuf;
 115 
 116 static void     execute(const char *, int);
 117 static void     regerr(int);
 118 static void     prepare(const char *);
 119 static int      recursive(const char *, const struct stat *, int, struct FTW *);
 120 static int      succeed(const char *);
 121 
 122 int
 123 main(int argc, char **argv)
 124 {
 125         int     c;
 126         char    *arg;
 127         extern int      optind;
 128 
 129         (void) setlocale(LC_ALL, "");
 130 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 131 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 132 #endif
 133         (void) textdomain(TEXT_DOMAIN);
 134 
 135         while ((c = getopt(argc, argv, "hHqblcnoRrsviyw")) != -1)
 136                 switch (c) {
 137                 /* based on options order h or H is set as in GNU grep */
 138                 case 'h':
 139                         hflag++;
 140                         Hflag = 0; /* h excludes H */
 141                         break;
 142                 case 'H':
 143                         if (!lflag) /* H is excluded by l */
 144                                 Hflag++;
 145                         hflag = 0; /* H excludes h */
 146                         break;
 147                 case 'q':       /* POSIX: quiet: status only */
 148                         qflag++;
 149                         break;
 150                 case 'v':
 151                         vflag++;
 152                         break;
 153                 case 'c':
 154                         cflag++;
 155                         break;
 156                 case 'n':
 157                         nflag++;
 158                         break;
 159                 case 'o':
 160                         oflag++;
 161                         break;
 162                 case 'R':
 163                         Rflag++;
 164                         /* FALLTHROUGH */
 165                 case 'r':
 166                         rflag++;
 167                         break;
 168                 case 'b':
 169                         bflag++;
 170                         break;
 171                 case 's':
 172                         sflag++;
 173                         break;
 174                 case 'l':
 175                         lflag++;
 176                         Hflag = 0; /* l excludes H */
 177                         break;
 178                 case 'y':
 179                 case 'i':
 180                         iflag++;
 181                         break;
 182                 case 'w':
 183                         wflag++;
 184                         break;
 185                 case '?':
 186                         errflg++;
 187                 }
 188 
 189         if (errflg || (optind >= argc)) {
 190                 errmsg("Usage: grep [-c|-l|-q|-o] [-r|-R] -hHbnsviw "
 191                     "pattern file . . .\n",
 192                     (char *)NULL);
 193                 exit(2);
 194         }
 195 
 196         argv = &argv[optind];
 197         argc -= optind;
 198         nfile = argc - 1;
 199 
 200         if (strrchr(*argv, '\n') != NULL)
 201                 regerr(41);
 202 
 203         if (iflag) {
 204                 for (arg = *argv; *arg != NULL; ++arg)
 205                         *arg = (char)tolower((int)((unsigned char)*arg));
 206         }
 207 
 208         if (wflag) {
 209                 unsigned int    wordlen;
 210                 char            *wordbuf;
 211 
 212                 wordlen = strlen(*argv) + 5; /* '\\' '<' *argv '\\' '>' '\0' */
 213                 if ((wordbuf = malloc(wordlen)) == NULL) {
 214                         errmsg("grep: Out of memory for word\n", (char *)NULL);
 215                         exit(2);
 216                 }
 217 
 218                 (void) strcpy(wordbuf, "\\<");
 219                 (void) strcat(wordbuf, *argv);
 220                 (void) strcat(wordbuf, "\\>");
 221                 *argv = wordbuf;
 222         }
 223 
 224         expbuf = compile(*argv, (char *)0, (char *)0);
 225         if (regerrno)
 226                 regerr(regerrno);
 227 
 228         if (--argc == 0)
 229                 execute(NULL, 0);
 230         else
 231                 while (argc-- > 0)
 232                         prepare(*++argv);
 233 
 234         return (nsucc == 2 ? 2 : (nsucc == 0 ? 1 : 0));
 235 }
 236 
 237 static void
 238 prepare(const char *path)
 239 {
 240         struct  stat st;
 241         int     walkflags = FTW_CHDIR;
 242         char    *buf = NULL;
 243 
 244         if (rflag) {
 245                 if (stat(path, &st) != -1 &&
 246                     (st.st_mode & S_IFMT) == S_IFDIR) {
 247                         outfn = 1;
 248 
 249                         /*
 250                          * Add trailing slash if arg
 251                          * is directory, to resolve symlinks.
 252                          */
 253                         if (path[strlen(path) - 1] != '/') {
 254                                 (void) asprintf(&buf, "%s/", path);
 255                                 if (buf != NULL)
 256                                         path = buf;
 257                         }
 258 
 259                         /*
 260                          * Search through subdirs if path is directory.
 261                          * Don't follow symlinks if Rflag is not set.
 262                          */
 263                         if (!Rflag)
 264                                 walkflags |= FTW_PHYS;
 265 
 266                         if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
 267                                 if (!sflag)
 268                                         errmsg("grep: can't open %s\n", path);
 269                                 nsucc = 2;
 270                         }
 271                         return;
 272                 }
 273         }
 274         execute(path, 0);
 275 }
 276 
 277 static int
 278 recursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
 279 {
 280         /*
 281          * process files and follow symlinks if Rflag set.
 282          */
 283         if (info != FTW_F) {
 284                 if (!sflag &&
 285                     (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
 286                         /* report broken symlinks and unreadable files */
 287                         errmsg("grep: can't open %s\n", name);
 288                 }
 289                 return (0);
 290         }
 291 
 292         /* skip devices and pipes if Rflag is not set */
 293         if (!Rflag && !S_ISREG(statp->st_mode))
 294                 return (0);
 295 
 296         /* pass offset to relative name from FTW_CHDIR */
 297         execute(name, ftw->base);
 298         return (0);
 299 }
 300 
 301 static void
 302 execute(const char *file, int base)
 303 {
 304         char    *lbuf, *p;
 305         long    count;
 306         long    offset = 0;
 307         char    *next_ptr = NULL;
 308         long    next_count = 0;
 309 
 310         tln = 0;
 311 
 312         if (prntbuf == NULL) {
 313                 fw_lPrntBufLen = GBUFSIZ + 1;
 314                 if ((prntbuf = malloc(fw_lPrntBufLen)) == NULL) {
 315                         exit(2); /* out of memory - BAIL */
 316                 }
 317                 if ((linebuf = malloc(fw_lPrntBufLen)) == NULL) {
 318                         exit(2); /* out of memory - BAIL */
 319                 }
 320         }
 321 
 322         if (file == NULL) {
 323                 temp = 0;
 324                 file = STDIN_FILENAME;
 325         } else if ((temp = open(file + base, O_RDONLY)) == -1) {
 326                 if (!sflag)
 327                         errmsg("grep: can't open %s\n", file);
 328                 nsucc = 2;
 329                 return;
 330         }
 331 
 332         /* read in first block of bytes */
 333         if ((count = read(temp, prntbuf, GBUFSIZ)) <= 0) {
 334                 (void) close(temp);
 335 
 336                 if (cflag && !qflag) {
 337                         if (Hflag || (nfile > 1 && !hflag))
 338                                 (void) fprintf(stdout, "%s:", file);
 339                         if (!rflag)
 340                         (void) fprintf(stdout, "%lld\n", tln);
 341                 }
 342                 return;
 343         }
 344 
 345         lnum = 0;
 346         ptr = prntbuf;
 347         for (;;) {
 348                 /* look for next newline */
 349                 if ((ptrend = memchr(ptr + offset, '\n', count)) == NULL) {
 350                         offset += count;
 351 
 352                         /*
 353                          * shift unused data to the beginning of the buffer
 354                          */
 355                         if (ptr > prntbuf) {
 356                                 (void) memmove(prntbuf, ptr, offset);
 357                                 ptr = prntbuf;
 358                         }
 359 
 360                         /*
 361                          * re-allocate a larger buffer if this one is full
 362                          */
 363                         if (offset + GBUFSIZ > fw_lPrntBufLen) {
 364                                 /*
 365                                  * allocate a new buffer and preserve the
 366                                  * contents...
 367                                  */
 368                                 fw_lPrntBufLen += GBUFSIZ;
 369                                 if ((prntbuf = realloc(prntbuf,
 370                                     fw_lPrntBufLen)) == NULL)
 371                                         exit(2);
 372 
 373                                 /*
 374                                  * set up a bigger linebuffer (this is only used
 375                                  * for case insensitive operations). Contents do
 376                                  * not have to be preserved.
 377                                  */
 378                                 free(linebuf);
 379                                 if ((linebuf = malloc(fw_lPrntBufLen)) == NULL)
 380                                         exit(2);
 381 
 382                                 ptr = prntbuf;
 383                         }
 384 
 385                         p = prntbuf + offset;
 386                         if ((count = read(temp, p, GBUFSIZ)) > 0)
 387                                 continue;
 388 
 389                         if (offset == 0)
 390                                 /* end of file already reached */
 391                                 break;
 392 
 393                         /* last line of file has no newline */
 394                         ptrend = ptr + offset;
 395                         nlflag = 0;
 396                 } else {
 397                         next_ptr = ptrend + 1;
 398                         next_count = offset + count - (next_ptr - ptr);
 399                         nlflag = 1;
 400                 }
 401                 lnum++;
 402                 *ptrend = '\0';
 403 
 404                 if (iflag) {
 405                         /*
 406                          * Make a lower case copy of the record
 407                          */
 408                         p = ptr;
 409                         for (lbuf = linebuf; p < ptrend; )
 410                                 *lbuf++ = (char)tolower((int)
 411                                     (unsigned char)*p++);
 412                         *lbuf = '\0';
 413                         lbuf = linebuf;
 414                 } else
 415                         /*
 416                          * Use record as is
 417                          */
 418                         lbuf = ptr;
 419 
 420                 /* lflag only once */
 421                 if (step(lbuf, expbuf) ^ vflag) {
 422                         if (oflag) {
 423                                 /*
 424                                  * Only store the matching bits
 425                                  */
 426                                 ptr = loc1;
 427                                 ptrend = loc2;
 428                         }
 429                         if (succeed(file) == 1)
 430                                 break;
 431                 }
 432 
 433                 if (!nlflag)
 434                         break;
 435 
 436                 ptr = next_ptr;
 437                 count = next_count;
 438                 offset = 0;
 439         }
 440         (void) close(temp);
 441 
 442         if (cflag && !qflag) {
 443                 if (Hflag || (!hflag && ((nfile > 1) ||
 444                     (rflag && outfn))))
 445                         (void) fprintf(stdout, "%s:", file);
 446                 (void) fprintf(stdout, "%lld\n", tln);
 447         }
 448 }
 449 
 450 static int
 451 succeed(const char *f)
 452 {
 453         int nchars;
 454         nsucc = (nsucc == 2) ? 2 : 1;
 455 
 456         if (qflag) {
 457                 /* no need to continue */
 458                 return (1);
 459         }
 460 
 461         if (cflag) {
 462                 tln++;
 463                 return (0);
 464         }
 465 
 466         if (lflag) {
 467                 (void) fprintf(stdout, "%s\n", f);
 468                 return (1);
 469         }
 470 
 471         if (Hflag || (!hflag && (nfile > 1 || (rflag && outfn)))) {
 472                 /* print filename */
 473                 (void) fprintf(stdout, "%s:", f);
 474         }
 475 
 476         if (bflag)
 477                 /* print block number */
 478                 (void) fprintf(stdout, "%lld:", (offset_t)
 479                     ((lseek(temp, (off_t)0, SEEK_CUR) - 1) / BLKSIZE));
 480 
 481         if (nflag)
 482                 /* print line number */
 483                 (void) fprintf(stdout, "%lld:", lnum);
 484 
 485         if (nlflag) {
 486                 /* newline at end of line */
 487                 *ptrend = '\n';
 488                 nchars = ptrend - ptr + 1;
 489         } else {
 490                 /* don't write sentinel \0 */
 491                 nchars = ptrend - ptr;
 492         }
 493 
 494         (void) fwrite(ptr, 1, nchars, stdout);
 495         return (0);
 496 }
 497 
 498 static void
 499 regerr(int err)
 500 {
 501         errmsg("grep: RE error %d: ", err);
 502         switch (err) {
 503                 case 11:
 504                         err = 0;
 505                         break;
 506                 case 16:
 507                         err = 1;
 508                         break;
 509                 case 25:
 510                         err = 2;
 511                         break;
 512                 case 41:
 513                         err = 3;
 514                         break;
 515                 case 42:
 516                         err = 4;
 517                         break;
 518                 case 43:
 519                         err = 5;
 520                         break;
 521                 case 44:
 522                         err = 6;
 523                         break;
 524                 case 45:
 525                         err = 7;
 526                         break;
 527                 case 46:
 528                         err = 8;
 529                         break;
 530                 case 49:
 531                         err = 9;
 532                         break;
 533                 case 50:
 534                         err = 10;
 535                         break;
 536                 case 67:
 537                         err = 11;
 538                         break;
 539                 default:
 540                         err = 12;
 541                         break;
 542         }
 543 
 544         errmsg("%s\n", gettext(errstr[err]));
 545         exit(2);
 546 }