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  * grep -- print lines matching (or not matching) a pattern
  37  *
  38  *      status returns:
  39  *              0 - ok, and some matches
  40  *              1 - ok, but no matches
  41  *              2 - some error
  42  */
  43 
  44 #include <sys/types.h>
  45 
  46 #include <ctype.h>
  47 #include <fcntl.h>
  48 #include <locale.h>
  49 #include <memory.h>
  50 #include <regexpr.h>
  51 #include <stdio.h>
  52 #include <stdlib.h>
  53 #include <string.h>
  54 #include <unistd.h>
  55 
  56 static const char *errstr[] = {
  57         "Range endpoint too large.",
  58         "Bad number.",
  59         "``\\digit'' out of range.",
  60         "No remembered search string.",
  61         "\\( \\) imbalance.",
  62         "Too many \\(.",
  63         "More than 2 numbers given in \\{ \\}.",
  64         "} expected after \\.",
  65         "First number exceeds second in \\{ \\}.",
  66         "[ ] imbalance.",
  67         "Regular expression overflow.",
  68         "Illegal byte sequence.",
  69         "Unknown regexp error code!!",
  70         NULL
  71 };
  72 
  73 #define errmsg(msg, arg)        (void) fprintf(stderr, gettext(msg), arg)
  74 #define BLKSIZE 512
  75 #define GBUFSIZ 8192
  76 
  77 static int      temp;
  78 static long long        lnum;
  79 static char     *linebuf;
  80 static char     *prntbuf = NULL;
  81 static long     fw_lPrntBufLen = 0;
  82 static int      nflag;
  83 static int      bflag;
  84 static int      lflag;
  85 static int      cflag;
  86 static int      vflag;
  87 static int      sflag;
  88 static int      iflag;
  89 static int      wflag;
  90 static int      hflag;
  91 static int      qflag;
  92 static int      errflg;
  93 static int      nfile;
  94 static long long        tln;
  95 static int      nsucc;
  96 static int      nlflag;
  97 static char     *ptr, *ptrend;
  98 static char     *expbuf;
  99 
 100 static void     execute(char *);
 101 static void     regerr(int);
 102 static int      succeed(char *);
 103 
 104 int
 105 main(int argc, char **argv)
 106 {
 107         int     c;
 108         char    *arg;
 109         extern int      optind;
 110 
 111         (void) setlocale(LC_ALL, "");
 112 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 113 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 114 #endif
 115         (void) textdomain(TEXT_DOMAIN);
 116 
 117         while ((c = getopt(argc, argv, "hqblcnsviyw")) != -1)
 118                 switch (c) {
 119                 case 'h':
 120                         hflag++;
 121                         break;
 122                 case 'q':       /* POSIX: quiet: status only */
 123                         qflag++;
 124                         break;
 125                 case 'v':
 126                         vflag++;
 127                         break;
 128                 case 'c':
 129                         cflag++;
 130                         break;
 131                 case 'n':
 132                         nflag++;
 133                         break;
 134                 case 'b':
 135                         bflag++;
 136                         break;
 137                 case 's':
 138                         sflag++;
 139                         break;
 140                 case 'l':
 141                         lflag++;
 142                         break;
 143                 case 'y':
 144                 case 'i':
 145                         iflag++;
 146                         break;
 147                 case 'w':
 148                         wflag++;
 149                         break;
 150                 case '?':
 151                         errflg++;
 152                 }
 153 
 154         if (errflg || (optind >= argc)) {
 155                 errmsg("Usage: grep [-c|-l|-q] -hbnsviw pattern file . . .\n",
 156                     (char *)NULL);
 157                 exit(2);
 158         }
 159 
 160         argv = &argv[optind];
 161         argc -= optind;
 162         nfile = argc - 1;
 163 
 164         if (strrchr(*argv, '\n') != NULL)
 165                 regerr(41);
 166 
 167         if (iflag) {
 168                 for (arg = *argv; *arg != NULL; ++arg)
 169                         *arg = (char)tolower((int)((unsigned char)*arg));
 170         }
 171 
 172         if (wflag) {
 173                 unsigned int    wordlen;
 174                 char            *wordbuf;
 175 
 176                 wordlen = strlen(*argv) + 5; /* '\\' '<' *argv '\\' '>' '\0' */
 177                 if ((wordbuf = malloc(wordlen)) == NULL) {
 178                         errmsg("grep: Out of memory for word\n", (char *)NULL);
 179                         exit(2);
 180                 }
 181 
 182                 (void) strcpy(wordbuf, "\\<");
 183                 (void) strcat(wordbuf, *argv);
 184                 (void) strcat(wordbuf, "\\>");
 185                 *argv = wordbuf;
 186         }
 187 
 188         expbuf = compile(*argv, (char *)0, (char *)0);
 189         if (regerrno)
 190                 regerr(regerrno);
 191 
 192         if (--argc == 0)
 193                 execute(NULL);
 194         else
 195                 while (argc-- > 0)
 196                         execute(*++argv);
 197 
 198         return (nsucc == 2 ? 2 : (nsucc == 0 ? 1 : 0));
 199 }
 200 
 201 static void
 202 execute(char *file)
 203 {
 204         char    *lbuf, *p;
 205         long    count;
 206         long    offset = 0;
 207         char    *next_ptr = NULL;
 208         long    next_count = 0;
 209 
 210         tln = 0;
 211 
 212         if (prntbuf == NULL) {
 213                 fw_lPrntBufLen = GBUFSIZ + 1;
 214                 if ((prntbuf = malloc(fw_lPrntBufLen)) == NULL) {
 215                         exit(2); /* out of memory - BAIL */
 216                 }
 217                 if ((linebuf = malloc(fw_lPrntBufLen)) == NULL) {
 218                         exit(2); /* out of memory - BAIL */
 219                 }
 220         }
 221 
 222         if (file == NULL)
 223                 temp = 0;
 224         else if ((temp = open(file, O_RDONLY)) == -1) {
 225                 if (!sflag)
 226                         errmsg("grep: can't open %s\n", file);
 227                 nsucc = 2;
 228                 return;
 229         }
 230 
 231         /* read in first block of bytes */
 232         if ((count = read(temp, prntbuf, GBUFSIZ)) <= 0) {
 233                 (void) close(temp);
 234 
 235                 if (cflag && !qflag) {
 236                         if (nfile > 1 && !hflag && file)
 237                                 (void) fprintf(stdout, "%s:", file);
 238                         (void) fprintf(stdout, "%lld\n", tln);
 239                 }
 240                 return;
 241         }
 242 
 243         lnum = 0;
 244         ptr = prntbuf;
 245         for (;;) {
 246                 /* look for next newline */
 247                 if ((ptrend = memchr(ptr + offset, '\n', count)) == NULL) {
 248                         offset += count;
 249 
 250                         /*
 251                          * shift unused data to the beginning of the buffer
 252                          */
 253                         if (ptr > prntbuf) {
 254                                 (void) memmove(prntbuf, ptr, offset);
 255                                 ptr = prntbuf;
 256                         }
 257 
 258                         /*
 259                          * re-allocate a larger buffer if this one is full
 260                          */
 261                         if (offset + GBUFSIZ > fw_lPrntBufLen) {
 262                                 /*
 263                                  * allocate a new buffer and preserve the
 264                                  * contents...
 265                                  */
 266                                 fw_lPrntBufLen += GBUFSIZ;
 267                                 if ((prntbuf = realloc(prntbuf,
 268                                     fw_lPrntBufLen)) == NULL)
 269                                         exit(2);
 270 
 271                                 /*
 272                                  * set up a bigger linebuffer (this is only used
 273                                  * for case insensitive operations). Contents do
 274                                  * not have to be preserved.
 275                                  */
 276                                 free(linebuf);
 277                                 if ((linebuf = malloc(fw_lPrntBufLen)) == NULL)
 278                                         exit(2);
 279 
 280                                 ptr = prntbuf;
 281                         }
 282 
 283                         p = prntbuf + offset;
 284                         if ((count = read(temp, p, GBUFSIZ)) > 0)
 285                                 continue;
 286 
 287                         if (offset == 0)
 288                                 /* end of file already reached */
 289                                 break;
 290 
 291                         /* last line of file has no newline */
 292                         ptrend = ptr + offset;
 293                         nlflag = 0;
 294                 } else {
 295                         next_ptr = ptrend + 1;
 296                         next_count = offset + count - (next_ptr - ptr);
 297                         nlflag = 1;
 298                 }
 299                 lnum++;
 300                 *ptrend = '\0';
 301 
 302                 if (iflag) {
 303                         /*
 304                          * Make a lower case copy of the record
 305                          */
 306                         p = ptr;
 307                         for (lbuf = linebuf; p < ptrend; )
 308                                 *lbuf++ = (char)tolower((int)
 309                                     (unsigned char)*p++);
 310                         *lbuf = '\0';
 311                         lbuf = linebuf;
 312                 } else
 313                         /*
 314                          * Use record as is
 315                          */
 316                         lbuf = ptr;
 317 
 318                 /* lflag only once */
 319                 if ((step(lbuf, expbuf) ^ vflag) && succeed(file) == 1)
 320                         break;
 321 
 322                 if (!nlflag)
 323                         break;
 324 
 325                 ptr = next_ptr;
 326                 count = next_count;
 327                 offset = 0;
 328         }
 329         (void) close(temp);
 330 
 331         if (cflag && !qflag) {
 332                 if (nfile > 1 && !hflag && file)
 333                         (void) fprintf(stdout, "%s:", file);
 334                 (void) fprintf(stdout, "%lld\n", tln);
 335         }
 336 }
 337 
 338 static int
 339 succeed(char *f)
 340 {
 341         int nchars;
 342         nsucc = (nsucc == 2) ? 2 : 1;
 343 
 344         if (f == NULL)
 345                 f = "<stdin>";
 346 
 347         if (qflag) {
 348                 /* no need to continue */
 349                 return (1);
 350         }
 351 
 352         if (cflag) {
 353                 tln++;
 354                 return (0);
 355         }
 356 
 357         if (lflag) {
 358                 (void) fprintf(stdout, "%s\n", f);
 359                 return (1);
 360         }
 361 
 362         if (nfile > 1 && !hflag)
 363                 /* print filename */
 364                 (void) fprintf(stdout, "%s:", f);
 365 
 366         if (bflag)
 367                 /* print block number */
 368                 (void) fprintf(stdout, "%lld:", (offset_t)
 369                     ((lseek(temp, (off_t)0, SEEK_CUR) - 1) / BLKSIZE));
 370 
 371         if (nflag)
 372                 /* print line number */
 373                 (void) fprintf(stdout, "%lld:", lnum);
 374 
 375         if (nlflag) {
 376                 /* newline at end of line */
 377                 *ptrend = '\n';
 378                 nchars = ptrend - ptr + 1;
 379         } else {
 380                 /* don't write sentinel \0 */
 381                 nchars = ptrend - ptr;
 382         }
 383 
 384         (void) fwrite(ptr, 1, nchars, stdout);
 385         return (0);
 386 }
 387 
 388 static void
 389 regerr(int err)
 390 {
 391         errmsg("grep: RE error %d: ", err);
 392         switch (err) {
 393                 case 11:
 394                         err = 0;
 395                         break;
 396                 case 16:
 397                         err = 1;
 398                         break;
 399                 case 25:
 400                         err = 2;
 401                         break;
 402                 case 41:
 403                         err = 3;
 404                         break;
 405                 case 42:
 406                         err = 4;
 407                         break;
 408                 case 43:
 409                         err = 5;
 410                         break;
 411                 case 44:
 412                         err = 6;
 413                         break;
 414                 case 45:
 415                         err = 7;
 416                         break;
 417                 case 46:
 418                         err = 8;
 419                         break;
 420                 case 49:
 421                         err = 9;
 422                         break;
 423                 case 50:
 424                         err = 10;
 425                         break;
 426                 case 67:
 427                         err = 11;
 428                         break;
 429                 default:
 430                         err = 12;
 431                         break;
 432         }
 433 
 434         errmsg("%s\n", gettext(errstr[err]));
 435         exit(2);
 436 }