rpm 5.3.7

rpmio/mire.c

Go to the documentation of this file.
00001 
00004 #include "system.h"
00005 
00006 #include <rpmiotypes.h>
00007 #include <rpmio.h>
00008 #include <rpmlog.h>
00009 
00010 #define _MIRE_INTERNAL
00011 #include <mire.h>
00012 
00013 #include "debug.h"
00014 
00015 /*@access regex_t @*/
00016 
00017 /*@unchecked@*/
00018 int _mire_debug = 0;
00019 
00020 /*@unchecked@*/
00021 const unsigned char * _mirePCREtables = NULL;
00022 
00023 /*@unchecked@*/
00024 mireEL_t _mireEL = EL_LF;
00025 
00026 /*@unchecked@*/
00027 int _mireSTRINGoptions = 0;
00028 
00029 /*@unchecked@*/
00030 int _mireGLOBoptions = FNM_EXTMATCH | FNM_PATHNAME | FNM_PERIOD;
00031 
00032 /*@unchecked@*/
00033 int _mireREGEXoptions = REG_EXTENDED | REG_NEWLINE;
00034 
00035 /*@unchecked@*/
00036 int _mirePCREoptions = 0;
00037 
00038 int mireClean(miRE mire)
00039 {
00040     if (mire == NULL) return 0;
00041     mire->pattern = _free(mire->pattern);
00042     if (mire->mode == RPMMIRE_REGEX) {
00043         if (mire->preg != NULL) {
00044             regfree(mire->preg);
00045             /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */
00046             mire->preg = _free(mire->preg);
00047             /*@=voidabstract =usereleased @*/
00048         }
00049     }
00050     if (mire->mode == RPMMIRE_PCRE) {   /* TODO: (*pcre_free)(_p) override */
00051         mire->pcre = _free(mire->pcre);
00052         mire->hints = _free(mire->hints);
00053     }
00054     mire->errmsg = NULL;
00055     mire->erroff = 0;
00056     mire->errcode = 0;
00057     mire->fnflags = 0;
00058     mire->cflags = 0;
00059     mire->eflags = 0;
00060     mire->coptions = 0;
00061     mire->eoptions = 0;
00062     mire->notmatch = 0;
00063 /*@-modfilesys@*/
00064 if (_mire_debug)
00065 fprintf(stderr, "<-- mireClean(%p)\n", mire);
00066 /*@=modfilesys@*/
00067     return 0;
00068 }
00069 
00070 static void mireFini(void * _mire)
00071         /*@modifies _mire @*/
00072 {
00073     miRE mire = _mire;
00074     (void) mireClean(mire);
00075 }
00076 
00077 /*@unchecked@*/ /*@only@*/ /*@null@*/
00078 rpmioPool _mirePool;
00079 
00080 miRE mireGetPool(rpmioPool pool)
00081 {
00082     miRE mire;
00083 
00084     if (_mirePool == NULL) {
00085         _mirePool = rpmioNewPool("mire", sizeof(*mire), -1, _mire_debug,
00086                         NULL, NULL, mireFini);
00087         pool = _mirePool;
00088     }
00089     mire = (miRE) rpmioGetPool(pool, sizeof(*mire));
00090     memset(((char *)mire)+sizeof(mire->_item), 0, sizeof(*mire)-sizeof(mire->_item));
00091     return mire;
00092 }
00093 
00094 /*@-onlytrans@*/        /* XXX miRE array, not refcounted. */
00095 void * mireFreeAll(miRE mire, int nmire)
00096 {
00097     if (mire != NULL) {
00098         while (--nmire > 0)
00099             (void) mireClean(mire + nmire);
00100         /* XXX rpmgrep doesn't use mire pools yet. retrofit a fix. */
00101         if (mire->_item.use != NULL && mire->_item.pool != NULL)
00102             mire = (miRE)rpmioFreePoolItem((rpmioItem)mire, __FUNCTION__, __FILE__, __LINE__);
00103         else
00104             mire = _free(mire);
00105     }
00106     return NULL;
00107 }
00108 /*@=onlytrans@*/
00109 
00110 miRE mireNew(rpmMireMode mode, int tag)
00111 {
00112     miRE mire = mireGetPool(_mirePool);
00113     mire->mode = mode;
00114     mire->tag = tag;
00115     return mireLink(mire);
00116 }
00117 
00118 int mireSetCOptions(miRE mire, rpmMireMode mode, int tag, int options,
00119                 const unsigned char * table)
00120 {
00121     int rc = 0;
00122     mire->mode = mode;
00123     mire->tag = tag;
00124     switch (mire->mode) {
00125     case RPMMIRE_DEFAULT:
00126         break;
00127     case RPMMIRE_STRCMP:
00128         /* XXX strcasecmp? */
00129         break;
00130     case RPMMIRE_GLOB:
00131         if (options == 0)
00132             options = _mireGLOBoptions;
00133         mire->fnflags = options;
00134         break;
00135     case RPMMIRE_REGEX:
00136         if (options == 0)
00137             options = _mireREGEXoptions;
00138         mire->cflags = options;
00139         break;
00140     case RPMMIRE_PCRE:
00141         if (options == 0)
00142             options = _mirePCREoptions;
00143         /* XXX check default compile options? */
00144         mire->coptions = options;
00145 /*@-assignexpose -temptrans @*/
00146         mire->table = table;
00147 /*@=assignexpose =temptrans @*/
00148         break;
00149     }
00150     return rc;
00151 }
00152 
00153 int mireSetEOptions(miRE mire, int * offsets, int noffsets)
00154 {
00155     int rc = 0;
00156     if (mire->mode == RPMMIRE_PCRE) {
00157         mire->startoff = 0;
00158         mire->eoptions = 0;
00159 /*@-assignexpose@*/
00160         mire->offsets = offsets;
00161 /*@=assignexpose@*/
00162         mire->noffsets = noffsets;
00163     } else
00164     if (mire->mode == RPMMIRE_REGEX) {
00165         mire->startoff = 0;
00166         mire->eoptions = 0;
00167 /*@-assignexpose@*/
00168         mire->offsets = offsets;
00169 /*@=assignexpose@*/
00170         mire->noffsets = noffsets;
00171     } else
00172         rc = -1;
00173 
00174     return rc;
00175 }
00176 
00177 int mireSetGOptions(const char * newline, int caseless, int multiline, int utf8)
00178 {
00179     int rc = 0;
00180 
00181     if (caseless) {
00182 #if defined(PCRE_CASELESS)
00183         _mirePCREoptions |= PCRE_CASELESS;
00184 #endif
00185         _mireREGEXoptions |= REG_ICASE;
00186 #if defined(FNM_CASEFOLD)
00187         _mireGLOBoptions |= FNM_CASEFOLD;
00188 #endif
00189     } else {
00190 #if defined(PCRE_CASELESS)
00191         _mirePCREoptions &= ~PCRE_CASELESS;
00192 #endif
00193         _mireREGEXoptions &= ~REG_ICASE;
00194 #if defined(FNM_CASEFOLD)
00195         _mireGLOBoptions &= ~FNM_CASEFOLD;
00196 #endif
00197     }
00198 
00199     if (multiline) {
00200 #if defined(PCRE_MULTILINE)
00201         _mirePCREoptions |= PCRE_MULTILINE|PCRE_FIRSTLINE;
00202 #endif
00203     } else {
00204 #if defined(PCRE_MULTILINE)
00205         _mirePCREoptions &= ~(PCRE_MULTILINE|PCRE_FIRSTLINE);
00206 #endif
00207     }
00208 
00209     if (utf8) {
00210 #if defined(PCRE_UTF8)
00211         _mirePCREoptions |= PCRE_UTF8;
00212 #endif
00213     } else {
00214 #if defined(PCRE_UTF8)
00215         _mirePCREoptions &= ~PCRE_UTF8;
00216 #endif
00217     }
00218 
00219     /*
00220      * Set the default line ending value from the default in the PCRE library;
00221      * "lf", "cr", "crlf", and "any" are supported. Anything else is treated
00222      * as "lf".
00223      */
00224     if (newline == NULL) {
00225         int val = 0;
00226 #if defined(PCRE_CONFIG_NEWLINE)
00227 /*@-modunconnomods@*/
00228         (void)pcre_config(PCRE_CONFIG_NEWLINE, &val);
00229 /*@=modunconnomods@*/
00230 #endif
00231         switch (val) {
00232         default:        newline = "lf";         break;
00233         case '\r':      newline = "cr";         break;
00234 /*@-shiftimplementation@*/
00235         case ('\r' << 8) | '\n': newline = "crlf"; break;
00236 /*@=shiftimplementation@*/
00237         case -1:        newline = "any";        break;
00238         case -2:        newline = "anycrlf";    break;
00239         }
00240     }
00241 
00242     /* Interpret the newline type; the default settings are Unix-like. */
00243     if (!strcasecmp(newline, "cr")) {
00244 #if defined(PCRE_NEWLINE_CR)
00245         _mirePCREoptions |= PCRE_NEWLINE_CR;
00246 #endif
00247         _mireEL = EL_CR;
00248     } else if (!strcasecmp(newline, "lf")) {
00249 #if defined(PCRE_NEWLINE_LF)
00250         _mirePCREoptions |= PCRE_NEWLINE_LF;
00251 #endif
00252         _mireEL = EL_LF;
00253     } else if (!strcasecmp(newline, "crlf")) {
00254 #if defined(PCRE_NEWLINE_CRLF)
00255         _mirePCREoptions |= PCRE_NEWLINE_CRLF;
00256 #endif
00257         _mireEL = EL_CRLF;
00258     } else if (!strcasecmp(newline, "any")) {
00259 #if defined(PCRE_NEWLINE_ANY)
00260         _mirePCREoptions |= PCRE_NEWLINE_ANY;
00261 #endif
00262         _mireEL = EL_ANY;
00263     } else if (!strcasecmp(newline, "anycrlf")) {
00264 #if defined(PCRE_NEWLINE_ANYCRLF)
00265         _mirePCREoptions |= PCRE_NEWLINE_ANYCRLF;
00266 #endif
00267         _mireEL = EL_ANYCRLF;
00268     } else {
00269         rc = -1;
00270     }
00271 
00272     return rc;
00273 }
00274 
00275 int mireSetLocale(/*@unused@*/ miRE mire, const char * locale)
00276 {
00277     const char * locale_from = NULL;
00278     int rc = -1;        /* assume failure */
00279 
00280     /* XXX TODO: --locale jiggery-pokery should be done env LC_ALL=C rpmgrep */
00281     if (locale == NULL) {
00282         if (locale)
00283             locale_from = "--locale";
00284         else {
00285             /*
00286              * If a locale has not been provided as an option, see if the
00287              * LC_CTYPE or LC_ALL environment variable is set, and if so,
00288              * use it.
00289              */
00290 /*@-dependenttrans -observertrans@*/
00291             if ((locale = getenv("LC_ALL")) != NULL)
00292                 locale_from = "LC_ALL";
00293             else if ((locale = getenv("LC_CTYPE")) != NULL)
00294                 locale_from = "LC_CTYPE";
00295 /*@=dependenttrans =observertrans@*/
00296             if (locale)
00297                 locale = xstrdup(locale);
00298         }
00299     }
00300 
00301     /*
00302     * If a locale has been provided, set it, and generate the tables PCRE
00303     * needs. Otherwise, _mirePCREtables == NULL, which uses default tables.
00304     */
00305     if (locale != NULL) {
00306         const char * olocale = setlocale(LC_CTYPE, locale);
00307         if (olocale == NULL) {
00308 /*@-modfilesys@*/
00309             fprintf(stderr,
00310                 _("%s: Failed to set locale %s (obtained from %s)\n"),
00311                 __progname, locale, locale_from);
00312 /*@=modfilesys@*/
00313             goto exit;
00314         }
00315 #if defined(WITH_PCRE)
00316 /*@-evalorderuncon -onlytrans @*/
00317         _mirePCREtables = pcre_maketables();
00318 /*@=evalorderuncon =onlytrans @*/
00319 #ifdef  NOTYET
00320         if (setlocale(LC_CTYPE, olocale) == NULL)
00321             goto exit;
00322 #endif
00323 #endif
00324     }
00325     rc = 0;
00326 
00327 exit:
00328     return rc;
00329 }
00330 
00331 int mireRegcomp(miRE mire, const char * pattern)
00332 {
00333     int rc = 0;
00334 
00335     mire->pattern = xstrdup(pattern);
00336 
00337     switch (mire->mode) {
00338     case RPMMIRE_STRCMP:
00339         break;
00340     case RPMMIRE_PCRE:
00341 #ifdef  WITH_PCRE
00342         mire->errcode = 0;
00343         mire->errmsg = NULL;
00344         mire->erroff = 0;
00345         mire->pcre = pcre_compile2(mire->pattern, mire->coptions,
00346                 &mire->errcode, &mire->errmsg, &mire->erroff, mire->table);
00347         if (mire->pcre == NULL) {
00348             if (_mire_debug)
00349                 rpmlog(RPMLOG_ERR,
00350                     _("pcre_compile2 failed: %s(%d) at offset %d of \"%s\"\n"),
00351                     mire->errmsg, mire->errcode, mire->erroff, mire->pattern);
00352             rc = -1;
00353             goto exit;  /* XXX HACK: rpmgrep is not expecting mireClean. */
00354         }
00355 #else
00356         rc = -99;
00357 #endif
00358         break;
00359     case RPMMIRE_DEFAULT:
00360     case RPMMIRE_REGEX:
00361         mire->preg = xcalloc(1, sizeof(*mire->preg));
00362         if (mire->cflags == 0)
00363             mire->cflags = _mireREGEXoptions;
00364         rc = regcomp(mire->preg, mire->pattern, mire->cflags);
00365         if (rc) {
00366             char msg[256];
00367             (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
00368             msg[sizeof(msg)-1] = '\0';
00369             rpmlog(RPMLOG_ERR, _("%s: regcomp failed: %s\n"),
00370                         mire->pattern, msg);
00371         }
00372         break;
00373     case RPMMIRE_GLOB:
00374         if (mire->fnflags == 0)
00375             mire->fnflags = _mireGLOBoptions;
00376         break;
00377     default:
00378         rc = -1;
00379         break;
00380     }
00381 
00382     if (rc)
00383         (void) mireClean(mire);
00384 
00385 #ifdef  WITH_PCRE
00386 exit:
00387 #endif
00388 /*@-modfilesys@*/
00389 if (_mire_debug)
00390 fprintf(stderr, "<-- mireRegcomp(%p, \"%s\") rc %d\n", mire, pattern, rc);
00391 /*@=modfilesys@*/
00392     return rc;
00393 }
00394 
00395 int mireRegexec(miRE mire, const char * val, size_t vallen)
00396 {
00397     int rc = -1;        /* assume failure */
00398 
00399     switch (mire->mode) {
00400     case RPMMIRE_STRCMP:
00401         if (mire->pattern == NULL)
00402             break;
00403         if (vallen == 0)
00404             vallen = strlen(val);
00405         /* XXX strcasecmp? */
00406         rc = strncmp(mire->pattern, val, vallen);
00407         if (rc) rc = -1;
00408         break;
00409     case RPMMIRE_DEFAULT:
00410     case RPMMIRE_REGEX:
00411         if (mire->preg == NULL)
00412             break;
00413         /* XXX rpmgrep: ensure that the string is NUL terminated. */
00414         if (vallen > 0) {
00415             if (val[vallen] != '\0') {
00416                 char * t = strncpy(alloca(vallen+1), val, vallen);
00417                 t[vallen] = '\0';
00418                 val = t;
00419             }
00420         } else
00421         if (vallen == 0)
00422             vallen = strlen(val);
00423 /*@-nullpass@*/
00424         /* XXX HACK: PCRE returns 2/3 of array, POSIX dimensions regmatch_t. */
00425         rc = regexec(mire->preg, val,
00426                 mire->noffsets/3, (regmatch_t *)mire->offsets, mire->eflags);
00427 /*@=nullpass@*/
00428         switch (rc) {
00429         case 0:                 rc = 0; /*@innerbreak@*/ break;
00430         case REG_NOMATCH:       rc = -1;/*@innerbreak@*/ break;
00431         default:
00432           { char msg[256];
00433             (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
00434             msg[sizeof(msg)-1] = '\0';
00435             rpmlog(RPMLOG_ERR, _("%s: regexec failed: %s(%d)\n"),
00436                         mire->pattern, msg, rc);
00437             if (rc < 0) rc -= 1;        /* XXX ensure -1 is nomatch. */
00438             if (rc > 0) rc = -(rc+1);   /* XXX ensure errors are negative. */
00439           } /*@innerbreak@*/ break;
00440         }
00441         break;
00442     case RPMMIRE_PCRE:
00443 #ifdef  WITH_PCRE
00444         if (mire->pcre == NULL)
00445             break;
00446         if (vallen == 0)
00447             vallen = strlen(val);
00448         rc = pcre_exec(mire->pcre, mire->hints, val, (int)vallen, mire->startoff,
00449                 mire->eoptions, mire->offsets, mire->noffsets);
00450         switch (rc) {
00451         case 0:                         rc = 0; /*@innerbreak@*/ break;
00452         case PCRE_ERROR_NOMATCH:        rc = -1;/*@innerbreak@*/ break;
00453         default:
00454             if (_mire_debug && rc < 0)
00455                 rpmlog(RPMLOG_ERR, _("pcre_exec failed: return %d\n"), rc);
00456             /*@innerbreak@*/ break;
00457         }
00458 #else
00459         rc = -99;
00460 #endif
00461         break;
00462     case RPMMIRE_GLOB:
00463         if (mire->pattern == NULL)
00464             break;
00465         rc = fnmatch(mire->pattern, val, mire->fnflags);
00466         switch (rc) {
00467         case 0:                 rc = 0; /*@innerbreak@*/ break;
00468         case FNM_NOMATCH:       rc = -1;/*@innerbreak@*/ break;
00469         default:
00470             if (_mire_debug)
00471                 rpmlog(RPMLOG_ERR, _("fnmatch failed: return %d\n"), rc);
00472             if (rc < 0) rc -= 1;        /* XXX ensure -1 is nomatch. */
00473             if (rc > 0) rc = -(rc+1);   /* XXX ensure errors are negative. */
00474             /*@innerbreak@*/ break;
00475         }
00476         break;
00477     default:
00478         break;
00479     }
00480 
00481 /*@-modfilesys@*/
00482 if (_mire_debug)
00483 fprintf(stderr, "<-- mireRegexec(%p, %p[%u]) rc %d mode %d \"%.*s\"\n", mire, val, (unsigned)vallen, rc, mire->mode, (int)(vallen < 20 ? vallen : 20), val);
00484 /*@=modfilesys@*/
00485     return rc;
00486 }
00487 
00488 /*@-onlytrans@*/        /* XXX miRE array, not refcounted. */
00489 int mireAppend(rpmMireMode mode, int tag, const char * pattern,
00490                 const unsigned char * table, miRE * mirep, int * nmirep)
00491 {
00492     miRE mire;
00493     int xx;
00494 
00495     if (*mirep == NULL) {
00496         (*mirep) = mireGetPool(_mirePool);
00497         mire = (*mirep);
00498     } else {
00499         void *use =  (*mirep)->_item.use;
00500         void *pool = (*mirep)->_item.pool;
00501 
00502         /* XXX only the 1st element in the array has a usage mutex. */
00503         (*mirep) = xrealloc((*mirep), ((*nmirep) + 1) * sizeof(*mire));
00504         mire = (*mirep) + (*nmirep);
00505         memset(mire, 0, sizeof(*mire));
00506         /* XXX ensure no segfault, copy the use/pool from 1st item. */
00507 /*@-assignexpose@*/
00508         mire->_item.use = use;
00509         mire->_item.pool = pool;
00510 /*@=assignexpose@*/
00511     }
00512 
00513     (*nmirep)++;
00514     xx = mireSetCOptions(mire, mode, tag, 0, table);
00515 /*@-usereleased@*/
00516     return mireRegcomp(mire, pattern);
00517 /*@=usereleased@*/
00518 }
00519 /*@=onlytrans@*/
00520 
00521 int mireLoadPatterns(rpmMireMode mode, int tag, const char ** patterns,
00522                 const unsigned char * table, miRE * mirep, int * nmirep)
00523 {
00524     const char *pattern;
00525     int rc = -1;        /* assume failure */
00526 
00527     if (patterns != NULL)       /* note rc=0 return with no patterns to load. */
00528     while ((pattern = *patterns++) != NULL) {
00529         /* XXX pcre_options is not used. should it be? */
00530         /* XXX more realloc's than necessary. */
00531         int xx = mireAppend(mode, tag, pattern, table, mirep, nmirep);
00532         if (xx) {
00533             rc = xx;
00534             goto exit;
00535         }
00536     }
00537     rc = 0;
00538 
00539 exit:
00540     return rc;
00541 }
00542 
00543 int mireApply(miRE mire, int nmire, const char *s, size_t slen, int rc)
00544 {
00545     int i;
00546 
00547     if (slen == 0)
00548         slen = strlen(s);
00549 
00550     if (mire)
00551     for (i = 0; i < nmire; mire++, i++) {
00552         int xx = mireRegexec(mire, s, slen);
00553 
00554         /* Check if excluding or including condition applies. */
00555         if (rc < 0 && xx < 0)
00556             continue;   /* excluding: continue on negative matches. */
00557         if (rc > 0 && xx >= 0)
00558             continue;   /* including: continue on positive matches. */
00559         /* Save 1st found termination condition and exit. */
00560         rc = xx;
00561         break;
00562     }
00563     return rc;
00564 }
00565 
00566 int mireStudy(miRE mire, int nmires)
00567 {
00568     int rc = -1;        /* assume failure */
00569     int j;
00570 
00571     /* Study the PCRE regex's, as we will be running them many times */
00572     if (mire)           /* note rc=0 return with no mire's. */
00573     for (j = 0; j < nmires; mire++, j++) {
00574         if (mire->mode != RPMMIRE_PCRE)
00575             continue;
00576 #if defined(WITH_PCRE)
00577       { const char * error;
00578         mire->hints = pcre_study(mire->pcre, 0, &error);
00579         if (error != NULL) {
00580             char s[32];
00581             if (nmires == 1) s[0] = '\0'; else sprintf(s, _(" number %d"), j);
00582             rpmlog(RPMLOG_ERR, _("%s: Error while studying regex%s: %s\n"),
00583                 __progname, s, error);
00584             goto exit;
00585         }
00586       }
00587 #endif
00588     }
00589     rc = 0;
00590 
00591 #if defined(WITH_PCRE)
00592 exit:
00593 #endif
00594     return rc;
00595 }