rpm 5.3.12
|
00001 /************************************************* 00002 * pcregrep program * 00003 *************************************************/ 00004 00005 /* This is a grep program that uses the PCRE regular expression library to do 00006 its pattern matching. On a Unix or Win32 system it can recurse into 00007 directories. 00008 00009 Copyright (c) 1997-2008 University of Cambridge 00010 00011 ----------------------------------------------------------------------------- 00012 Redistribution and use in source and binary forms, with or without 00013 modification, are permitted provided that the following conditions are met: 00014 00015 * Redistributions of source code must retain the above copyright notice, 00016 this list of conditions and the following disclaimer. 00017 00018 * Redistributions in binary form must reproduce the above copyright 00019 notice, this list of conditions and the following disclaimer in the 00020 documentation and/or other materials provided with the distribution. 00021 00022 * Neither the name of the University of Cambridge nor the names of its 00023 contributors may be used to endorse or promote products derived from 00024 this software without specific prior written permission. 00025 00026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00028 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00029 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00030 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00031 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00032 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00033 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00034 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00035 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00036 POSSIBILITY OF SUCH DAMAGE. 00037 ----------------------------------------------------------------------------- 00038 */ 00039 00040 #include "system.h" 00041 00042 #define _MIRE_INTERNAL 00043 #include <rpmio_internal.h> /* XXX fdGetFILE */ 00044 #include <rpmdir.h> 00045 #include <poptIO.h> 00046 00047 #include "debug.h" 00048 00049 /*@access miRE @*/ 00050 00051 typedef unsigned BOOL; 00052 #define FALSE ((BOOL)0) 00053 #define TRUE ((BOOL)1) 00054 00055 #define MAX_PATTERN_COUNT 100 00056 00057 #if BUFSIZ > 8192 00058 #define MBUFTHIRD BUFSIZ 00059 #else 00060 #define MBUFTHIRD 8192 00061 #endif 00062 00063 static inline void fwrite_check(const void *ptr, size_t size, size_t nmemb, FILE *stream) 00064 { 00065 if(fwrite(ptr, size, nmemb, stream) != nmemb) 00066 perror("fwrite"); 00067 } 00068 00069 /************************************************* 00070 * Global variables * 00071 *************************************************/ 00072 00073 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00074 static const char *newline = NULL; 00075 00076 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00077 static const char *color_string = NULL; 00078 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00079 static ARGV_t pattern_filenames = NULL; 00080 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00081 static const char *stdin_name = NULL; 00082 00083 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00084 static const char *locale = NULL; 00085 00086 /*@unchecked@*/ /*@only@*/ /*@relnull@*/ 00087 static ARGV_t patterns = NULL; 00088 /*@unchecked@*/ /*@only@*/ /*@relnull@*/ 00089 static miRE pattern_list = NULL; 00090 /*@unchecked@*/ 00091 static int pattern_count = 0; 00092 00093 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00094 static ARGV_t exclude_patterns = NULL; 00095 /*@unchecked@*/ /*@only@*/ /*@relnull@*/ 00096 static miRE excludeMire = NULL; 00097 /*@unchecked@*/ 00098 static int nexcludes = 0; 00099 00100 /*@unchecked@*/ /*@only@*/ /*@null@*/ 00101 static ARGV_t include_patterns = NULL; 00102 /*@unchecked@*/ /*@only@*/ /*@relnull@*/ 00103 static miRE includeMire = NULL; 00104 /*@unchecked@*/ 00105 static int nincludes = 0; 00106 00107 /*@unchecked@*/ 00108 static int after_context = 0; 00109 /*@unchecked@*/ 00110 static int before_context = 0; 00111 /*@unchecked@*/ 00112 static int both_context = 0; 00113 00115 enum dee_e { dee_READ=1, dee_SKIP, dee_RECURSE }; 00116 /*@unchecked@*/ 00117 static enum dee_e dee_action = dee_READ; 00118 00120 enum DEE_e { DEE_READ=1, DEE_SKIP }; 00121 /*@unchecked@*/ 00122 static enum DEE_e DEE_action = DEE_READ; 00123 00124 /*@unchecked@*/ 00125 static int error_count = 0; 00126 00132 enum FN_e { FN_NONE, FN_DEFAULT, FN_ONLY, FN_NOMATCH_ONLY, FN_FORCE }; 00133 /*@unchecked@*/ 00134 static enum FN_e filenames = FN_DEFAULT; 00135 00136 #define _GFB(n) ((1U << (n)) | 0x40000000) 00137 #define GF_ISSET(_FLAG) ((grepFlags & ((GREP_FLAGS_##_FLAG) & ~0x40000000)) != GREP_FLAGS_NONE) 00138 00139 enum grepFlags_e { 00140 GREP_FLAGS_NONE = 0, 00141 00142 /* XXX WATCHOUT: the next 3 bits are also used as an index, do not change!!! */ 00143 GREP_FLAGS_WORD_MATCH = _GFB( 0), 00144 GREP_FLAGS_LINE_MATCH = _GFB( 1), 00145 GREP_FLAGS_FIXED_STRINGS = _GFB( 2), 00147 GREP_FLAGS_COUNT = _GFB( 3), 00148 GREP_FLAGS_COLOR = _GFB( 4), 00149 GREP_FLAGS_FOFFSETS = _GFB( 5), 00150 GREP_FLAGS_LOFFSETS = _GFB( 6), 00151 GREP_FLAGS_LNUMBER = _GFB( 7), 00152 GREP_FLAGS_MULTILINE = _GFB( 8), 00153 GREP_FLAGS_ONLY_MATCHING = _GFB( 9), 00154 GREP_FLAGS_INVERT = _GFB(10), 00155 GREP_FLAGS_QUIET = _GFB(11), 00156 GREP_FLAGS_SILENT = _GFB(12), 00157 GREP_FLAGS_UTF8 = _GFB(13), 00158 GREP_FLAGS_CASELESS = _GFB(14), 00159 }; 00160 00161 /*@unchecked@*/ 00162 static enum grepFlags_e grepFlags = GREP_FLAGS_NONE; 00163 00164 #if defined(WITH_PCRE) 00165 /*@unchecked@*/ 00166 static rpmMireMode grepMode = RPMMIRE_PCRE; 00167 #else 00168 static rpmMireMode grepMode = RPMMIRE_REGEX; 00169 #endif 00170 00171 /*@unchecked@*/ 00172 static struct rpmop_s grep_totalops; 00173 /*@unchecked@*/ 00174 static struct rpmop_s grep_readops; 00175 00182 /*@unchecked@*/ /*@observer@*/ 00183 static const char *prefix[] = { 00184 "", "\\b", "^(?:", "^(?:", "\\Q", "\\b\\Q", "^(?:\\Q", "^(?:\\Q" 00185 }; 00186 00187 /*@unchecked@*/ /*@observer@*/ 00188 static const char *suffix[] = { 00189 "", "\\b", ")$", ")$", "\\E", "\\E\\b", "\\E)$", "\\E)$" 00190 }; 00191 00193 /*@unchecked@*/ /*@observer@*/ 00194 static const unsigned utf8_table3[] = { 00195 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01 00196 }; 00197 00198 /*@+charint@*/ 00199 /*@unchecked@*/ /*@observer@*/ 00200 static const char utf8_table4[] = { 00201 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 00202 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 00203 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 00204 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 00205 }; 00206 /*@=charint@*/ 00207 00208 /************************************************* 00209 * Find end of line. 00210 * 00211 * The length of the endline sequence that is found is set via lenptr. 00212 * This may be zero at the very end of the file if there is no line-ending 00213 * sequence there. 00214 * 00215 * @param p current position in line 00216 * @param endptr end of available data 00217 * @retval *lenptr length of the eol sequence 00218 * @return pointer to the last byte of the line 00219 */ 00220 /*@observer@*/ 00221 static const char * 00222 end_of_line(const char *p, const char *endptr, /*@out@*/ size_t *lenptr) 00223 /*@modifies *lenptr @*/ 00224 { 00225 switch(_mireEL) { 00226 default: /* Just in case */ 00227 case EL_LF: 00228 while (p < endptr && *p != '\n') p++; 00229 if (p < endptr) { 00230 *lenptr = 1; 00231 return p + 1; 00232 } 00233 *lenptr = 0; 00234 return endptr; 00235 /*@notreached@*/ break; 00236 00237 case EL_CR: 00238 while (p < endptr && *p != '\r') p++; 00239 if (p < endptr) { 00240 *lenptr = 1; 00241 return p + 1; 00242 } 00243 *lenptr = 0; 00244 return endptr; 00245 /*@notreached@*/ break; 00246 00247 case EL_CRLF: 00248 for (;;) { 00249 while (p < endptr && *p != '\r') p++; 00250 if (++p >= endptr) { 00251 *lenptr = 0; 00252 return endptr; 00253 } 00254 if (*p == '\n') { 00255 *lenptr = 2; 00256 return p + 1; 00257 } 00258 } 00259 /*@notreached@*/ break; 00260 00261 case EL_ANYCRLF: 00262 while (p < endptr) { 00263 size_t extra = 0; 00264 unsigned int c = (unsigned)*((unsigned char *)p); 00265 00266 if (GF_ISSET(UTF8) && c >= 0xc0) { 00267 size_t gcii, gcss; 00268 extra = (size_t)utf8_table4[c & 0x3f]; /* No. of additional bytes */ 00269 gcss = 6*extra; 00270 c = (c & utf8_table3[extra]) << gcss; 00271 for (gcii = 1; gcii <= extra; gcii++) { 00272 gcss -= 6; 00273 c |= ((unsigned)p[gcii] & 0x3f) << gcss; 00274 } 00275 } 00276 00277 p += 1 + extra; 00278 00279 switch (c) { 00280 case 0x0a: /* LF */ 00281 *lenptr = 1; 00282 return p; 00283 /*@notreached@*/ /*@switchbreak@*/ break; 00284 00285 case 0x0d: /* CR */ 00286 if (p < endptr && (unsigned)*p == 0x0a) { 00287 *lenptr = 2; 00288 p++; 00289 } 00290 else *lenptr = 1; 00291 return p; 00292 /*@notreached@*/ /*@switchbreak@*/ break; 00293 00294 default: 00295 /*@switchbreak@*/ break; 00296 } 00297 } /* End of loop for ANYCRLF case */ 00298 00299 *lenptr = 0; /* Must have hit the end */ 00300 return endptr; 00301 /*@notreached@*/ break; 00302 00303 case EL_ANY: 00304 while (p < endptr) { 00305 size_t extra = 0; 00306 unsigned int c = (unsigned)*((unsigned char *)p); 00307 00308 if (GF_ISSET(UTF8) && c >= 0xc0) { 00309 size_t gcii, gcss; 00310 extra = (size_t)utf8_table4[c & 0x3f]; /* No. of additional bytes */ 00311 gcss = 6*extra; 00312 c = (c & utf8_table3[extra]) << gcss; 00313 for (gcii = 1; gcii <= extra; gcii++) { 00314 gcss -= 6; 00315 c |= ((unsigned)p[gcii] & 0x3f) << gcss; 00316 } 00317 } 00318 00319 p += 1 + extra; 00320 00321 switch (c) { 00322 case 0x0a: /* LF */ 00323 case 0x0b: /* VT */ 00324 case 0x0c: /* FF */ 00325 *lenptr = 1; 00326 return p; 00327 /*@notreached@*/ /*@switchbreak@*/ break; 00328 00329 case 0x0d: /* CR */ 00330 if (p < endptr && (unsigned)*p == 0x0a) { 00331 *lenptr = 2; 00332 p++; 00333 } 00334 else *lenptr = 1; 00335 return p; 00336 /*@notreached@*/ /*@switchbreak@*/ break; 00337 00338 case 0x85: /* NEL */ 00339 *lenptr = GF_ISSET(UTF8) ? 2 : 1; 00340 return p; 00341 /*@notreached@*/ /*@switchbreak@*/ break; 00342 00343 case 0x2028: /* LS */ 00344 case 0x2029: /* PS */ 00345 *lenptr = 3; 00346 return p; 00347 /*@notreached@*/ /*@switchbreak@*/ break; 00348 00349 default: 00350 /*@switchbreak@*/ break; 00351 } 00352 } /* End of loop for ANY case */ 00353 00354 *lenptr = 0; /* Must have hit the end */ 00355 return endptr; 00356 /*@notreached@*/ break; 00357 } /* End of overall switch */ 00358 /*@notreached@*/ 00359 } 00360 00361 /************************************************* 00362 * Find start of previous line 00363 * 00364 * This is called when looking back for before lines to print. 00365 * 00366 * @param p start of the subsequent line 00367 * @param startptr start of available data 00368 * @return pointer to the start of the previous line 00369 */ 00370 /*@observer@*/ 00371 static const char * 00372 previous_line(const char *p, const char *startptr) 00373 /*@*/ 00374 { 00375 switch (_mireEL) { 00376 default: /* Just in case */ 00377 case EL_LF: 00378 p--; 00379 while (p > startptr && p[-1] != '\n') p--; 00380 return p; 00381 /*@notreached@*/ break; 00382 00383 case EL_CR: 00384 p--; 00385 while (p > startptr && p[-1] != '\n') p--; 00386 return p; 00387 /*@notreached@*/ break; 00388 00389 case EL_CRLF: 00390 for (;;) { 00391 p -= 2; 00392 while (p > startptr && p[-1] != '\n') p--; 00393 if (p <= startptr + 1 || p[-2] == '\r') return p; 00394 } 00395 /*@notreached@*/ return p; /* But control should never get here */ 00396 /*@notreached@*/ break; 00397 00398 case EL_ANY: 00399 case EL_ANYCRLF: 00400 if (*(--p) == '\n' && p > startptr && p[-1] == '\r') p--; 00401 if (GF_ISSET(UTF8)) while ((((unsigned)*p) & 0xc0) == 0x80) p--; 00402 00403 while (p > startptr) { 00404 const char *pp = p - 1; 00405 unsigned int c; 00406 00407 if (GF_ISSET(UTF8)) { 00408 size_t extra = 0; 00409 while ((((unsigned)*pp) & 0xc0) == 0x80) pp--; 00410 c = (unsigned)*((unsigned char *)pp); 00411 if (c >= 0xc0) { 00412 size_t gcii, gcss; 00413 extra = (size_t)utf8_table4[c & 0x3f]; /* No. of additional bytes */ 00414 gcss = 6*extra; 00415 c = (c & utf8_table3[extra]) << gcss; 00416 for (gcii = 1; gcii <= extra; gcii++) { 00417 gcss -= 6; 00418 c |= ((unsigned)pp[gcii] & 0x3f) << gcss; 00419 } 00420 } 00421 } else 00422 c = (unsigned)*((unsigned char *)pp); 00423 00424 if (_mireEL == EL_ANYCRLF) { 00425 switch (c) { 00426 case 0x0a: /* LF */ 00427 case 0x0d: /* CR */ 00428 return p; 00429 /*@notreached@*/ /*@switchbreak@*/ break; 00430 00431 default: 00432 /*@switchbreak@*/ break; 00433 } 00434 } else { 00435 switch (c) { 00436 case 0x0a: /* LF */ 00437 case 0x0b: /* VT */ 00438 case 0x0c: /* FF */ 00439 case 0x0d: /* CR */ 00440 case 0x85: /* NEL */ 00441 case 0x2028: /* LS */ 00442 case 0x2029: /* PS */ 00443 return p; 00444 /*@notreached@*/ /*@switchbreak@*/ break; 00445 00446 default: 00447 /*@switchbreak@*/ break; 00448 } 00449 } 00450 00451 p = pp; /* Back one character */ 00452 } /* End of loop for ANY case */ 00453 00454 return startptr; /* Hit start of data */ 00455 /*@notreached@*/ break; 00456 } /* End of overall switch */ 00457 /*@notreached@*/ 00458 } 00459 00460 /************************************************* 00461 * Print the previous "after" lines 00462 * 00463 * This is called if we are about to lose said lines because of buffer filling, 00464 * and at the end of the file. The data in the line is written using fwrite() so 00465 * that a binary zero does not terminate it. 00466 * 00467 * @param lastmatchnumber the number of the last matching line, plus one 00468 * @param lastmatchrestart where we restarted after the last match 00469 * @param endptr end of available data 00470 * @param printname filename for printing (or NULL) 00471 */ 00472 static void do_after_lines(int lastmatchnumber, const char *lastmatchrestart, 00473 const char *endptr, /*@null@*/ const char *printname) 00474 /*@globals fileSystem @*/ 00475 /*@modifies fileSystem @*/ 00476 { 00477 int count = 0; 00478 while (lastmatchrestart < endptr && count++ < after_context) { 00479 const char *pp = lastmatchrestart; 00480 size_t ellength; 00481 if (printname != NULL) fprintf(stdout, "%s-", printname); 00482 if (GF_ISSET(LNUMBER)) fprintf(stdout, "%d-", lastmatchnumber++); 00483 pp = end_of_line(pp, endptr, &ellength); 00484 fwrite_check(lastmatchrestart, 1, pp - lastmatchrestart, stdout); 00485 lastmatchrestart = pp; 00486 } 00487 } 00488 00489 /************************************************* 00490 * Grep an individual file 00491 * 00492 * This is called from grep_or_recurse() below. It uses a buffer that is three 00493 * times the value of MBUFTHIRD. The matching point is never allowed to stray 00494 * into the top third of the buffer, thus keeping more of the file available 00495 * for context printing or for multiline scanning. For large files, the pointer 00496 * will be in the middle third most of the time, so the bottom third is 00497 * available for "before" context printing. 00498 * 00499 * @param handle the fopen'd FILE stream for a normal file 00500 * the gzFile pointer when reading is via libz 00501 * the BZFILE pointer when reading is via libbz2 00502 * @param frtype FR_PLAIN, FR_LIBZ, or FR_LIBBZ2 00503 * @param printname the file name if it is to be printed for each match 00504 * or NULL if the file name is not to be printed 00505 * it cannot be NULL if filenames[_nomatch]_only is set 00506 * @return 0: at least one match, 1: no match, 2: read error (bz2) 00507 */ 00508 static int 00509 pcregrep(FD_t fd, const char *printname) 00510 /*@globals error_count, pattern_list, fileSystem @*/ 00511 /*@modifies fd, error_count, pattern_list, fileSystem @*/ 00512 { 00513 int rc = 1; 00514 int linenumber = 1; 00515 int lastmatchnumber = 0; 00516 int count = 0; 00517 int filepos = 0; 00518 int offsets[99]; 00519 const char *lastmatchrestart = NULL; 00520 char buffer[3*MBUFTHIRD]; 00521 const char *ptr = buffer; 00522 const char *endptr; 00523 size_t bufflength; 00524 static BOOL hyphenpending = FALSE; 00525 BOOL endhyphenpending = FALSE; 00526 BOOL invert = (GF_ISSET(INVERT) ? TRUE : FALSE); 00527 00528 bufflength = Fread(buffer, 1, 3*MBUFTHIRD, fd); 00529 endptr = buffer + bufflength; 00530 00531 /* 00532 * Loop while the current pointer is not at the end of the file. For large 00533 * files, endptr will be at the end of the buffer when we are in the middle 00534 * of the file, but ptr will never get there, because as soon as it gets 00535 * over 2/3 of the way, the buffer is shifted left and re-filled. 00536 */ 00537 while (ptr < endptr) { 00538 int i; 00539 int mrc = 0; 00540 BOOL match = FALSE; 00541 const char *matchptr = ptr; 00542 const char *t = ptr; 00543 size_t length, linelength; 00544 size_t endlinelength; 00545 00546 /* 00547 * At this point, ptr is at the start of a line. We need to find the 00548 * length of the subject string to pass to mireRegexec(). In multiline 00549 * mode, it is the length remainder of the data in the buffer. 00550 * Otherwise, it is the length of the next line. After matching, we 00551 * always advance by the length of the next line. In multiline mode 00552 * the PCRE_FIRSTLINE option is used for compiling, so that any match 00553 * is constrained to be in the first line. 00554 */ 00555 t = end_of_line(t, endptr, &endlinelength); 00556 linelength = t - ptr - endlinelength; 00557 length = GF_ISSET(MULTILINE) ? (size_t)(endptr - ptr) : linelength; 00558 00559 /* 00560 * We come back here after a match when the -o,--only-matching option 00561 * is set, in order to find any further matches in the same line. 00562 */ 00563 ONLY_MATCHING_RESTART: 00564 00565 /* 00566 * Run through all the patterns until one matches. Note that we don't 00567 * include the final newline in the subject string. 00568 */ 00569 for (i = 0; i < pattern_count; i++) { 00570 miRE mire = pattern_list + i; 00571 int xx; 00572 00573 /*@-onlytrans@*/ 00574 /* Set sub-string offset array. */ 00575 xx = mireSetEOptions(mire, offsets, 99); 00576 00577 /* XXX WATCHOUT: mireRegexec w length=0 does strlen(matchptr)! */ 00578 mrc = (length > 0 ? mireRegexec(mire, matchptr, length) : -1); 00579 /*@=onlytrans@*/ 00580 if (mrc >= 0) { match = TRUE; /*@innerbreak@*/ break; } 00581 if (mrc < -1) { /* XXX -1 == NOMATCH, otherwise error. */ 00582 fprintf(stderr, _("%s: pcre_exec() error %d while matching "), __progname, mrc); 00583 if (pattern_count > 1) fprintf(stderr, _("pattern number %d to "), i+1); 00584 fprintf(stderr, _("this line:\n")); 00585 fwrite_check(matchptr, 1, linelength, stderr); /* In case binary zero included */ 00586 fprintf(stderr, "\n"); 00587 #if defined(PCRE_ERROR_MATCHLIMIT) 00588 if (error_count == 0 && 00589 (mrc == PCRE_ERROR_MATCHLIMIT || mrc == PCRE_ERROR_RECURSIONLIMIT)) 00590 { 00591 fprintf(stderr, 00592 _("%s: error %d means that a resource limit was exceeded\n"), 00593 __progname, mrc); 00594 fprintf(stderr, 00595 _("%s: check your regex for nested unlimited loops\n"), 00596 __progname); 00597 } 00598 #endif 00599 if (error_count++ > 20) { 00600 fprintf(stderr, _("%s: too many errors - abandoned\n"), 00601 __progname); 00602 /*@-exitarg@*/ 00603 exit(2); 00604 /*@=exitarg@*/ 00605 } 00606 match = invert; /* No more matching; don't show the line again */ 00607 /*@innerbreak@*/ break; 00608 } 00609 } 00610 00611 /* If it's a match or a not-match (as required), do what's wanted. */ 00612 if (match != invert) { 00613 BOOL hyphenprinted = FALSE; 00614 00615 /* We've failed if we want a file that doesn't have any matches. */ 00616 if (filenames == FN_NOMATCH_ONLY) { 00617 rc = 1; 00618 goto exit; 00619 } 00620 00621 /* Just count if just counting is wanted. */ 00622 if (GF_ISSET(COUNT)) count++; 00623 00624 /* 00625 * If all we want is a file name, there is no need to scan any 00626 * more lines in the file. 00627 */ 00628 else if (filenames == FN_ONLY) { 00629 if (printname != NULL) fprintf(stdout, "%s\n", printname); 00630 rc = 0; 00631 goto exit; 00632 } 00633 00634 /* Likewise, if all we want is a yes/no answer. */ 00635 else if (GF_ISSET(QUIET)) { 00636 rc = 0; 00637 goto exit; 00638 } 00639 00640 /* 00641 * The --only-matching option prints just the substring that 00642 * matched, and the --file-offsets and --line-offsets options 00643 * output offsets for the matching substring (they both force 00644 * --only-matching). None of these options prints any context. 00645 * Afterwards, adjust the start and length, and then jump back 00646 * to look for further matches in the same line. If we are in 00647 * invert mode, however, nothing is printed - this could be 00648 * still useful because the return code is set. 00649 */ 00650 else if (GF_ISSET(ONLY_MATCHING)) { 00651 if (!GF_ISSET(INVERT)) { 00652 if (printname != NULL) fprintf(stdout, "%s:", printname); 00653 if (GF_ISSET(LNUMBER)) fprintf(stdout, "%d:", linenumber); 00654 if (GF_ISSET(LOFFSETS)) 00655 fprintf(stdout, "%d,%d", (int)(matchptr + offsets[0] - ptr), 00656 offsets[1] - offsets[0]); 00657 else if (GF_ISSET(FOFFSETS)) 00658 fprintf(stdout, "%d,%d", (int)(filepos + matchptr + offsets[0] - ptr), 00659 offsets[1] - offsets[0]); 00660 else 00661 fwrite_check(matchptr + offsets[0], 1, offsets[1] - offsets[0], stdout); 00662 fprintf(stdout, "\n"); 00663 matchptr += offsets[1]; 00664 length -= offsets[1]; 00665 match = FALSE; 00666 goto ONLY_MATCHING_RESTART; 00667 } 00668 } 00669 00670 /* 00671 * This is the default case when none of the above options is set. 00672 * We print the matching lines(s), possibly preceded and/or 00673 * followed by other lines of context. 00674 */ 00675 else { 00676 /* 00677 * See if there is a requirement to print some "after" lines 00678 * from a previous match. We never print any overlaps. 00679 */ 00680 if (after_context > 0 00681 && lastmatchnumber > 0 && lastmatchrestart != NULL) 00682 { 00683 size_t ellength; 00684 int linecount = 0; 00685 const char *p = lastmatchrestart; 00686 00687 while (p < ptr && linecount < after_context) { 00688 p = end_of_line(p, ptr, &ellength); 00689 linecount++; 00690 } 00691 00692 /* 00693 * It is important to advance lastmatchrestart during this 00694 * printing so that it interacts correctly with any 00695 * "before" printing below. Print each line's data using 00696 * fwrite() in case there are binary zeroes. 00697 */ 00698 while (lastmatchrestart < p) { 00699 const char *pp = lastmatchrestart; 00700 if (printname != NULL) fprintf(stdout, "%s-", printname); 00701 if (GF_ISSET(LNUMBER)) fprintf(stdout, "%d-", lastmatchnumber++); 00702 pp = end_of_line(pp, endptr, &ellength); 00703 fwrite_check(lastmatchrestart, 1, pp - lastmatchrestart, stdout); 00704 lastmatchrestart = pp; 00705 } 00706 if (lastmatchrestart != ptr) hyphenpending = TRUE; 00707 } 00708 00709 /* If there were non-contiguous lines printed above, insert hyphens. */ 00710 if (hyphenpending) { 00711 fprintf(stdout, "--\n"); 00712 hyphenpending = FALSE; 00713 hyphenprinted = TRUE; 00714 } 00715 00716 /* 00717 * See if there is a requirement to print some "before" lines 00718 * for this match. Again, don't print overlaps. 00719 */ 00720 if (before_context > 0) { 00721 int linecount = 0; 00722 const char *p = ptr; 00723 00724 while (p > buffer && (lastmatchnumber == 0 || p > lastmatchrestart) && 00725 linecount < before_context) 00726 { 00727 linecount++; 00728 p = previous_line(p, buffer); 00729 } 00730 00731 if (lastmatchnumber > 0 && p > lastmatchrestart && !hyphenprinted) 00732 fprintf(stdout, "--\n"); 00733 00734 while (p < ptr) { 00735 size_t ellength; 00736 const char *pp = p; 00737 if (printname != NULL) fprintf(stdout, "%s-", printname); 00738 if (GF_ISSET(LNUMBER)) fprintf(stdout, "%d-", linenumber - linecount--); 00739 pp = end_of_line(pp, endptr, &ellength); 00740 fwrite_check(p, 1, pp - p, stdout); 00741 p = pp; 00742 } 00743 } 00744 00745 /* 00746 * Now print the matching line(s); ensure we set hyphenpending 00747 * at the end of the file if any context lines are being output. 00748 */ 00749 if (after_context > 0 || before_context > 0) 00750 endhyphenpending = TRUE; 00751 00752 if (printname != NULL) fprintf(stdout, "%s:", printname); 00753 if (GF_ISSET(LNUMBER)) fprintf(stdout, "%d:", linenumber); 00754 00755 /* 00756 * In multiline mode, we want to print to the end of the line 00757 * in which the end of the matched string is found, so we 00758 * adjust linelength and the line number appropriately, but 00759 * only when there actually was a match (invert not set). 00760 * Because the PCRE_FIRSTLINE option is set, the start of 00761 * the match will always be before the first newline sequence. 00762 * */ 00763 if (GF_ISSET(MULTILINE)) { 00764 size_t ellength; 00765 const char *endmatch = ptr; 00766 if (!GF_ISSET(INVERT)) { 00767 endmatch += offsets[1]; 00768 t = ptr; 00769 while (t < endmatch) { 00770 t = end_of_line(t, endptr, &ellength); 00771 if (t <= endmatch) linenumber++; else /*@innerbreak@*/ break; 00772 } 00773 } 00774 endmatch = end_of_line(endmatch, endptr, &ellength); 00775 linelength = endmatch - ptr - ellength; 00776 } 00777 /* 00778 * NOTE: Use only fwrite() to output the data line, so that 00779 * binary zeroes are treated as just another data character. 00780 */ 00781 00782 /* We have to split the line(s) up if coloring. */ 00783 if (GF_ISSET(COLOR) && color_string != NULL) { 00784 fwrite_check(ptr, 1, offsets[0], stdout); 00785 fprintf(stdout, "%c[%sm", 0x1b, color_string); 00786 fwrite_check(ptr + offsets[0], 1, offsets[1] - offsets[0], stdout); 00787 fprintf(stdout, "%c[00m", 0x1b); 00788 fwrite_check(ptr + offsets[1], 1, (linelength + endlinelength) - offsets[1], 00789 stdout); 00790 } 00791 else fwrite_check(ptr, 1, linelength + endlinelength, stdout); 00792 } 00793 00794 /* End of doing what has to be done for a match */ 00795 rc = 0; /* Had some success */ 00796 00797 /* 00798 * Remember where the last match happened for after_context. 00799 * We remember where we are about to restart, and that line's 00800 * number. 00801 */ 00802 lastmatchrestart = ptr + linelength + endlinelength; 00803 lastmatchnumber = linenumber + 1; 00804 } 00805 00806 /* 00807 * For a match in multiline inverted mode (which of course did not 00808 * cause anything to be printed), we have to move on to the end of 00809 * the match before proceeding. 00810 */ 00811 if (GF_ISSET(MULTILINE) && GF_ISSET(INVERT) && match) { 00812 size_t ellength; 00813 const char *endmatch = ptr + offsets[1]; 00814 t = ptr; 00815 while (t < endmatch) { 00816 t = end_of_line(t, endptr, &ellength); 00817 if (t <= endmatch) linenumber++; else /*@innerbreak@*/ break; 00818 } 00819 endmatch = end_of_line(endmatch, endptr, &ellength); 00820 linelength = endmatch - ptr - ellength; 00821 } 00822 00823 /* 00824 * Advance to after the newline and increment the line number. The 00825 * file offset to the current line is maintained in filepos. 00826 */ 00827 ptr += linelength + endlinelength; 00828 filepos += linelength + endlinelength; 00829 linenumber++; 00830 00831 /* 00832 * If we haven't yet reached the end of the file (the buffer is full), 00833 * and the current point is in the top 1/3 of the buffer, slide the 00834 * buffer down by 1/3 and refill it. Before we do this, if some 00835 * unprinted "after" lines are about to be lost, print them. 00836 */ 00837 if (bufflength >= sizeof(buffer) && ptr > buffer + 2*MBUFTHIRD) { 00838 if (after_context > 0 && 00839 lastmatchnumber > 0 && lastmatchrestart != NULL && 00840 lastmatchrestart < buffer + MBUFTHIRD) 00841 { 00842 if (after_context > 0 00843 && lastmatchnumber > 0 && lastmatchrestart != NULL) 00844 { 00845 do_after_lines(lastmatchnumber, lastmatchrestart, 00846 endptr, printname); 00847 hyphenpending = TRUE; 00848 } 00849 lastmatchnumber = 0; 00850 } 00851 00852 /* Now do the shuffle */ 00853 00854 /*@-modobserver@*/ /* XXX buffer <=> t aliasing */ 00855 memmove(buffer, buffer + MBUFTHIRD, 2*MBUFTHIRD); 00856 ptr -= MBUFTHIRD; 00857 00858 bufflength = 2*MBUFTHIRD; 00859 bufflength += Fread(buffer + bufflength, 1, MBUFTHIRD, fd); 00860 endptr = buffer + bufflength; 00861 /*@=modobserver@*/ 00862 00863 /* Adjust any last match point */ 00864 if (lastmatchnumber > 0 && lastmatchrestart != NULL) 00865 lastmatchrestart -= MBUFTHIRD; 00866 } 00867 } /* Loop through the whole file */ 00868 00869 /* 00870 * End of file; print final "after" lines if wanted; do_after_lines sets 00871 * hyphenpending if it prints something. 00872 */ 00873 if (!GF_ISSET(ONLY_MATCHING) && !GF_ISSET(COUNT)) { 00874 if (after_context > 0 00875 && lastmatchnumber > 0 && lastmatchrestart != NULL) 00876 { 00877 do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname); 00878 hyphenpending = TRUE; 00879 } 00880 hyphenpending |= endhyphenpending; 00881 } 00882 00883 /* 00884 * Print the file name if we are looking for those without matches and 00885 * there were none. If we found a match, we won't have got this far. 00886 */ 00887 if (filenames == FN_NOMATCH_ONLY) { 00888 if (printname != NULL) fprintf(stdout, "%s\n", printname); 00889 rc = 0; 00890 goto exit; 00891 } 00892 00893 /* Print the match count if wanted */ 00894 if (GF_ISSET(COUNT)) { 00895 if (printname != NULL) fprintf(stdout, "%s:", printname); 00896 fprintf(stdout, "%d\n", count); 00897 } 00898 00899 exit: 00900 return rc; 00901 } 00902 00909 static int chkSuffix(const char * fn, const char * suffix) 00910 /*@*/ 00911 { 00912 size_t flen = strlen(fn); 00913 size_t slen = strlen(suffix); 00914 return (flen > slen && !strcmp(fn + flen - slen, suffix)); 00915 } 00916 00917 /************************************************* 00918 * Grep a file or recurse into a directory. 00919 * 00920 * Given a path name, if it's a directory, scan all the files if we are 00921 * recursing; if it's a file, grep it. 00922 * 00923 * @param pathname the path to investigate 00924 * @param dir_recurse TRUE if recursing is wanted (-r or -drecurse) 00925 * @param only_one_at_top TRUE if the path is the only one at toplevel 00926 * @return 0: at least one match, 1: no match, 2: read error (bz2) 00927 * 00928 * @note file opening failures are suppressed if "silent" is set. 00929 */ 00930 static int 00931 grep_or_recurse(const char *pathname, BOOL dir_recurse, BOOL only_one_at_top) 00932 /*@globals h_errno, fileSystem, internalState @*/ 00933 /*@modifies h_errno, fileSystem, internalState @*/ 00934 { 00935 struct stat sb, *st = &sb; 00936 int rc = 1; 00937 size_t pathlen; 00938 FD_t fd = NULL; 00939 const char * fmode = "r.ufdio"; 00940 int xx; 00941 00942 /* If the file name is "-" we scan stdin */ 00943 if (!strcmp(pathname, "-")) { 00944 fd = fdDup(STDIN_FILENO); 00945 goto openthestream; 00946 } 00947 00948 if ((xx = Stat(pathname, st)) != 0) 00949 goto openthestream; /* XXX exit with Strerror(3) message. */ 00950 00951 /* 00952 * If the file is a directory, skip if skipping or if we are recursing, 00953 * scan each file within it, subject to any include or exclude patterns 00954 * that were set. The scanning code is localized so it can be made 00955 * system-specific. 00956 */ 00957 if (S_ISDIR(st->st_mode)) 00958 switch (dee_action) { 00959 case dee_READ: 00960 break; 00961 case dee_SKIP: 00962 rc = 1; 00963 goto exit; 00964 /*@notreached@*/ break; 00965 case dee_RECURSE: 00966 { char buffer[1024]; 00967 DIR *dir = Opendir(pathname); 00968 struct dirent *dp; 00969 00970 if (dir == NULL) { 00971 if (!GF_ISSET(SILENT)) 00972 fprintf(stderr, _("%s: Failed to open directory %s: %s\n"), 00973 __progname, pathname, strerror(errno)); 00974 rc = 2; 00975 goto exit; 00976 } 00977 00978 while ((dp = Readdir(dir)) != NULL) { 00979 char sep = '/'; 00980 00981 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 00982 continue; 00983 00984 xx = snprintf(buffer, sizeof(buffer), "%.512s%c%.128s", 00985 pathname, sep, dp->d_name); 00986 buffer[sizeof(buffer)-1] = '\0'; 00987 00988 /*@-onlytrans@*/ 00989 if (mireApply(excludeMire, nexcludes, buffer, 0, -1) >= 0) 00990 continue; 00991 00992 if (mireApply(includeMire, nincludes, buffer, 0, +1) < 0) 00993 continue; 00994 /*@=onlytrans@*/ 00995 00996 xx = grep_or_recurse(buffer, dir_recurse, FALSE); 00997 if (xx > 1) rc = xx; 00998 else if (xx == 0 && rc == 1) rc = 0; 00999 } 01000 xx = Closedir(dir); 01001 goto exit; 01002 } /*@notreached@*/ break; 01003 } 01004 01005 /* 01006 * If the file is not a directory and not a regular file, skip it if 01007 * that's been requested. 01008 */ 01009 else if (!S_ISREG(st->st_mode) && DEE_action == DEE_SKIP) { 01010 rc = 1; 01011 goto exit; 01012 } 01013 01014 /* 01015 * Control reaches here if we have a regular file, or if we have a 01016 * directory and recursion or skipping was not requested, or if we have 01017 * anything else and skipping was not requested. The scan proceeds. 01018 * If this is the first and only argument at top level, we don't show 01019 * the file name, unless we are only showing the file name, or the 01020 * filename was forced (-H). 01021 */ 01022 pathlen = strlen(pathname); 01023 01024 /* Identify how to Fopen the file from the suffix. */ 01025 if (chkSuffix(pathname, ".gz")) 01026 fmode = "r.gzdio"; /* Open with zlib decompression. */ 01027 else if (chkSuffix(pathname, ".bz2")) 01028 fmode = "r.bzdio"; /* Open with bzip2 decompression. */ 01029 else if (chkSuffix(pathname, ".lzma")) 01030 fmode = "r.lzdio"; /* Open with lzma decompression. */ 01031 else if (chkSuffix(pathname, ".xz")) 01032 fmode = "r.xzdio"; /* Open with xz decompression. */ 01033 else 01034 fmode = "r.ufdio"; 01035 01036 /* Open the stream. */ 01037 fd = Fopen(pathname, fmode); 01038 openthestream: 01039 if (fd == NULL || Ferror(fd)) { 01040 if (!GF_ISSET(SILENT)) 01041 fprintf(stderr, _("%s: Failed to open %s: %s\n"), 01042 __progname, pathname, Fstrerror(fd)); 01043 rc = 2; 01044 goto exit; 01045 } 01046 01047 /* Now grep the file */ 01048 rc = pcregrep(fd, (filenames > FN_DEFAULT || 01049 (filenames == FN_DEFAULT && !only_one_at_top))? pathname : NULL); 01050 01051 if (fd != NULL) 01052 (void) rpmswAdd(&grep_readops, fdstat_op(fd, FDSTAT_READ)); 01053 01054 exit: 01055 if (fd != NULL) 01056 xx = Fclose(fd); 01057 return rc; /* Pass back the yield from pcregrep(). */ 01058 } 01059 01060 /************************************************* 01061 * Compile a single pattern. 01062 * 01063 * When the -F option has been used, this is called for each substring. 01064 * Otherwise it's called for each supplied pattern. 01065 * 01066 * @param pattern the pattern string 01067 * @param filename the file name (NULL for a command-line pattern) 01068 * @param count pattern index (0 is single pattern) 01069 * @return TRUE on success, FALSE after an error 01070 */ 01071 static BOOL 01072 compile_single_pattern(const char *pattern, 01073 /*@null@*/ const char *filename, int count) 01074 /*@globals pattern_list, pattern_count, fileSystem @*/ 01075 /*@modifies pattern_list, pattern_count, fileSystem @*/ 01076 { 01077 miRE mire; 01078 char buffer[MBUFTHIRD + 16]; 01079 int xx; 01080 01081 if (pattern_count >= MAX_PATTERN_COUNT) { 01082 fprintf(stderr, _("%s: Too many patterns (max %d)\n"), __progname, 01083 MAX_PATTERN_COUNT); 01084 return FALSE; 01085 } 01086 01087 sprintf(buffer, "%s%.*s%s", prefix[(int)(grepFlags & 0x7)], 01088 MBUFTHIRD, pattern, suffix[(int)(grepFlags & 0x7)]); 01089 01090 mire = pattern_list + pattern_count; 01091 /*@-onlytrans@*/ 01092 /* XXX initialize mire->{mode,tag,options,table}. */ 01093 xx = mireSetCOptions(mire, grepMode, 0, 0, _mirePCREtables); 01094 01095 if (!mireRegcomp(mire, buffer)) { 01096 pattern_count++; 01097 return TRUE; 01098 } 01099 /*@=onlytrans@*/ 01100 /* Handle compile errors */ 01101 mire->erroff -= (int)strlen(prefix[(int)(grepFlags & 0x7)]); 01102 if (mire->erroff < 0) 01103 mire->erroff = 0; 01104 if (mire->erroff > (int)strlen(pattern)) 01105 mire->erroff = (int)strlen(pattern); 01106 01107 fprintf(stderr, _("%s: Error in"), __progname); 01108 if (filename == NULL) 01109 fprintf(stderr, _(" command-line %d"), count); 01110 else 01111 fprintf(stderr, _(" file:line %s:%d"), filename, count); 01112 fprintf(stderr, _(" regex at offset %d: %s\n"), mire->erroff, mire->errmsg); 01113 return FALSE; 01114 } 01115 01116 /************************************************* 01117 * Compile one supplied pattern. 01118 * 01119 * When the -F option has been used, each string may be a list of strings, 01120 * separated by line breaks. They will be matched literally. 01121 * 01122 * @param pattern the pattern string 01123 * @param filename the file name, or NULL for a command-line pattern 01124 * @param count pattern index (0 is single pattern) 01125 * @return TRUE on success, FALSE after an error 01126 */ 01127 static BOOL 01128 compile_pattern(const char *pattern, /*@null@*/ const char *filename, int count) 01129 /*@globals fileSystem @*/ 01130 /*@modifies fileSystem @*/ 01131 { 01132 if (GF_ISSET(FIXED_STRINGS) != 0) { 01133 const char *eop = pattern + strlen(pattern); 01134 char buffer[MBUFTHIRD]; 01135 for(;;) { 01136 size_t ellength; 01137 const char *p = end_of_line(pattern, eop, &ellength); 01138 if (ellength == 0) 01139 return compile_single_pattern(pattern, filename, count); 01140 sprintf(buffer, "%.*s", (int)(p - pattern - ellength), pattern); 01141 pattern = p; 01142 if (!compile_single_pattern(buffer, filename, count)) 01143 return FALSE; 01144 } 01145 } 01146 else return compile_single_pattern(pattern, filename, count); 01147 } 01148 01154 static int mireLoadPatternFiles(/*@null@*/ ARGV_t files) 01155 /*@globals h_errno, fileSystem, internalState @*/ 01156 /*@modifies h_errno, fileSystem, internalState @*/ 01157 { 01158 const char *fn; 01159 int rc = -1; /* assume failure */ 01160 01161 if (files != NULL) /* note rc=0 return with no files to load. */ 01162 while ((fn = *files++) != NULL) { 01163 char buffer[MBUFTHIRD]; 01164 int linenumber; 01165 FD_t fd = NULL; 01166 FILE *fp; 01167 01168 if (strcmp(fn, "-") == 0) { 01169 fd = NULL; 01170 fp = stdin; 01171 fn = stdin_name; /* XXX use the stdin display name */ 01172 } else { 01173 /* XXX .fpio is needed because of fgets(3) usage. */ 01174 fd = Fopen(fn, "r.fpio"); 01175 if (fd == NULL || Ferror(fd) || (fp = fdGetFILE(fd)) == NULL) { 01176 fprintf(stderr, _("%s: Failed to open %s: %s\n"), 01177 __progname, fn, Fstrerror(fd)); 01178 if (fd != NULL) (void) Fclose(fd); 01179 fd = NULL; 01180 fp = NULL; 01181 goto exit; 01182 } 01183 } 01184 01185 linenumber = 0; 01186 while (fgets(buffer, MBUFTHIRD, fp) != NULL) { 01187 char *se = buffer + (int)strlen(buffer); 01188 while (se > buffer && xisspace((int)se[-1])) 01189 se--; 01190 *se = '\0'; 01191 linenumber++; 01192 /* Skip blank lines */ 01193 if (buffer[0] == '\0') /*@innercontinue@*/ continue; 01194 if (!compile_pattern(buffer, fn, linenumber)) 01195 goto exit; 01196 } 01197 01198 if (fd != NULL) { 01199 (void) rpmswAdd(&grep_readops, fdstat_op(fd, FDSTAT_READ)); 01200 (void) Fclose(fd); 01201 fd = NULL; 01202 } 01203 } 01204 rc = 0; 01205 01206 exit: 01207 return rc; 01208 } 01209 01210 /* Options without a single-letter equivalent get a negative value. This can be 01211 used to identify them. */ 01212 01215 static void grepArgCallback(poptContext con, 01216 /*@unused@*/ enum poptCallbackReason reason, 01217 const struct poptOption * opt, const char * arg, 01218 /*@unused@*/ void * data) 01219 /*@globals color_string, dee_action, DEE_action, grepFlags, fileSystem @*/ 01220 /*@modifies color_string, dee_action, DEE_action, grepFlags, fileSystem @*/ 01221 { 01222 /* XXX avoid accidental collisions with POPT_BIT_SET for flags */ 01223 if (opt->arg == NULL) 01224 switch (opt->val) { 01225 01226 case 'd': 01227 if (!strcmp(arg, "read")) dee_action = dee_READ; 01228 else if (!strcmp(arg, "recurse")) dee_action = dee_RECURSE; 01229 else if (!strcmp(arg, "skip")) dee_action = dee_SKIP; 01230 else { 01231 fprintf(stderr, _("%s: Invalid value \"%s\" for -d\n"), 01232 __progname, arg); 01233 /*@-exitarg@*/ exit(2); /*@=exitarg@*/ 01234 /*@notreached@*/ 01235 } 01236 break; 01237 case 'D': 01238 if (!strcmp(arg, "read")) DEE_action = DEE_READ; 01239 else if (!strcmp(arg, "skip")) DEE_action = DEE_SKIP; 01240 else { 01241 fprintf(stderr, _("%s: Invalid value \"%s\" for -D\n"), 01242 __progname, arg); 01243 /*@-exitarg@*/ exit(2); /*@=exitarg@*/ 01244 /*@notreached@*/ 01245 } 01246 break; 01247 case 'C': 01248 if (!strcmp(arg, "never")) 01249 grepFlags &= ~GREP_FLAGS_COLOR; 01250 else if (!strcmp(arg, "always")) 01251 grepFlags |= GREP_FLAGS_COLOR; 01252 else if (!strcmp(arg, "auto")) { 01253 if (isatty(fileno(stdout))) 01254 grepFlags |= GREP_FLAGS_COLOR; 01255 else 01256 grepFlags &= ~GREP_FLAGS_COLOR; 01257 } else { 01258 fprintf(stderr, _("%s: Unknown color setting \"%s\"\n"), 01259 __progname, arg); 01260 /*@-exitarg@*/ exit(2); /*@=exitarg@*/ 01261 /*@notreached@*/ 01262 } 01263 color_string = _free(color_string); 01264 if (GF_ISSET(COLOR)) { 01265 char *cs = getenv("PCREGREP_COLOUR"); 01266 if (cs == NULL) cs = getenv("PCREGREP_COLOR"); 01267 color_string = xstrdup(cs != NULL ? cs : "1;31"); 01268 } 01269 break; 01270 01271 case 'V': 01272 #if defined(WITH_PCRE) 01273 /*@-evalorderuncon -moduncon @*/ 01274 fprintf(stderr, _("%s %s (PCRE version %s)\n"), __progname, VERSION, pcre_version()); 01275 /*@=evalorderuncon =moduncon @*/ 01276 #else 01277 fprintf(stderr, _("%s %s (without PCRE)\n"), __progname, VERSION); 01278 #endif 01279 exit(0); 01280 /*@notreached@*/ break; 01281 default: 01282 fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val); 01283 poptPrintUsage(con, stderr, 0); 01284 /*@-exitarg@*/ exit(2); /*@=exitarg@*/ 01285 /*@notreached@*/ break; 01286 } 01287 } 01288 01291 /*@+enumint@*/ 01292 /*@unchecked@*/ 01293 static struct poptOption optionsTable[] = { 01294 /*@-type@*/ /* FIX: cast? */ 01295 { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE, 01296 grepArgCallback, 0, NULL, NULL }, 01297 /*@=type@*/ 01298 01299 { "after-context", 'A', POPT_ARG_INT, &after_context, 0, 01300 N_("set number of following context lines"), N_("=number") }, 01301 { "before-context", 'B', POPT_ARG_INT, &before_context, 0, 01302 N_("set number of prior context lines"), N_("=number") }, 01303 { "context", 'C', POPT_ARG_INT, &both_context, 0, 01304 N_("set number of context lines, before & after"), N_("=number") }, 01305 { "count", 'c', POPT_BIT_SET, &grepFlags, GREP_FLAGS_COUNT, 01306 N_("print only a count of matching lines per FILE"), NULL }, 01307 { "color", '\0', POPT_ARG_STRING, NULL, (int)'C', 01308 N_("matched text color option (auto|always|never)"), N_("=option") }, 01309 { "colour", '\0', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, NULL, (int)'C', 01310 N_("matched text colour option (auto|always|never)"), N_("=option") }, 01311 /* XXX HACK: there is a shortName option conflict with -D,--define */ 01312 { "devices", 'D', POPT_ARG_STRING, NULL, (int)'D', 01313 N_("device, FIFO, or socket action (read|skip)"), N_("=action") }, 01314 { "directories", 'd', POPT_ARG_STRING, NULL, (int)'d', 01315 N_("directory action (read|skip|recurse)"), N_("=action") }, 01316 { "regex", 'e', POPT_ARG_ARGV, &patterns, 0, 01317 N_("specify pattern (may be used more than once)"), N_("(p)") }, 01318 { "fixed_strings", 'F', POPT_BIT_SET, &grepFlags, GREP_FLAGS_FIXED_STRINGS, 01319 N_("patterns are sets of newline-separated strings"), NULL }, 01320 { "file", 'f', POPT_ARG_ARGV, &pattern_filenames, 0, 01321 N_("read patterns from file (may be used more than once)"), 01322 N_("=path") }, 01323 { "file-offsets", '\0', POPT_BIT_SET, &grepFlags, GREP_FLAGS_FOFFSETS, 01324 N_("output file offsets, not text"), NULL }, 01325 { "with-filename", 'H', POPT_ARG_VAL, &filenames, FN_FORCE, 01326 N_("force the prefixing filename on output"), NULL }, 01327 { "no-filename", 'h', POPT_ARG_VAL, &filenames, FN_NONE, 01328 N_("suppress the prefixing filename on output"), NULL }, 01329 { "ignore-case", 'i', POPT_BIT_SET, &grepFlags, GREP_FLAGS_CASELESS, 01330 N_("ignore case distinctions"), NULL }, 01331 { "files-with-matches", 'l', POPT_ARG_VAL, &filenames, FN_ONLY, 01332 N_("print only FILE names containing matches"), NULL }, 01333 { "files-without-match", 'L', POPT_ARG_VAL, &filenames, FN_NOMATCH_ONLY, 01334 N_("print only FILE names not containing matches"), NULL }, 01335 { "label", '\0', POPT_ARG_STRING, &stdin_name, 0, 01336 N_("set name for standard input"), N_("=name") }, 01337 { "line-offsets", '\0', POPT_BIT_SET, &grepFlags, (GREP_FLAGS_LOFFSETS|GREP_FLAGS_LNUMBER), 01338 N_("output line numbers and offsets, not text"), NULL }, 01339 /* XXX TODO: --locale jiggery-pokery should be done env LC_ALL=C rpmgrep */ 01340 { "locale", '\0', POPT_ARG_STRING, &locale, 0, 01341 N_("use the named locale"), N_("=locale") }, 01342 { "multiline", 'M', POPT_BIT_SET, &grepFlags, GREP_FLAGS_MULTILINE, 01343 N_("run in multiline mode"), NULL }, 01344 { "newline", 'N', POPT_ARG_STRING, &newline, 0, 01345 N_("set newline type (CR|LF|CRLF|ANYCRLF|ANY)"), N_("=type") }, 01346 { "line-number", 'n', POPT_BIT_SET, &grepFlags, GREP_FLAGS_LNUMBER, 01347 N_("print line number with output lines"), NULL }, 01348 { "only-matching", 'o', POPT_BIT_SET, &grepFlags, GREP_FLAGS_ONLY_MATCHING, 01349 N_("show only the part of the line that matched"), NULL }, 01350 /* XXX HACK: there is a longName option conflict with --quiet */ 01351 { "quiet", 'q', POPT_BIT_SET, &grepFlags, GREP_FLAGS_QUIET, 01352 N_("suppress output, just set return code"), NULL }, 01353 { "recursive", 'r', POPT_ARG_VAL, &dee_action, dee_RECURSE, 01354 N_("recursively scan sub-directories"), NULL }, 01355 { "exclude", '\0', POPT_ARG_ARGV, &exclude_patterns, 0, 01356 N_("exclude matching files when recursing (may be used more than once)"), 01357 N_("=pattern") }, 01358 { "include", '\0', POPT_ARG_ARGV, &include_patterns, 0, 01359 N_("include matching files when recursing (may be used more than once)"), 01360 N_("=pattern") }, 01361 { "no-messages", 's', POPT_BIT_SET, &grepFlags, GREP_FLAGS_SILENT, 01362 N_("suppress error messages"), NULL }, 01363 { "silent", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN, &grepFlags, GREP_FLAGS_SILENT, 01364 N_("suppress error messages"), NULL }, 01365 { "utf-8", 'u', POPT_BIT_SET, &grepFlags, GREP_FLAGS_UTF8, 01366 N_("use UTF-8 mode"), NULL }, 01367 /* XXX HACK: there is a longName option conflict with --version */ 01368 { "version", 'V', POPT_ARG_NONE, NULL, (int)'V', 01369 N_("print version information and exit"), NULL }, 01370 /* XXX HACK: there is a shortName option conflict with -v, --verbose */ 01371 { "invert-match", 'v', POPT_BIT_SET, &grepFlags, GREP_FLAGS_INVERT, 01372 N_("select non-matching lines"), NULL }, 01373 { "word-regex", 'w', POPT_BIT_SET, &grepFlags, GREP_FLAGS_WORD_MATCH, 01374 N_("force patterns to match only as words") , N_("(p)") }, 01375 { "line-regex", 'x', POPT_BIT_SET, &grepFlags, GREP_FLAGS_LINE_MATCH, 01376 N_("force patterns to match only whole lines"), N_("(p)") }, 01377 01378 POPT_AUTOALIAS 01379 01380 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0, 01381 N_("Common options for all rpmio executables:"), 01382 NULL }, 01383 01384 POPT_AUTOHELP 01385 01386 { NULL, (char)-1, POPT_ARG_INCLUDE_TABLE, NULL, 0, 01387 N_("\ 01388 Usage: rpmgrep [OPTION...] [PATTERN] [FILE1 FILE2 ...]\n\n\ 01389 Search for PATTERN in each FILE or standard input.\n\ 01390 PATTERN must be present if neither -e nor -f is used.\n\ 01391 \"-\" can be used as a file name to mean STDIN.\n\ 01392 All files are read as plain files, without any interpretation.\n\n\ 01393 Example: rpmgrep -i 'hello.*world' menu.h main.c\ 01394 ") , NULL }, 01395 01396 { NULL, (char)-1, POPT_ARG_INCLUDE_TABLE, NULL, 0, 01397 N_("\ 01398 When reading patterns from a file instead of using a command line option,\n\ 01399 trailing white space is removed and blank lines are ignored.\n\ 01400 \n\ 01401 With no FILEs, read standard input. If fewer than two FILEs given, assume -h.\n\ 01402 ") , NULL }, 01403 01404 POPT_TABLEEND 01405 }; 01406 /*@=enumint@*/ 01407 01408 /************************************************* 01409 * Main program. 01410 * @return 0: match found, 1: no match, 2: error. 01411 */ 01412 int 01413 main(int argc, char **argv) 01414 /*@globals __assert_program_name, after_context, before_context, 01415 color_string, dee_action, 01416 exclude_patterns, excludeMire, grepFlags, 01417 include_patterns, includeMire, locale, newline, 01418 patterns, pattern_count, pattern_filenames, pattern_list, 01419 stdin_name, 01420 rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 01421 /*@modifies __assert_program_name, after_context, before_context, 01422 color_string, dee_action, 01423 exclude_patterns, excludeMire, grepFlags, 01424 include_patterns, includeMire, locale, newline, 01425 patterns, pattern_count, pattern_filenames, pattern_list, 01426 stdin_name, 01427 rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 01428 { 01429 poptContext optCon = rpmioInit(argc, argv, optionsTable); 01430 ARGV_t av = NULL; 01431 int ac = 0; 01432 int i = 0; /* assume av[0] is 1st argument. */ 01433 int rc = 1; /* assume not found. */ 01434 int j; 01435 int xx; 01436 01437 xx = rpmswEnter(&grep_totalops, -1); 01438 01439 /*@-observertrans -readonlytrans@*/ 01440 __progname = "pcregrep"; /* XXX HACK in expected name. */ 01441 /*@=observertrans =readonlytrans@*/ 01442 01443 01444 if (stdin_name == NULL) 01445 stdin_name = xstrdup("(standard input)"); 01446 01447 av = poptGetArgs(optCon); 01448 ac = argvCount(av); 01449 01450 /* If -C was used, its value is used as a default for -A and -B. */ 01451 if (both_context > 0) { 01452 if (after_context == 0) after_context = both_context; 01453 if (before_context == 0) before_context = both_context; 01454 } 01455 01456 /* 01457 * Only one of --only-matching, --file-offsets, or --line-offsets is 01458 * permitted. However, the latter two imply the --only-matching option. 01459 */ 01460 if (((GF_ISSET(FOFFSETS) || GF_ISSET(LOFFSETS)) && GF_ISSET(ONLY_MATCHING)) 01461 || (GF_ISSET(FOFFSETS) && GF_ISSET(LOFFSETS))) 01462 { 01463 fprintf(stderr, 01464 _("%s: Cannot mix --only-matching, --file-offsets and/or --line-offsets\n"), 01465 __progname); 01466 poptPrintUsage(optCon, stderr, 0); 01467 goto errxit; 01468 } 01469 01470 if (GF_ISSET(FOFFSETS) || GF_ISSET(LOFFSETS)) 01471 grepFlags |= GREP_FLAGS_ONLY_MATCHING; 01472 01473 /* Compile locale-specific PCRE tables. */ 01474 if ((xx = mireSetLocale(NULL, locale)) != 0) 01475 goto errxit; 01476 01477 /* Initialize global pattern options. */ 01478 /* Interpret the newline type; the default settings are Unix-like. */ 01479 /*@-moduncon@*/ /* LCL: something fishy. */ 01480 xx = mireSetGOptions(newline, GF_ISSET(CASELESS), GF_ISSET(MULTILINE), 01481 GF_ISSET(UTF8)); 01482 /*@=moduncon@*/ 01483 if (xx != 0) { 01484 fprintf(stderr, _("%s: Invalid newline specifier \"%s\"\n"), 01485 __progname, (newline != NULL ? newline : "lf")); 01486 goto errxit; 01487 } 01488 01489 /* Get memory to store the pattern and hints lists. */ 01490 /* XXX FIXME: rpmmireNew needs to be used here. */ 01491 pattern_list = xcalloc(MAX_PATTERN_COUNT, sizeof(*pattern_list)); 01492 01493 /* 01494 * If no patterns were provided by -e, and there is no file provided by -f, 01495 * the first argument is the one and only pattern, and it must exist. 01496 */ 01497 { int npatterns = argvCount(patterns); 01498 01499 /* 01500 * If no patterns were provided by -e, and no file was provided by -f, 01501 * the first argument is the one and only pattern, and it must exist. 01502 */ 01503 if (npatterns == 0 && pattern_filenames == NULL) { 01504 if (av == NULL|| i >= ac) { 01505 poptPrintUsage(optCon, stderr, 0); 01506 goto errxit; 01507 } 01508 xx = poptSaveString(&patterns, POPT_ARG_ARGV, av[i]); 01509 i++; 01510 } 01511 01512 /* 01513 * Compile the patterns that were provided on the command line, either 01514 * by multiple uses of -e or as a single unkeyed pattern. 01515 */ 01516 npatterns = argvCount(patterns); 01517 if (patterns != NULL) 01518 for (j = 0; j < npatterns; j++) { 01519 if (!compile_pattern(patterns[j], NULL, 01520 (j == 0 && npatterns == 1)? 0 : j + 1)) 01521 goto errxit; 01522 } 01523 } 01524 01525 /* Compile the regular expressions that are provided from file(s). */ 01526 if (mireLoadPatternFiles(pattern_filenames)) 01527 goto errxit; 01528 01529 /* Study the regular expressions, as we will be running them many times */ 01530 /*@-onlytrans@*/ 01531 if (mireStudy(pattern_list, pattern_count)) 01532 goto errxit; 01533 /*@=onlytrans@*/ 01534 01535 /* If there are include or exclude patterns, compile them. */ 01536 /*@-compmempass@*/ 01537 if (mireLoadPatterns(grepMode, 0, exclude_patterns, NULL, 01538 &excludeMire, &nexcludes)) 01539 { 01540 /*@-nullptrarith@*/ 01541 miRE mire = excludeMire + (nexcludes - 1); 01542 /*@=nullptrarith@*/ 01543 fprintf(stderr, _("%s: Error in 'exclude' regex at offset %d: %s\n"), 01544 __progname, mire->erroff, mire->errmsg); 01545 goto errxit; 01546 } 01547 /*@=compmempass@*/ 01548 /*@-compmempass@*/ 01549 if (mireLoadPatterns(grepMode, 0, include_patterns, NULL, 01550 &includeMire, &nincludes)) 01551 { 01552 /*@-nullptrarith@*/ 01553 miRE mire = includeMire + (nincludes - 1); 01554 /*@=nullptrarith@*/ 01555 fprintf(stderr, _("%s: Error in 'include' regex at offset %d: %s\n"), 01556 __progname, mire->erroff, mire->errmsg); 01557 goto errxit; 01558 } 01559 /*@=compmempass@*/ 01560 01561 /* If there are no further arguments, do the business on stdin and exit. */ 01562 if (i >= ac) { 01563 rc = grep_or_recurse("-", 0, 1); 01564 } else 01565 01566 /* 01567 * Otherwise, work through the remaining arguments as files or directories. 01568 * Pass in the fact that there is only one argument at top level - this 01569 * suppresses the file name if the argument is not a directory and 01570 * filenames are not otherwise forced. 01571 */ 01572 { BOOL only_one_at_top = (i == ac -1); /* Catch initial value of i */ 01573 01574 if (av != NULL) 01575 for (; i < ac; i++) { 01576 int frc = grep_or_recurse(av[i], dee_action == dee_RECURSE, 01577 only_one_at_top); 01578 if (frc > 1) rc = frc; 01579 else if (frc == 0 && rc == 1) rc = 0; 01580 } 01581 } 01582 01583 exit: 01584 /*@-statictrans@*/ 01585 pattern_list = mireFreeAll(pattern_list, pattern_count); 01586 patterns = argvFree(patterns); 01587 excludeMire = mireFreeAll(excludeMire, nexcludes); 01588 exclude_patterns = argvFree(exclude_patterns); 01589 includeMire = mireFreeAll(includeMire, nincludes); 01590 include_patterns = argvFree(include_patterns); 01591 01592 pattern_filenames = argvFree(pattern_filenames); 01593 01594 /*@-observertrans@*/ 01595 color_string = _free(color_string); 01596 locale = _free(locale); 01597 newline = _free(newline); 01598 stdin_name = _free(stdin_name); 01599 /*@=observertrans@*/ 01600 /*@=statictrans@*/ 01601 01602 xx = rpmswExit(&grep_totalops, 0); 01603 if (_rpmsw_stats) { 01604 rpmswPrint(" total:", &grep_totalops, NULL); 01605 rpmswPrint(" read:", &grep_readops, NULL); 01606 } 01607 01608 optCon = rpmioFini(optCon); 01609 01610 return rc; 01611 01612 errxit: 01613 rc = 2; 01614 goto exit; 01615 }