popt  1.18
poptconfig.c
Go to the documentation of this file.
1 
5 /* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
6  file accompanying popt source distributions, available from
7  ftp://ftp.rpm.org/pub/rpm/dist. */
8 
9 #include "system.h"
10 #include "poptint.h"
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <errno.h>
15 
16 #if defined(HAVE_FNMATCH_H)
17 #include <fnmatch.h>
18 
19 #endif
20 
21 #if defined(HAVE_GLOB_H)
22 #include <glob.h>
23 
24 #if !defined(__GLIBC__)
25 /* Return nonzero if PATTERN contains any metacharacters.
26  Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
27 static int
28 glob_pattern_p (const char * pattern, int quote)
29 {
30  const char * p;
31  int open = 0;
32 
33  for (p = pattern; *p != '\0'; ++p)
34  switch (*p) {
35  case '?':
36  case '*':
37  return 1;
38  break;
39  case '\\':
40  if (quote && p[1] != '\0')
41  ++p;
42  break;
43  case '[':
44  open = 1;
45  break;
46  case ']':
47  if (open)
48  return 1;
49  break;
50  }
51  return 0;
52 }
53 #endif /* !defined(__GLIBC__) */
54 
55 static int poptGlobFlags = 0;
56 
57 static int poptGlob_error(UNUSED(const char * epath),
58  UNUSED(int eerrno))
59 {
60  return 1;
61 }
62 #endif /* HAVE_GLOB_H */
63 
72 static int poptGlob(UNUSED(poptContext con), const char * pattern,
73  int * acp, const char *** avp)
74 {
75  const char * pat = pattern;
76  int rc = 0; /* assume success */
77 
78 #if defined(HAVE_GLOB_H)
79  if (glob_pattern_p(pat, 0)) {
80  glob_t _g, *pglob = &_g;
81 
82  if (!(rc = glob(pat, poptGlobFlags, poptGlob_error, pglob))) {
83  if (acp) {
84  *acp = (int) pglob->gl_pathc;
85  pglob->gl_pathc = 0;
86  }
87  if (avp) {
88  *avp = (const char **) pglob->gl_pathv;
89  pglob->gl_pathv = NULL;
90  }
91  globfree(pglob);
92  } else if (rc == GLOB_NOMATCH) {
93  *avp = NULL;
94  *acp = 0;
95  rc = 0;
96  } else
97  rc = POPT_ERROR_ERRNO;
98  } else
99 #endif /* HAVE_GLOB_H */
100  {
101  if (acp)
102  *acp = 1;
103  if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
104  (*avp)[0] = xstrdup(pat);
105  }
106 
107  return rc;
108 }
109 
110 
111 int poptSaneFile(const char * fn)
112 {
113  struct stat sb;
114 
115  if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
116  return 0;
117  if (stat(fn, &sb) == -1)
118  return 0;
119  if (!S_ISREG(sb.st_mode))
120  return 0;
121  if (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
122  return 0;
123  return 1;
124 }
125 
126 int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
127 {
128  int fdno;
129  char * b = NULL;
130  off_t nb = 0;
131  char * s, * t, * se;
132  int rc = POPT_ERROR_ERRNO; /* assume failure */
133 
134  fdno = open(fn, O_RDONLY);
135  if (fdno < 0)
136  goto exit;
137 
138  if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
139  || lseek(fdno, 0, SEEK_SET) == (off_t)-1
140  || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
141  || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
142  {
143  int oerrno = errno;
144  (void) close(fdno);
145  errno = oerrno;
146  goto exit;
147  }
148  if (close(fdno) == -1)
149  goto exit;
150  if (b == NULL) {
151  rc = POPT_ERROR_MALLOC;
152  goto exit;
153  }
154  rc = 0;
155 
156  /* Trim out escaped newlines. */
157  if (flags & POPT_READFILE_TRIMNEWLINES)
158  {
159  for (t = b, s = b, se = b + nb; *s && s < se; s++) {
160  switch (*s) {
161  case '\\':
162  if (s[1] == '\n') {
163  s++;
164  continue;
165  }
166  /* fallthrough */
167  default:
168  *t++ = *s;
169  break;
170  }
171  }
172  *t++ = '\0';
173  nb = (off_t)(t - b);
174  }
175 
176 exit:
177  if (rc != 0) {
178  if (b)
179  free(b);
180  b = NULL;
181  nb = 0;
182  }
183  if (bp)
184  *bp = b;
185  else if (b)
186  free(b);
187  if (nbp)
188  *nbp = (size_t)nb;
189  return rc;
190 }
191 
198 static int configAppMatch(poptContext con, const char * s)
199 {
200  int rc = 1;
201 
202  if (con == NULL || con->appName == NULL) /* XXX can't happen. */
203  return rc;
204 
205 #if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
206  if (glob_pattern_p(s, 1)) {
207  static int flags = FNM_PATHNAME | FNM_PERIOD;
208 #ifdef FNM_EXTMATCH
209  flags |= FNM_EXTMATCH;
210 #endif
211  rc = fnmatch(s, con->appName, flags);
212  } else
213 #endif
214  rc = strcmp(s, con->appName);
215  return rc;
216 }
217 
218 static int poptConfigLine(poptContext con, char * line)
219 {
220  char *b = NULL;
221  size_t nb = 0;
222  char * se = line;
223  const char * appName;
224  const char * entryType;
225  const char * opt;
226  struct poptItem_s item_buf;
227  poptItem item = &item_buf;
228  int i, j;
229  int rc = POPT_ERROR_BADCONFIG;
230 
231  if (con == NULL || con->appName == NULL)
232  goto exit;
233 
234  memset(item, 0, sizeof(*item));
235 
236  appName = se;
237  while (*se != '\0' && !_isspaceptr(se)) se++;
238  if (*se == '\0')
239  goto exit;
240  else
241  *se++ = '\0';
242 
243  if (configAppMatch(con, appName)) goto exit;
244 
245  while (*se != '\0' && _isspaceptr(se)) se++;
246  entryType = se;
247  while (*se != '\0' && !_isspaceptr(se)) se++;
248  if (*se != '\0') *se++ = '\0';
249 
250  while (*se != '\0' && _isspaceptr(se)) se++;
251  if (*se == '\0') goto exit;
252  opt = se;
253  while (*se != '\0' && !_isspaceptr(se)) se++;
254  if (opt[0] == '-' && *se == '\0') goto exit;
255  if (*se != '\0') *se++ = '\0';
256 
257  while (*se != '\0' && _isspaceptr(se)) se++;
258  if (opt[0] == '-' && *se == '\0') goto exit;
259 
260  if (opt[0] == '-' && opt[1] == '-')
261  item->option.longName = opt + 2;
262  else if (opt[0] == '-' && opt[2] == '\0')
263  item->option.shortName = opt[1];
264  else {
265  const char * fn = opt;
266 
267  /* XXX handle globs and directories in fn? */
268  if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
269  goto exit;
270  if (b == NULL || nb == 0)
271  goto exit;
272 
273  /* Append remaining text to the interpolated file option text. */
274  if (*se != '\0') {
275  size_t nse = strlen(se) + 1;
276  if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
277  goto exit;
278  (void) stpcpy( stpcpy(&b[nb-1], " "), se);
279  nb += nse;
280  }
281  se = b;
282 
283  /* Use the basename of the path as the long option name. */
284  { const char * longName = strrchr(fn, '/');
285  if (longName != NULL)
286  longName++;
287  else
288  longName = fn;
289  if (longName == NULL) /* XXX can't happen. */
290  goto exit;
291  /* Single character basenames are treated as short options. */
292  if (longName[1] != '\0')
293  item->option.longName = longName;
294  else
295  item->option.shortName = longName[0];
296  }
297  }
298 
299  if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
300 
301  item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
302  for (i = 0, j = 0; i < item->argc; i++, j++) {
303  const char * f;
304  if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
305  f = item->argv[i] + sizeof("--POPTdesc=");
306  if (f[0] == '$' && f[1] == '"') f++;
307  item->option.descrip = f;
308  item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
309  j--;
310  } else
311  if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
312  f = item->argv[i] + sizeof("--POPTargs=");
313  if (f[0] == '$' && f[1] == '"') f++;
314  item->option.argDescrip = f;
315  item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
316  item->option.argInfo |= POPT_ARG_STRING;
317  j--;
318  } else
319  if (j != i)
320  item->argv[j] = item->argv[i];
321  }
322  if (j != i) {
323  item->argv[j] = NULL;
324  item->argc = j;
325  }
326 
327  if (!strcmp(entryType, "alias"))
328  rc = poptAddItem(con, item, 0);
329  else if (!strcmp(entryType, "exec"))
330  rc = poptAddItem(con, item, 1);
331 exit:
332  rc = 0; /* XXX for now, always return success */
333  if (b)
334  free(b);
335  return rc;
336 }
337 
338 int poptReadConfigFile(poptContext con, const char * fn)
339 {
340  char * b = NULL, *be;
341  size_t nb = 0;
342  const char *se;
343  char *t, *te;
344  int rc;
345 
346  if (con == NULL) return POPT_ERROR_NOCONTEXT;
347 
348  if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
349  return (errno == ENOENT ? 0 : rc);
350  if (b == NULL || nb == 0)
351  return POPT_ERROR_BADCONFIG;
352 
353  if ((t = malloc(nb + 1)) == NULL)
354  goto exit;
355  te = t;
356 
357  be = (b + nb);
358  for (se = b; se < be; se++) {
359  switch (*se) {
360  case '\n':
361  *te = '\0';
362  te = t;
363  while (*te && _isspaceptr(te)) te++;
364  if (*te && *te != '#')
365  if ((rc = poptConfigLine(con, te)) != 0)
366  goto exit;
367  break;
368  case '\\':
369  *te = *se++;
370  /* \ at the end of a line does not insert a \n */
371  if (se < be && *se != '\n') {
372  te++;
373  *te++ = *se;
374  }
375  break;
376  default:
377  *te++ = *se;
378  break;
379  }
380  }
381  rc = 0;
382 
383 exit:
384  free(t);
385  if (b)
386  free(b);
387  return rc;
388 }
389 
390 int poptReadConfigFiles(poptContext con, const char * paths)
391 {
392  char * buf = (paths ? xstrdup(paths) : NULL);
393  const char * p;
394  char * pe;
395  int rc = 0; /* assume success */
396 
397  for (p = buf; p != NULL && *p != '\0'; p = pe) {
398  const char ** av = NULL;
399  int ac = 0;
400  int i;
401  int xx;
402 
403  /* locate start of next path element */
404  pe = strchr(p, ':');
405  if (pe != NULL && *pe == ':')
406  *pe++ = '\0';
407  else
408  pe = (char *) (p + strlen(p));
409 
410  xx = poptGlob(con, p, &ac, &av);
411 
412  /* work-off each resulting file from the path element */
413  for (i = 0; i < ac; i++) {
414  const char * fn = av[i];
415  if (!poptSaneFile(fn))
416  continue;
417  xx = poptReadConfigFile(con, fn);
418  if (xx && rc == 0)
419  rc = xx;
420  free((void *)av[i]);
421  av[i] = NULL;
422  }
423  free(av);
424  av = NULL;
425  }
426 
427  if (buf)
428  free(buf);
429 
430  return rc;
431 }
432 
434 {
435  char * home;
436  struct stat sb;
437  int rc = 0; /* assume success */
438 
439  if (con == NULL || con->appName == NULL) goto exit;
440 
441  rc = poptReadConfigFile(con, POPT_SYSCONFDIR "/popt");
442  if (rc) goto exit;
443 
444 #if defined(HAVE_GLOB_H)
445  if (!stat(POPT_SYSCONFDIR "/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
446  const char ** av = NULL;
447  int ac = 0;
448  int i;
449 
450  if ((rc = poptGlob(con, POPT_SYSCONFDIR "/popt.d/*", &ac, &av)) == 0) {
451  for (i = 0; rc == 0 && i < ac; i++) {
452  const char * fn = av[i];
453  if (!poptSaneFile(fn))
454  continue;
455  rc = poptReadConfigFile(con, fn);
456  free((void *)av[i]);
457  av[i] = NULL;
458  }
459  free(av);
460  av = NULL;
461  }
462  }
463  if (rc) goto exit;
464 #endif
465 
466  if ((home = getenv("HOME"))) {
467  char * fn = malloc(strlen(home) + 20);
468  if (fn != NULL) {
469  (void) stpcpy(stpcpy(fn, home), "/.popt");
470  rc = poptReadConfigFile(con, fn);
471  free(fn);
472  } else
473  rc = POPT_ERROR_ERRNO;
474  if (rc) goto exit;
475  }
476 
477 exit:
478  return rc;
479 }
480 
483 {
484  return poptFreeContext(con);
485 }
486 
488 poptInit(int argc, const char ** argv,
489  const struct poptOption * options, const char * configPaths)
490 {
491  poptContext con = NULL;
492  const char * argv0;
493 
494  if (argv == NULL || argv[0] == NULL || options == NULL)
495  return con;
496 
497  if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
498  else argv0 = argv[0];
499 
500  con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
501  if (con != NULL&& poptReadConfigFiles(con, configPaths))
502  con = poptFini(con);
503 
504  return con;
505 }
int poptAddItem(poptContext con, poptItem newItem, int flags)
Add alias/exec item to context.
Definition: popt.c:1574
poptContext poptGetContext(const char *name, int argc, const char **argv, const struct poptOption *options, unsigned int flags)
Initialize popt context.
Definition: popt.c:155
poptContext poptFreeContext(poptContext con)
Destroy context.
Definition: popt.c:1531
#define POPT_ARGFLAG_DOC_HIDDEN
Definition: popt.h:53
#define POPT_READFILE_TRIMNEWLINES
Definition: popt.h:355
#define POPT_ERROR_MALLOC
Definition: popt.h:102
#define POPT_ERROR_ERRNO
Definition: popt.h:97
#define POPT_ARG_STRING
Definition: popt.h:21
int poptParseArgvString(const char *s, int *argcPtr, const char ***argvPtr)
Parse a string into an argument array.
Definition: poptparse.c:54
#define POPT_ERROR_NOCONTEXT
Definition: popt.h:103
#define POPT_ERROR_BADCONFIG
Definition: popt.h:104
static int poptGlob(poptContext con, const char *pattern, int *acp, const char ***avp)
Return path(s) from a glob pattern.
Definition: poptconfig.c:72
int poptReadFile(const char *fn, char **bp, size_t *nbp, int flags)
Read a file into a buffer.
Definition: poptconfig.c:126
static int poptConfigLine(poptContext con, char *line)
Definition: poptconfig.c:218
int poptReadConfigFiles(poptContext con, const char *paths)
Read configuration file(s).
Definition: poptconfig.c:390
static int configAppMatch(poptContext con, const char *s)
Check for application match.
Definition: poptconfig.c:198
int poptReadConfigFile(poptContext con, const char *fn)
Read configuration file.
Definition: poptconfig.c:338
poptContext poptFini(poptContext con)
Destroy context (alternative implementation).
Definition: poptconfig.c:482
poptContext poptInit(int argc, const char **argv, const struct poptOption *options, const char *configPaths)
Initialize popt context (alternative implementation).
Definition: poptconfig.c:488
int poptSaneFile(const char *fn)
Test path/file for config file sanity (regular file, permissions etc)
Definition: poptconfig.c:111
int poptReadDefaultConfig(poptContext con, int useEnv)
Read default configuration from /etc/popt and $HOME/.popt.
Definition: poptconfig.c:433
const char * appName
Definition: poptint.h:100
A popt alias or exec argument for poptAddItem().
Definition: popt.h:150
#define _isspaceptr(_chp)
Definition: system.h:12
#define UNUSED(x)
Definition: system.h:65
static char * stpcpy(char *dest, const char *src)
Definition: system.h:31
char * xstrdup(const char *str)