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