2 * \file popt/poptconfig.c
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. */
13 #if defined(HAVE_FNMATCH_H)
16 #if defined(__LCLINT__)
17 /*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
18 extern int fnmatch (const char *__pattern, const char *__name, int __flags)
20 /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
21 #endif /* __LCLINT__ */
24 #if defined(HAVE_GLOB_H)
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 @*/;
35 /* XXX only annotation is a white lie */
36 extern void globfree (/*@only@*/ glob_t *__pglob)
37 /*@modifies *__pglob @*/;
39 /* XXX _GNU_SOURCE ifdef and/or retrofit is needed for portability. */
40 extern int glob_pattern_p (const char *__pattern, int __quote)
42 /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
43 #endif /* __LCLINT__ */
45 #if !defined(__GLIBC__)
46 /* Return nonzero if PATTERN contains any metacharacters.
47 Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
49 glob_pattern_p (const char * pattern, int quote)
55 for (p = pattern; *p != '\0'; ++p)
60 /*@notreached@*/ /*@switchbreak@*/ break;
62 if (quote && p[1] != '\0')
64 /*@switchbreak@*/ break;
67 /*@switchbreak@*/ break;
71 /*@switchbreak@*/ break;
75 #endif /* !defined(__GLIBC__) */
78 static int poptGlobFlags = 0;
80 static int poptGlob_error(/*@unused@*/ UNUSED(const char * epath),
81 /*@unused@*/ UNUSED(int eerrno))
86 #endif /* HAVE_GLOB_H */
89 * Return path(s) from a glob pattern.
91 * @param pattern glob pattern
92 * @retval *acp no. of paths
93 * @retval *avp array of paths
94 * @return 0 on success
96 static int poptGlob(/*@unused@*/ UNUSED(poptContext con), const char * pattern,
97 /*@out@*/ int * acp, /*@out@*/ const char *** avp)
98 /*@modifies *acp, *avp @*/
100 const char * pat = pattern;
101 int rc = 0; /* assume success */
103 /* XXX skip the attention marker. */
104 if (pat[0] == '@' && pat[1] != '(')
107 #if defined(HAVE_GLOB_H)
108 if (glob_pattern_p(pat, 0)) {
109 glob_t _g, *pglob = &_g;
111 if (!glob(pat, poptGlobFlags, poptGlob_error, pglob)) {
113 *acp = (int) pglob->gl_pathc;
118 *avp = (const char **) pglob->gl_pathv;
120 pglob->gl_pathv = NULL;
126 rc = POPT_ERROR_ERRNO;
128 #endif /* HAVE_GLOB_H */
132 if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
133 (*avp)[0] = xstrdup(pat);
139 /*@access poptContext @*/
141 int poptSaneFile(const char * fn)
144 uid_t uid = getuid();
146 if (stat(fn, &sb) == -1)
148 if ((uid_t)sb.st_uid != uid)
150 if (!S_ISREG(sb.st_mode))
153 if (sb.st_mode & (S_IWGRP|S_IWOTH))
159 int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
165 int rc = POPT_ERROR_ERRNO; /* assume failure */
167 fdno = open(fn, O_RDONLY);
171 if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
172 || lseek(fdno, 0, SEEK_SET) == (off_t)-1
173 || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
174 || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
181 if (close(fdno) == -1)
184 rc = POPT_ERROR_MALLOC;
189 /* Trim out escaped newlines. */
191 if (flags & POPT_READFILE_TRIMNEWLINES)
194 for (t = b, s = b, se = b + nb; *s && s < se; s++) {
204 /*@switchbreak@*/ break;
228 /*@-compdef -nullstate @*/ /* XXX cannot annotate char ** correctly */
230 /*@=compdef =nullstate @*/
234 * Check for application match.
236 * @param s config application name
237 * return 0 if config application matches
239 static int configAppMatch(poptContext con, const char * s)
244 if (con->appName == NULL) /* XXX can't happen. */
247 #if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
248 if (glob_pattern_p(s, 1)) {
250 static int flags = FNM_PATHNAME | FNM_PERIOD;
252 flags |= FNM_EXTMATCH;
255 rc = fnmatch(s, con->appName, flags);
258 rc = strcmp(s, con->appName);
262 /*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
263 static int poptConfigLine(poptContext con, char * line)
264 /*@globals fileSystem, internalState @*/
265 /*@modifies con, fileSystem, internalState @*/
270 const char * appName;
271 const char * entryType;
273 struct poptItem_s item_buf;
274 poptItem item = &item_buf;
276 int rc = POPT_ERROR_BADCONFIG;
278 if (con->appName == NULL)
281 memset(item, 0, sizeof(*item));
284 while (*se != '\0' && !_isspaceptr(se)) se++;
290 if (configAppMatch(con, appName)) goto exit;
292 while (*se != '\0' && _isspaceptr(se)) se++;
294 while (*se != '\0' && !_isspaceptr(se)) se++;
295 if (*se != '\0') *se++ = '\0';
297 while (*se != '\0' && _isspaceptr(se)) se++;
298 if (*se == '\0') goto exit;
300 while (*se != '\0' && !_isspaceptr(se)) se++;
301 if (opt[0] == '-' && *se == '\0') goto exit;
302 if (*se != '\0') *se++ = '\0';
304 while (*se != '\0' && _isspaceptr(se)) se++;
305 if (opt[0] == '-' && *se == '\0') goto exit;
307 /*@-temptrans@*/ /* FIX: line alias is saved */
308 if (opt[0] == '-' && opt[1] == '-')
309 item->option.longName = opt + 2;
310 else if (opt[0] == '-' && opt[2] == '\0')
311 item->option.shortName = opt[1];
313 const char * fn = opt;
315 /* XXX handle globs and directories in fn? */
316 if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
318 if (b == NULL || nb == 0)
321 /* Append remaining text to the interpolated file option text. */
323 size_t nse = strlen(se) + 1;
324 if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
326 (void) stpcpy( stpcpy(&b[nb-1], " "), se);
331 /* Use the basename of the path as the long option name. */
332 { const char * longName = strrchr(fn, '/');
333 if (longName != NULL)
337 if (longName == NULL) /* XXX can't happen. */
339 /* Single character basenames are treated as short options. */
340 if (longName[1] != '\0')
341 item->option.longName = longName;
343 item->option.shortName = longName[0];
348 if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
351 item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
352 for (i = 0, j = 0; i < item->argc; i++, j++) {
354 if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
355 f = item->argv[i] + sizeof("--POPTdesc=");
356 if (f[0] == '$' && f[1] == '"') f++;
357 item->option.descrip = f;
358 item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
361 if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
362 f = item->argv[i] + sizeof("--POPTargs=");
363 if (f[0] == '$' && f[1] == '"') f++;
364 item->option.argDescrip = f;
365 item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
366 item->option.argInfo |= POPT_ARG_STRING;
370 item->argv[j] = item->argv[i];
373 item->argv[j] = NULL;
378 /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
379 if (!strcmp(entryType, "alias"))
380 rc = poptAddItem(con, item, 0);
381 else if (!strcmp(entryType, "exec"))
382 rc = poptAddItem(con, item, 1);
385 rc = 0; /* XXX for now, always return success */
392 int poptReadConfigFile(poptContext con, const char * fn)
394 char * b = NULL, *be;
401 if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
402 return (errno == ENOENT ? 0 : rc);
403 if (b == NULL || nb == 0)
404 return POPT_ERROR_BADCONFIG;
406 if ((t = malloc(nb + 1)) == NULL)
411 for (se = b; se < be; se++) {
416 while (*te && _isspaceptr(te)) te++;
417 if (*te && *te != '#')
418 xx = poptConfigLine(con, te);
419 /*@switchbreak@*/ break;
420 /*@-usedef@*/ /* XXX *se may be uninitialized */
423 /* \ at the end of a line does not insert a \n */
424 if (se < be && *se != '\n') {
428 /*@switchbreak@*/ break;
431 /*@switchbreak@*/ break;
445 int poptReadConfigFiles(poptContext con, const char * paths)
447 char * buf = (paths ? xstrdup(paths) : NULL);
450 int rc = 0; /* assume success */
452 for (p = buf; p != NULL && *p != '\0'; p = pe) {
453 const char ** av = NULL;
458 /* locate start of next path element */
460 if (pe != NULL && *pe == ':')
463 pe = (char *) (p + strlen(p));
465 xx = poptGlob(con, p, &ac, &av);
467 /* work-off each resulting file from the path element */
468 for (i = 0; i < ac; i++) {
469 const char * fn = av[i];
470 if (av[i] == NULL) /* XXX can't happen */
471 /*@innercontinue@*/ continue;
472 /* XXX should '@' attention be pushed into poptReadConfigFile? */
473 if (p[0] == '@' && p[1] != '(') {
474 if (fn[0] == '@' && fn[1] != '(')
476 xx = poptSaneFile(fn);
478 rc = POPT_ERROR_BADCONFIG;
479 /*@innercontinue@*/ continue;
481 xx = poptReadConfigFile(con, fn);
499 int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
501 static const char _popt_sysconfdir[] = POPT_SYSCONFDIR "/popt";
502 static const char _popt_etc[] = "/etc/popt";
505 int rc = 0; /* assume success */
507 if (con->appName == NULL) goto exit;
509 if (strcmp(_popt_sysconfdir, _popt_etc)) {
510 rc = poptReadConfigFile(con, _popt_sysconfdir);
514 rc = poptReadConfigFile(con, _popt_etc);
517 #if defined(HAVE_GLOB_H)
518 if (!stat("/etc/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
519 const char ** av = NULL;
523 if ((rc = poptGlob(con, "/etc/popt.d/*", &ac, &av)) == 0) {
524 for (i = 0; rc == 0 && i < ac; i++) {
525 const char * fn = av[i];
526 if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
528 if (!stat(fn, &sb)) {
529 if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))
532 rc = poptReadConfigFile(con, fn);
543 if ((home = getenv("HOME"))) {
544 char * fn = malloc(strlen(home) + 20);
546 (void) stpcpy(stpcpy(fn, home), "/.popt");
547 rc = poptReadConfigFile(con, fn);
550 rc = POPT_ERROR_ERRNO;
559 poptFini(poptContext con)
561 return poptFreeContext(con);
565 poptInit(int argc, const char ** argv,
566 const struct poptOption * options, const char * configPaths)
568 poptContext con = NULL;
571 if (argv == NULL || argv[0] == NULL || options == NULL)
574 if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
575 else argv0 = argv[0];
577 con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
578 if (con != NULL&& poptReadConfigFiles(con, configPaths))