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 }