rpm 5.3.12
tools/rpmgrep.c
Go to the documentation of this file.
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 }