rpm 5.2.1

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