Line data Source code
1 : /** \ingroup popt
2 : * \file popt/poptconfig.c
3 : */
4 :
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 :
13 : #if defined(HAVE_FNMATCH_H)
14 : #include <fnmatch.h>
15 :
16 : #if defined(__LCLINT__)
17 : /*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
18 : extern int fnmatch (const char *__pattern, const char *__name, int __flags)
19 : /*@*/;
20 : /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
21 : #endif /* __LCLINT__ */
22 : #endif
23 :
24 : #if defined(HAVE_GLOB_H)
25 : #include <glob.h>
26 :
27 : #if defined(__LCLINT__)
28 : /*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
29 : extern int glob (const char *__pattern, int __flags,
30 : /*@null@*/ int (*__errfunc) (const char *, int),
31 : /*@out@*/ glob_t *__pglob)
32 : /*@globals errno, fileSystem @*/
33 : /*@modifies *__pglob, errno, fileSystem @*/;
34 :
35 : /* XXX only annotation is a white lie */
36 : extern void globfree (/*@only@*/ glob_t *__pglob)
37 : /*@modifies *__pglob @*/;
38 :
39 : /* XXX _GNU_SOURCE ifdef and/or retrofit is needed for portability. */
40 : extern int glob_pattern_p (const char *__pattern, int __quote)
41 : /*@*/;
42 : /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
43 : #endif /* __LCLINT__ */
44 :
45 : #if !defined(__GLIBC__)
46 : /* Return nonzero if PATTERN contains any metacharacters.
47 : Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
48 : static int
49 : glob_pattern_p (const char * pattern, int quote)
50 : /*@*/
51 : {
52 : const char * p;
53 : int open = 0;
54 :
55 : for (p = pattern; *p != '\0'; ++p)
56 : switch (*p) {
57 : case '?':
58 : case '*':
59 : return 1;
60 : /*@notreached@*/ /*@switchbreak@*/ break;
61 : case '\\':
62 : if (quote && p[1] != '\0')
63 : ++p;
64 : /*@switchbreak@*/ break;
65 : case '[':
66 : open = 1;
67 : /*@switchbreak@*/ break;
68 : case ']':
69 : if (open)
70 : return 1;
71 : /*@switchbreak@*/ break;
72 : }
73 : return 0;
74 : }
75 : #endif /* !defined(__GLIBC__) */
76 :
77 : /*@unchecked@*/
78 : static int poptGlobFlags = 0;
79 :
80 0 : static int poptGlob_error(/*@unused@*/ UNUSED(const char * epath),
81 : /*@unused@*/ UNUSED(int eerrno))
82 : /*@*/
83 : {
84 0 : return 1;
85 : }
86 : #endif /* HAVE_GLOB_H */
87 :
88 : /**
89 : * Return path(s) from a glob pattern.
90 : * @param con context
91 : * @param pattern glob pattern
92 : * @retval *acp no. of paths
93 : * @retval *avp array of paths
94 : * @return 0 on success
95 : */
96 0 : static int poptGlob(/*@unused@*/ UNUSED(poptContext con), const char * pattern,
97 : /*@out@*/ int * acp, /*@out@*/ const char *** avp)
98 : /*@modifies *acp, *avp @*/
99 : {
100 0 : const char * pat = pattern;
101 0 : int rc = 0; /* assume success */
102 :
103 : /* XXX skip the attention marker. */
104 0 : if (pat[0] == '@' && pat[1] != '(')
105 0 : pat++;
106 :
107 : #if defined(HAVE_GLOB_H)
108 0 : if (glob_pattern_p(pat, 0)) {
109 0 : glob_t _g, *pglob = &_g;
110 :
111 0 : if (!glob(pat, poptGlobFlags, poptGlob_error, pglob)) {
112 0 : if (acp) {
113 0 : *acp = (int) pglob->gl_pathc;
114 0 : pglob->gl_pathc = 0;
115 : }
116 0 : if (avp) {
117 : /*@-onlytrans@*/
118 0 : *avp = (const char **) pglob->gl_pathv;
119 : /*@=onlytrans@*/
120 0 : pglob->gl_pathv = NULL;
121 : }
122 : /*@-nullstate@*/
123 0 : globfree(pglob);
124 : /*@=nullstate@*/
125 : } else
126 0 : rc = POPT_ERROR_ERRNO;
127 : } else
128 : #endif /* HAVE_GLOB_H */
129 : {
130 0 : if (acp)
131 0 : *acp = 1;
132 0 : if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
133 0 : (*avp)[0] = xstrdup(pat);
134 : }
135 :
136 0 : return rc;
137 : }
138 :
139 : /*@access poptContext @*/
140 :
141 0 : int poptSaneFile(const char * fn)
142 : {
143 : struct stat sb;
144 0 : uid_t uid = getuid();
145 :
146 0 : if (stat(fn, &sb) == -1)
147 0 : return 1;
148 0 : if ((uid_t)sb.st_uid != uid)
149 0 : return 0;
150 0 : if (!S_ISREG(sb.st_mode))
151 0 : return 0;
152 : /*@-bitwisesigned@*/
153 0 : if (sb.st_mode & (S_IWGRP|S_IWOTH))
154 0 : return 0;
155 : /*@=bitwisesigned@*/
156 0 : return 1;
157 : }
158 :
159 0 : int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
160 : {
161 : int fdno;
162 0 : char * b = NULL;
163 0 : off_t nb = 0;
164 : char * s, * t, * se;
165 0 : int rc = POPT_ERROR_ERRNO; /* assume failure */
166 :
167 0 : fdno = open(fn, O_RDONLY);
168 0 : if (fdno < 0)
169 0 : goto exit;
170 :
171 0 : if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
172 0 : || lseek(fdno, 0, SEEK_SET) == (off_t)-1
173 0 : || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
174 0 : || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
175 : {
176 0 : int oerrno = errno;
177 0 : (void) close(fdno);
178 0 : errno = oerrno;
179 0 : goto exit;
180 : }
181 0 : if (close(fdno) == -1)
182 0 : goto exit;
183 0 : if (b == NULL) {
184 0 : rc = POPT_ERROR_MALLOC;
185 0 : goto exit;
186 : }
187 0 : rc = 0;
188 :
189 : /* Trim out escaped newlines. */
190 : /*@-bitwisesigned@*/
191 0 : if (flags & POPT_READFILE_TRIMNEWLINES)
192 : /*@=bitwisesigned@*/
193 : {
194 0 : for (t = b, s = b, se = b + nb; *s && s < se; s++) {
195 0 : switch (*s) {
196 0 : case '\\':
197 0 : if (s[1] == '\n') {
198 0 : s++;
199 0 : continue;
200 : }
201 : /*@fallthrough@*/
202 : default:
203 0 : *t++ = *s;
204 0 : /*@switchbreak@*/ break;
205 : }
206 : }
207 0 : *t++ = '\0';
208 0 : nb = (off_t)(t - b);
209 : }
210 :
211 0 : exit:
212 0 : if (rc != 0) {
213 : /*@-usedef@*/
214 0 : if (b)
215 0 : free(b);
216 : /*@=usedef@*/
217 0 : b = NULL;
218 0 : nb = 0;
219 : }
220 0 : if (bp)
221 0 : *bp = b;
222 : /*@-usereleased@*/
223 0 : else if (b)
224 0 : free(b);
225 : /*@=usereleased@*/
226 0 : if (nbp)
227 0 : *nbp = (size_t)nb;
228 : /*@-compdef -nullstate @*/ /* XXX cannot annotate char ** correctly */
229 0 : return rc;
230 : /*@=compdef =nullstate @*/
231 : }
232 :
233 : /**
234 : * Check for application match.
235 : * @param con context
236 : * @param s config application name
237 : * return 0 if config application matches
238 : */
239 0 : static int configAppMatch(poptContext con, const char * s)
240 : /*@*/
241 : {
242 0 : int rc = 1;
243 :
244 0 : if (con->appName == NULL) /* XXX can't happen. */
245 0 : return rc;
246 :
247 : #if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
248 0 : if (glob_pattern_p(s, 1)) {
249 : /*@-bitwisesigned@*/
250 : static int flags = FNM_PATHNAME | FNM_PERIOD;
251 : #ifdef FNM_EXTMATCH
252 0 : flags |= FNM_EXTMATCH;
253 : #endif
254 : /*@=bitwisesigned@*/
255 0 : rc = fnmatch(s, con->appName, flags);
256 : } else
257 : #endif
258 0 : rc = strcmp(s, con->appName);
259 0 : return rc;
260 : }
261 :
262 : /*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
263 0 : static int poptConfigLine(poptContext con, char * line)
264 : /*@globals fileSystem, internalState @*/
265 : /*@modifies con, fileSystem, internalState @*/
266 : {
267 0 : char *b = NULL;
268 0 : size_t nb = 0;
269 0 : char * se = line;
270 : const char * appName;
271 : const char * entryType;
272 : const char * opt;
273 : struct poptItem_s item_buf;
274 0 : poptItem item = &item_buf;
275 : int i, j;
276 0 : int rc = POPT_ERROR_BADCONFIG;
277 :
278 0 : if (con->appName == NULL)
279 0 : goto exit;
280 :
281 0 : memset(item, 0, sizeof(*item));
282 :
283 0 : appName = se;
284 0 : while (*se != '\0' && !_isspaceptr(se)) se++;
285 0 : if (*se == '\0')
286 0 : goto exit;
287 : else
288 0 : *se++ = '\0';
289 :
290 0 : if (configAppMatch(con, appName)) goto exit;
291 :
292 0 : while (*se != '\0' && _isspaceptr(se)) se++;
293 0 : entryType = se;
294 0 : while (*se != '\0' && !_isspaceptr(se)) se++;
295 0 : if (*se != '\0') *se++ = '\0';
296 :
297 0 : while (*se != '\0' && _isspaceptr(se)) se++;
298 0 : if (*se == '\0') goto exit;
299 0 : opt = se;
300 0 : while (*se != '\0' && !_isspaceptr(se)) se++;
301 0 : if (opt[0] == '-' && *se == '\0') goto exit;
302 0 : if (*se != '\0') *se++ = '\0';
303 :
304 0 : while (*se != '\0' && _isspaceptr(se)) se++;
305 0 : if (opt[0] == '-' && *se == '\0') goto exit;
306 :
307 : /*@-temptrans@*/ /* FIX: line alias is saved */
308 0 : if (opt[0] == '-' && opt[1] == '-')
309 0 : item->option.longName = opt + 2;
310 0 : else if (opt[0] == '-' && opt[2] == '\0')
311 0 : item->option.shortName = opt[1];
312 : else {
313 0 : const char * fn = opt;
314 :
315 : /* XXX handle globs and directories in fn? */
316 0 : if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
317 0 : goto exit;
318 0 : if (b == NULL || nb == 0)
319 0 : goto exit;
320 :
321 : /* Append remaining text to the interpolated file option text. */
322 0 : if (*se != '\0') {
323 0 : size_t nse = strlen(se) + 1;
324 0 : if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
325 0 : goto exit;
326 0 : (void) stpcpy( stpcpy(&b[nb-1], " "), se);
327 0 : nb += nse;
328 : }
329 0 : se = b;
330 :
331 : /* Use the basename of the path as the long option name. */
332 0 : { const char * longName = strrchr(fn, '/');
333 0 : if (longName != NULL)
334 0 : longName++;
335 : else
336 0 : longName = fn;
337 0 : if (longName == NULL) /* XXX can't happen. */
338 0 : goto exit;
339 : /* Single character basenames are treated as short options. */
340 0 : if (longName[1] != '\0')
341 0 : item->option.longName = longName;
342 : else
343 0 : item->option.shortName = longName[0];
344 : }
345 : }
346 : /*@=temptrans@*/
347 :
348 0 : if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
349 :
350 : /*@-modobserver@*/
351 0 : item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
352 0 : for (i = 0, j = 0; i < item->argc; i++, j++) {
353 : const char * f;
354 0 : if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
355 0 : f = item->argv[i] + sizeof("--POPTdesc=");
356 0 : if (f[0] == '$' && f[1] == '"') f++;
357 0 : item->option.descrip = f;
358 0 : item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
359 0 : j--;
360 : } else
361 0 : if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
362 0 : f = item->argv[i] + sizeof("--POPTargs=");
363 0 : if (f[0] == '$' && f[1] == '"') f++;
364 0 : item->option.argDescrip = f;
365 0 : item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
366 0 : item->option.argInfo |= POPT_ARG_STRING;
367 0 : j--;
368 : } else
369 0 : if (j != i)
370 0 : item->argv[j] = item->argv[i];
371 : }
372 0 : if (j != i) {
373 0 : item->argv[j] = NULL;
374 0 : item->argc = j;
375 : }
376 : /*@=modobserver@*/
377 :
378 : /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
379 0 : if (!strcmp(entryType, "alias"))
380 0 : rc = poptAddItem(con, item, 0);
381 0 : else if (!strcmp(entryType, "exec"))
382 0 : rc = poptAddItem(con, item, 1);
383 : /*@=nullstate@*/
384 0 : exit:
385 0 : rc = 0; /* XXX for now, always return success */
386 0 : if (b)
387 0 : free(b);
388 0 : return rc;
389 : }
390 : /*@=compmempass@*/
391 :
392 0 : int poptReadConfigFile(poptContext con, const char * fn)
393 : {
394 0 : char * b = NULL, *be;
395 0 : size_t nb = 0;
396 : const char *se;
397 : char *t, *te;
398 : int rc;
399 : int xx;
400 :
401 0 : if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
402 0 : return (errno == ENOENT ? 0 : rc);
403 0 : if (b == NULL || nb == 0)
404 0 : return POPT_ERROR_BADCONFIG;
405 :
406 0 : if ((t = malloc(nb + 1)) == NULL)
407 0 : goto exit;
408 0 : te = t;
409 :
410 0 : be = (b + nb);
411 0 : for (se = b; se < be; se++) {
412 0 : switch (*se) {
413 0 : case '\n':
414 0 : *te = '\0';
415 0 : te = t;
416 0 : while (*te && _isspaceptr(te)) te++;
417 0 : if (*te && *te != '#')
418 0 : xx = poptConfigLine(con, te);
419 0 : /*@switchbreak@*/ break;
420 : /*@-usedef@*/ /* XXX *se may be uninitialized */
421 0 : case '\\':
422 0 : *te = *se++;
423 : /* \ at the end of a line does not insert a \n */
424 0 : if (se < be && *se != '\n') {
425 0 : te++;
426 0 : *te++ = *se;
427 : }
428 0 : /*@switchbreak@*/ break;
429 0 : default:
430 0 : *te++ = *se;
431 0 : /*@switchbreak@*/ break;
432 : /*@=usedef@*/
433 : }
434 : }
435 :
436 0 : free(t);
437 0 : rc = 0;
438 :
439 0 : exit:
440 0 : if (b)
441 0 : free(b);
442 0 : return rc;
443 : }
444 :
445 0 : int poptReadConfigFiles(poptContext con, const char * paths)
446 : {
447 0 : char * buf = (paths ? xstrdup(paths) : NULL);
448 : const char * p;
449 : char * pe;
450 0 : int rc = 0; /* assume success */
451 :
452 0 : for (p = buf; p != NULL && *p != '\0'; p = pe) {
453 0 : const char ** av = NULL;
454 0 : int ac = 0;
455 : int i;
456 : int xx;
457 :
458 : /* locate start of next path element */
459 0 : pe = strchr(p, ':');
460 0 : if (pe != NULL && *pe == ':')
461 0 : *pe++ = '\0';
462 : else
463 0 : pe = (char *) (p + strlen(p));
464 :
465 0 : xx = poptGlob(con, p, &ac, &av);
466 :
467 : /* work-off each resulting file from the path element */
468 0 : for (i = 0; i < ac; i++) {
469 0 : const char * fn = av[i];
470 0 : if (av[i] == NULL) /* XXX can't happen */
471 0 : /*@innercontinue@*/ continue;
472 : /* XXX should '@' attention be pushed into poptReadConfigFile? */
473 0 : if (p[0] == '@' && p[1] != '(') {
474 0 : if (fn[0] == '@' && fn[1] != '(')
475 0 : fn++;
476 0 : xx = poptSaneFile(fn);
477 0 : if (!xx && rc == 0)
478 0 : rc = POPT_ERROR_BADCONFIG;
479 0 : /*@innercontinue@*/ continue;
480 : }
481 0 : xx = poptReadConfigFile(con, fn);
482 0 : if (xx && rc == 0)
483 0 : rc = xx;
484 0 : free((void *)av[i]);
485 0 : av[i] = NULL;
486 : }
487 0 : free(av);
488 0 : av = NULL;
489 : }
490 :
491 : /*@-usedef@*/
492 0 : if (buf)
493 0 : free(buf);
494 : /*@=usedef@*/
495 :
496 0 : return rc;
497 : }
498 :
499 0 : int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
500 : {
501 : static const char _popt_sysconfdir[] = POPT_SYSCONFDIR "/popt";
502 : static const char _popt_etc[] = "/etc/popt";
503 : char * home;
504 : struct stat sb;
505 0 : int rc = 0; /* assume success */
506 :
507 0 : if (con->appName == NULL) goto exit;
508 :
509 0 : if (strcmp(_popt_sysconfdir, _popt_etc)) {
510 0 : rc = poptReadConfigFile(con, _popt_sysconfdir);
511 0 : if (rc) goto exit;
512 : }
513 :
514 0 : rc = poptReadConfigFile(con, _popt_etc);
515 0 : if (rc) goto exit;
516 :
517 : #if defined(HAVE_GLOB_H)
518 0 : if (!stat("/etc/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
519 0 : const char ** av = NULL;
520 0 : int ac = 0;
521 : int i;
522 :
523 0 : if ((rc = poptGlob(con, "/etc/popt.d/*", &ac, &av)) == 0) {
524 0 : for (i = 0; rc == 0 && i < ac; i++) {
525 0 : const char * fn = av[i];
526 0 : if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
527 0 : continue;
528 0 : if (!stat(fn, &sb)) {
529 0 : if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))
530 0 : continue;
531 : }
532 0 : rc = poptReadConfigFile(con, fn);
533 0 : free((void *)av[i]);
534 0 : av[i] = NULL;
535 : }
536 0 : free(av);
537 0 : av = NULL;
538 : }
539 : }
540 0 : if (rc) goto exit;
541 : #endif
542 :
543 0 : if ((home = getenv("HOME"))) {
544 0 : char * fn = malloc(strlen(home) + 20);
545 0 : if (fn != NULL) {
546 0 : (void) stpcpy(stpcpy(fn, home), "/.popt");
547 0 : rc = poptReadConfigFile(con, fn);
548 0 : free(fn);
549 : } else
550 0 : rc = POPT_ERROR_ERRNO;
551 0 : if (rc) goto exit;
552 : }
553 :
554 0 : exit:
555 0 : return rc;
556 : }
557 :
558 : poptContext
559 0 : poptFini(poptContext con)
560 : {
561 0 : return poptFreeContext(con);
562 : }
563 :
564 : poptContext
565 0 : poptInit(int argc, const char ** argv,
566 : const struct poptOption * options, const char * configPaths)
567 : {
568 0 : poptContext con = NULL;
569 : const char * argv0;
570 :
571 0 : if (argv == NULL || argv[0] == NULL || options == NULL)
572 0 : return con;
573 :
574 0 : if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
575 0 : else argv0 = argv[0];
576 :
577 0 : con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
578 0 : if (con != NULL&& poptReadConfigFiles(con, configPaths))
579 0 : con = poptFini(con);
580 :
581 0 : return con;
582 : }
|