rpm  5.2.1
mire.c
Go to the documentation of this file.
1 
4 #include "system.h"
5 
6 #include <rpmiotypes.h>
7 #include <rpmio.h>
8 #include <rpmlog.h>
9 
10 #define _MIRE_INTERNAL
11 #include <mire.h>
12 
13 #include "debug.h"
14 
15 /*@access regex_t @*/
16 
17 /*@unchecked@*/
18 int _mire_debug = 0;
19 
20 /*@unchecked@*/
21 const unsigned char * _mirePCREtables = NULL;
22 
23 /*@unchecked@*/
25 
26 /*@unchecked@*/
28 
29 /*@unchecked@*/
31 
32 /*@unchecked@*/
33 int _mireREGEXoptions = REG_EXTENDED | REG_NEWLINE;
34 
35 /*@unchecked@*/
37 
38 int mireClean(miRE mire)
39 {
40  if (mire == NULL) return 0;
41 /*@-modfilesys@*/
42 if (_mire_debug)
43 fprintf(stderr, "--> mireClean(%p)\n", mire);
44 /*@=modfilesys@*/
45  mire->pattern = _free(mire->pattern);
46  if (mire->mode == RPMMIRE_REGEX) {
47  if (mire->preg != NULL) {
48  regfree(mire->preg);
49  /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */
50  mire->preg = _free(mire->preg);
51  /*@=voidabstract =usereleased @*/
52  }
53  }
54  if (mire->mode == RPMMIRE_PCRE) { /* TODO: (*pcre_free)(_p) override */
55  mire->pcre = _free(mire->pcre);
56  mire->hints = _free(mire->hints);
57  }
58  mire->errmsg = NULL;
59  mire->erroff = 0;
60  mire->errcode = 0;
61  mire->fnflags = 0;
62  mire->cflags = 0;
63  mire->eflags = 0;
64  mire->coptions = 0;
65  mire->eoptions = 0;
66  mire->notmatch = 0;
67  return 0;
68 }
69 
70 static void mireFini(void * _mire)
71  /*@modifies _mire @*/
72 {
73  miRE mire = _mire;
74  (void) mireClean(mire);
75 }
76 
77 /*@unchecked@*/ /*@only@*/ /*@null@*/
79 
81 {
82  miRE mire;
83 
84  if (_mirePool == NULL) {
85  _mirePool = rpmioNewPool("mire", sizeof(*mire), -1, _mire_debug,
86  NULL, NULL, mireFini);
87  pool = _mirePool;
88  }
89  return (miRE) rpmioGetPool(pool, sizeof(*mire));
90 }
91 
92 /*@-onlytrans@*/ /* XXX miRE array, not refcounted. */
93 void * mireFreeAll(miRE mire, int nmire)
94 {
95  if (mire != NULL) {
96  int i;
97  for (i = 0; i < nmire; i++)
98  (void) mireClean(mire + i);
99  /* XXX rpmgrep doesn't use mire pools yet. retrofit a fix. */
100  if (mire->_item.use != NULL && mire->_item.pool != NULL) {
101  /* XXX only the 1st element in the array has a usage mutex. */
102  mire = xrealloc(mire, sizeof(*mire));
103  mire = (miRE)rpmioFreePoolItem((rpmioItem)mire, __FUNCTION__, __FILE__, __LINE__);
104  } else
105  mire = _free(mire);
106  }
107  return NULL;
108 }
109 /*@=onlytrans@*/
110 
111 miRE mireNew(rpmMireMode mode, int tag)
112 {
113  miRE mire = mireGetPool(_mirePool);
114  mire->mode = mode;
115  mire->tag = tag;
116  return mireLink(mire);
117 }
118 
119 int mireSetCOptions(miRE mire, rpmMireMode mode, int tag, int options,
120  const unsigned char * table)
121 {
122  int rc = 0;
123  mire->mode = mode;
124  mire->tag = tag;
125  switch (mire->mode) {
126  case RPMMIRE_DEFAULT:
127  break;
128  case RPMMIRE_STRCMP:
129  /* XXX strcasecmp? */
130  break;
131  case RPMMIRE_GLOB:
132  if (options == 0)
133  options = _mireGLOBoptions;
134  mire->fnflags = options;
135  break;
136  case RPMMIRE_REGEX:
137  if (options == 0)
138  options = _mireREGEXoptions;
139  mire->cflags = options;
140  break;
141  case RPMMIRE_PCRE:
142  if (options == 0)
143  options = _mirePCREoptions;
144  /* XXX check default compile options? */
145  mire->coptions = options;
146 /*@-assignexpose -temptrans @*/
147  mire->table = table;
148 /*@=assignexpose =temptrans @*/
149  break;
150  }
151  return rc;
152 }
153 
154 int mireSetEOptions(miRE mire, int * offsets, int noffsets)
155 {
156  int rc = 0;
157  if (mire->mode == RPMMIRE_PCRE) {
158  mire->startoff = 0;
159  mire->eoptions = 0;
160 /*@-assignexpose@*/
161  mire->offsets = offsets;
162 /*@=assignexpose@*/
163  mire->noffsets = noffsets;
164  } else
165  if (mire->mode == RPMMIRE_REGEX) {
166  mire->startoff = 0;
167  mire->eoptions = 0;
168 /*@-assignexpose@*/
169  mire->offsets = offsets;
170 /*@=assignexpose@*/
171  mire->noffsets = noffsets;
172  } else
173  rc = -1;
174 
175  return rc;
176 }
177 
178 int mireSetGOptions(const char * newline, int caseless, int multiline, int utf8)
179 {
180  int rc = 0;
181 
182  if (caseless) {
183 #if defined(PCRE_CASELESS)
184  _mirePCREoptions |= PCRE_CASELESS;
185 #endif
186  _mireREGEXoptions |= REG_ICASE;
187 #if defined(FNM_CASEFOLD)
189 #endif
190  } else {
191 #if defined(PCRE_CASELESS)
192  _mirePCREoptions &= ~PCRE_CASELESS;
193 #endif
194  _mireREGEXoptions &= ~REG_ICASE;
195 #if defined(FNM_CASEFOLD)
197 #endif
198  }
199 
200  if (multiline) {
201 #if defined(PCRE_MULTILINE)
202  _mirePCREoptions |= PCRE_MULTILINE|PCRE_FIRSTLINE;
203 #endif
204  } else {
205 #if defined(PCRE_MULTILINE)
206  _mirePCREoptions &= ~(PCRE_MULTILINE|PCRE_FIRSTLINE);
207 #endif
208  }
209 
210  if (utf8) {
211 #if defined(PCRE_UTF8)
212  _mirePCREoptions |= PCRE_UTF8;
213 #endif
214  } else {
215 #if defined(PCRE_UTF8)
216  _mirePCREoptions &= ~PCRE_UTF8;
217 #endif
218  }
219 
220  /*
221  * Set the default line ending value from the default in the PCRE library;
222  * "lf", "cr", "crlf", and "any" are supported. Anything else is treated
223  * as "lf".
224  */
225  if (newline == NULL) {
226  int val = 0;
227 #if defined(PCRE_CONFIG_NEWLINE)
228 /*@-modunconnomods@*/
229  (void)pcre_config(PCRE_CONFIG_NEWLINE, &val);
230 /*@=modunconnomods@*/
231 #endif
232  switch (val) {
233  default: newline = "lf"; break;
234  case '\r': newline = "cr"; break;
235 /*@-shiftimplementation@*/
236  case ('\r' << 8) | '\n': newline = "crlf"; break;
237 /*@=shiftimplementation@*/
238  case -1: newline = "any"; break;
239  case -2: newline = "anycrlf"; break;
240  }
241  }
242 
243  /* Interpret the newline type; the default settings are Unix-like. */
244  if (!strcasecmp(newline, "cr")) {
245 #if defined(PCRE_NEWLINE_CR)
246  _mirePCREoptions |= PCRE_NEWLINE_CR;
247 #endif
248  _mireEL = EL_CR;
249  } else if (!strcasecmp(newline, "lf")) {
250 #if defined(PCRE_NEWLINE_LF)
251  _mirePCREoptions |= PCRE_NEWLINE_LF;
252 #endif
253  _mireEL = EL_LF;
254  } else if (!strcasecmp(newline, "crlf")) {
255 #if defined(PCRE_NEWLINE_CRLF)
256  _mirePCREoptions |= PCRE_NEWLINE_CRLF;
257 #endif
258  _mireEL = EL_CRLF;
259  } else if (!strcasecmp(newline, "any")) {
260 #if defined(PCRE_NEWLINE_ANY)
261  _mirePCREoptions |= PCRE_NEWLINE_ANY;
262 #endif
263  _mireEL = EL_ANY;
264  } else if (!strcasecmp(newline, "anycrlf")) {
265 #if defined(PCRE_NEWLINE_ANYCRLF)
266  _mirePCREoptions |= PCRE_NEWLINE_ANYCRLF;
267 #endif
269  } else {
270  rc = -1;
271  }
272 
273  return rc;
274 }
275 
276 int mireSetLocale(/*@unused@*/ miRE mire, const char * locale)
277 {
278  const char * locale_from = NULL;
279  int rc = -1; /* assume failure */
280 
281  /* XXX TODO: --locale jiggery-pokery should be done env LC_ALL=C rpmgrep */
282  if (locale == NULL) {
283  if (locale)
284  locale_from = "--locale";
285  else {
286  /*
287  * If a locale has not been provided as an option, see if the
288  * LC_CTYPE or LC_ALL environment variable is set, and if so,
289  * use it.
290  */
291 /*@-dependenttrans -observertrans@*/
292  if ((locale = getenv("LC_ALL")) != NULL)
293  locale_from = "LC_ALL";
294  else if ((locale = getenv("LC_CTYPE")) != NULL)
295  locale_from = "LC_CTYPE";
296 /*@=dependenttrans =observertrans@*/
297  if (locale)
298  locale = xstrdup(locale);
299  }
300  }
301 
302  /*
303  * If a locale has been provided, set it, and generate the tables PCRE
304  * needs. Otherwise, _mirePCREtables == NULL, which uses default tables.
305  */
306  if (locale != NULL) {
307  const char * olocale = setlocale(LC_CTYPE, locale);
308  if (olocale == NULL) {
309 /*@-modfilesys@*/
310  fprintf(stderr,
311  _("%s: Failed to set locale %s (obtained from %s)\n"),
312  __progname, locale, locale_from);
313 /*@=modfilesys@*/
314  goto exit;
315  }
316 #if defined(WITH_PCRE)
317 /*@-evalorderuncon -onlytrans @*/
318  _mirePCREtables = pcre_maketables();
319 /*@=evalorderuncon =onlytrans @*/
320 #ifdef NOTYET
321  if (setlocale(LC_CTYPE, olocale) == NULL)
322  goto exit;
323 #endif
324 #endif
325  }
326  rc = 0;
327 
328 exit:
329  return rc;
330 }
331 
332 int mireRegcomp(miRE mire, const char * pattern)
333 {
334  int rc = 0;
335 
336  mire->pattern = xstrdup(pattern);
337 
338  switch (mire->mode) {
339  case RPMMIRE_STRCMP:
340  break;
341  case RPMMIRE_PCRE:
342 #ifdef WITH_PCRE
343  mire->errcode = 0;
344  mire->errmsg = NULL;
345  mire->erroff = 0;
346  mire->pcre = pcre_compile2(mire->pattern, mire->coptions,
347  &mire->errcode, &mire->errmsg, &mire->erroff, mire->table);
348  if (mire->pcre == NULL) {
349  if (_mire_debug)
351  _("pcre_compile2 failed: %s(%d) at offset %d of \"%s\"\n"),
352  mire->errmsg, mire->errcode, mire->erroff, mire->pattern);
353  rc = -1;
354  goto exit; /* XXX HACK: rpmgrep is not expecting mireClean. */
355  }
356 #else
357  rc = -99;
358 #endif
359  break;
360  case RPMMIRE_DEFAULT:
361  case RPMMIRE_REGEX:
362  mire->preg = xcalloc(1, sizeof(*mire->preg));
363  if (mire->cflags == 0)
364  mire->cflags = _mireREGEXoptions;
365  rc = regcomp(mire->preg, mire->pattern, mire->cflags);
366  if (rc) {
367  char msg[256];
368  (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
369  msg[sizeof(msg)-1] = '\0';
370  rpmlog(RPMLOG_ERR, _("%s: regcomp failed: %s\n"),
371  mire->pattern, msg);
372  }
373  break;
374  case RPMMIRE_GLOB:
375  if (mire->fnflags == 0)
376  mire->fnflags = _mireGLOBoptions;
377  break;
378  default:
379  rc = -1;
380  break;
381  }
382 
383  if (rc)
384  (void) mireClean(mire);
385 
386 #ifdef WITH_PCRE
387 exit:
388 #endif
389 /*@-modfilesys@*/
390 if (_mire_debug)
391 fprintf(stderr, "--> mireRegcomp(%p, \"%s\") rc %d\n", mire, pattern, rc);
392 /*@=modfilesys@*/
393  return rc;
394 }
395 
396 int mireRegexec(miRE mire, const char * val, size_t vallen)
397 {
398  int rc = 0;
399 
400  switch (mire->mode) {
401  case RPMMIRE_STRCMP:
402  /* XXX strcasecmp? strncmp? */
403  rc = strcmp(mire->pattern, val);
404  if (rc) rc = -1;
405  break;
406  case RPMMIRE_DEFAULT:
407  case RPMMIRE_REGEX:
408  /* XXX rpmgrep: ensure that the string is NUL terminated. */
409  if (vallen > 0) {
410  if (val[vallen] != '\0') {
411  char * t = strncpy(alloca(vallen+1), val, vallen);
412  t[vallen] = '\0';
413  val = t;
414  }
415  }
416 /*@-nullpass@*/
417  /* XXX HACK: PCRE returns 2/3 of array, POSIX dimensions regmatch_t. */
418  rc = regexec(mire->preg, val,
419  mire->noffsets/3, (regmatch_t *)mire->offsets, mire->eflags);
420 /*@=nullpass@*/
421  switch (rc) {
422  case 0: rc = 0; /*@innerbreak@*/ break;
423  case REG_NOMATCH: rc = -1;/*@innerbreak@*/ break;
424  default:
425  { char msg[256];
426  (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
427  msg[sizeof(msg)-1] = '\0';
428  rpmlog(RPMLOG_ERR, _("%s: regexec failed: %s(%d)\n"),
429  mire->pattern, msg, rc);
430  if (rc < 0) rc -= 1; /* XXX ensure -1 is nomatch. */
431  if (rc > 0) rc = -(rc+1); /* XXX ensure errors are negative. */
432  } /*@innerbreak@*/ break;
433  }
434  break;
435  case RPMMIRE_PCRE:
436 #ifdef WITH_PCRE
437  if (vallen == 0)
438  vallen = strlen(val);
439  rc = pcre_exec(mire->pcre, mire->hints, val, (int)vallen, mire->startoff,
440  mire->eoptions, mire->offsets, mire->noffsets);
441  switch (rc) {
442  case 0: rc = 0; /*@innerbreak@*/ break;
443  case PCRE_ERROR_NOMATCH: rc = -1;/*@innerbreak@*/ break;
444  default:
445  if (_mire_debug && rc < 0)
446  rpmlog(RPMLOG_ERR, _("pcre_exec failed: return %d\n"), rc);
447  /*@innerbreak@*/ break;
448  }
449 #else
450  rc = -99;
451 #endif
452  break;
453  case RPMMIRE_GLOB:
454  rc = fnmatch(mire->pattern, val, mire->fnflags);
455  switch (rc) {
456  case 0: rc = 0; /*@innerbreak@*/ break;
457  case FNM_NOMATCH: rc = -1;/*@innerbreak@*/ break;
458  default:
459  if (_mire_debug)
460  rpmlog(RPMLOG_ERR, _("fnmatch failed: return %d\n"), rc);
461  if (rc < 0) rc -= 1; /* XXX ensure -1 is nomatch. */
462  if (rc > 0) rc = -(rc+1); /* XXX ensure errors are negative. */
463  /*@innerbreak@*/ break;
464  }
465  break;
466  default:
467  rc = -1;
468  break;
469  }
470 
471 /*@-modfilesys@*/
472 if (_mire_debug)
473 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);
474 /*@=modfilesys@*/
475  return rc;
476 }
477 
478 /*@-onlytrans@*/ /* XXX miRE array, not refcounted. */
479 int mireAppend(rpmMireMode mode, int tag, const char * pattern,
480  const unsigned char * table, miRE * mirep, int * nmirep)
481 {
482  miRE mire;
483  int xx;
484 
485  if (*mirep == NULL) {
486  (*mirep) = mireGetPool(_mirePool);
487  mire = (*mirep);
488  } else {
489  void *use = (*mirep)->_item.use;
490  void *pool = (*mirep)->_item.pool;
491 
492  /* XXX only the 1st element in the array has a usage mutex. */
493  (*mirep) = xrealloc((*mirep), ((*nmirep) + 1) * sizeof(*mire));
494  mire = (*mirep) + (*nmirep);
495  memset(mire, 0, sizeof(*mire));
496  /* XXX ensure no segfault, copy the use/pool from 1st item. */
497 /*@-assignexpose@*/
498  mire->_item.use = use;
499  mire->_item.pool = pool;
500 /*@=assignexpose@*/
501  }
502 
503  (*nmirep)++;
504  xx = mireSetCOptions(mire, mode, tag, 0, table);
505 /*@-usereleased@*/
506  return mireRegcomp(mire, pattern);
507 /*@=usereleased@*/
508 }
509 /*@=onlytrans@*/
510 
511 int mireLoadPatterns(rpmMireMode mode, int tag, const char ** patterns,
512  const unsigned char * table, miRE * mirep, int * nmirep)
513 {
514  const char *pattern;
515  int rc = -1; /* assume failure */
516 
517  if (patterns != NULL) /* note rc=0 return with no patterns to load. */
518  while ((pattern = *patterns++) != NULL) {
519  /* XXX pcre_options is not used. should it be? */
520  /* XXX more realloc's than necessary. */
521  int xx = mireAppend(mode, tag, pattern, table, mirep, nmirep);
522  if (xx) {
523  rc = xx;
524  goto exit;
525  }
526  }
527  rc = 0;
528 
529 exit:
530  return rc;
531 }
532 
533 int mireApply(miRE mire, int nmire, const char *s, size_t slen, int rc)
534 {
535  int i;
536 
537  if (slen == 0)
538  slen = strlen(s);
539 
540  if (mire)
541  for (i = 0; i < nmire; mire++, i++) {
542  int xx = mireRegexec(mire, s, slen);
543 
544  /* Check if excluding or including condition applies. */
545  if (rc < 0 && xx < 0)
546  continue; /* excluding: continue on negative matches. */
547  if (rc > 0 && xx >= 0)
548  continue; /* including: continue on positive matches. */
549  /* Save 1st found termination condition and exit. */
550  rc = xx;
551  break;
552  }
553  return rc;
554 }
555 
556 int mireStudy(miRE mire, int nmires)
557 {
558  int rc = -1; /* assume failure */
559  int j;
560 
561  /* Study the PCRE regex's, as we will be running them many times */
562  if (mire) /* note rc=0 return with no mire's. */
563  for (j = 0; j < nmires; mire++, j++) {
564  if (mire->mode != RPMMIRE_PCRE)
565  continue;
566 #if defined(WITH_PCRE)
567  { const char * error;
568  mire->hints = pcre_study(mire->pcre, 0, &error);
569  if (error != NULL) {
570  char s[32];
571  if (nmires == 1) s[0] = '\0'; else sprintf(s, _(" number %d"), j);
572  rpmlog(RPMLOG_ERR, _("%s: Error while studying regex%s: %s\n"),
573  __progname, s, error);
574  goto exit;
575  }
576  }
577 #endif
578  }
579  rc = 0;
580 
581 #if defined(WITH_PCRE)
582 exit:
583 #endif
584  return rc;
585 }