diff options
Diffstat (limited to 'third_party/popt/poptconfig.c')
-rw-r--r-- | third_party/popt/poptconfig.c | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/third_party/popt/poptconfig.c b/third_party/popt/poptconfig.c new file mode 100644 index 0000000..f0a92e0 --- /dev/null +++ b/third_party/popt/poptconfig.c @@ -0,0 +1,582 @@ +/** \ingroup popt + * \file popt/poptconfig.c + */ + +/* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.rpm.org/pub/rpm/dist. */ + +#include "system.h" +#include "poptint.h" +#include <sys/stat.h> + +#if defined(HAVE_FNMATCH_H) +#include <fnmatch.h> + +#if defined(__LCLINT__) +/*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/ +extern int fnmatch (const char *__pattern, const char *__name, int __flags) + /*@*/; +/*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/ +#endif /* __LCLINT__ */ +#endif + +#if defined(HAVE_GLOB_H) +#include <glob.h> + +#if defined(__LCLINT__) +/*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/ +extern int glob (const char *__pattern, int __flags, + /*@null@*/ int (*__errfunc) (const char *, int), + /*@out@*/ glob_t *__pglob) + /*@globals errno, fileSystem @*/ + /*@modifies *__pglob, errno, fileSystem @*/; + +/* XXX only annotation is a white lie */ +extern void globfree (/*@only@*/ glob_t *__pglob) + /*@modifies *__pglob @*/; + +/* XXX _GNU_SOURCE ifdef and/or retrofit is needed for portability. */ +extern int glob_pattern_p (const char *__pattern, int __quote) + /*@*/; +/*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/ +#endif /* __LCLINT__ */ + +#if !defined(__GLIBC__) +/* Return nonzero if PATTERN contains any metacharacters. + Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ +static int +glob_pattern_p (const char * pattern, int quote) + /*@*/ +{ + const char * p; + int open = 0; + + for (p = pattern; *p != '\0'; ++p) + switch (*p) { + case '?': + case '*': + return 1; + /*@notreached@*/ /*@switchbreak@*/ break; + case '\\': + if (quote && p[1] != '\0') + ++p; + /*@switchbreak@*/ break; + case '[': + open = 1; + /*@switchbreak@*/ break; + case ']': + if (open) + return 1; + /*@switchbreak@*/ break; + } + return 0; +} +#endif /* !defined(__GLIBC__) */ + +/*@unchecked@*/ +static int poptGlobFlags = 0; + +static int poptGlob_error(/*@unused@*/ UNUSED(const char * epath), + /*@unused@*/ UNUSED(int eerrno)) + /*@*/ +{ + return 1; +} +#endif /* HAVE_GLOB_H */ + +/** + * Return path(s) from a glob pattern. + * @param con context + * @param pattern glob pattern + * @retval *acp no. of paths + * @retval *avp array of paths + * @return 0 on success + */ +static int poptGlob(/*@unused@*/ UNUSED(poptContext con), const char * pattern, + /*@out@*/ int * acp, /*@out@*/ const char *** avp) + /*@modifies *acp, *avp @*/ +{ + const char * pat = pattern; + int rc = 0; /* assume success */ + + /* XXX skip the attention marker. */ + if (pat[0] == '@' && pat[1] != '(') + pat++; + +#if defined(HAVE_GLOB_H) + if (glob_pattern_p(pat, 0)) { + glob_t _g, *pglob = &_g; + + if (!glob(pat, poptGlobFlags, poptGlob_error, pglob)) { + if (acp) { + *acp = (int) pglob->gl_pathc; + pglob->gl_pathc = 0; + } + if (avp) { +/*@-onlytrans@*/ + *avp = (const char **) pglob->gl_pathv; +/*@=onlytrans@*/ + pglob->gl_pathv = NULL; + } +/*@-nullstate@*/ + globfree(pglob); +/*@=nullstate@*/ + } else + rc = POPT_ERROR_ERRNO; + } else +#endif /* HAVE_GLOB_H */ + { + if (acp) + *acp = 1; + if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL) + (*avp)[0] = xstrdup(pat); + } + + return rc; +} + +/*@access poptContext @*/ + +int poptSaneFile(const char * fn) +{ + struct stat sb; + uid_t uid = getuid(); + + if (stat(fn, &sb) == -1) + return 1; + if ((uid_t)sb.st_uid != uid) + return 0; + if (!S_ISREG(sb.st_mode)) + return 0; +/*@-bitwisesigned@*/ + if (sb.st_mode & (S_IWGRP|S_IWOTH)) + return 0; +/*@=bitwisesigned@*/ + return 1; +} + +int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags) +{ + int fdno; + char * b = NULL; + off_t nb = 0; + char * s, * t, * se; + int rc = POPT_ERROR_ERRNO; /* assume failure */ + + fdno = open(fn, O_RDONLY); + if (fdno < 0) + goto exit; + + if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1 + || lseek(fdno, 0, SEEK_SET) == (off_t)-1 + || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL + || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb) + { + int oerrno = errno; + (void) close(fdno); + errno = oerrno; + goto exit; + } + if (close(fdno) == -1) + goto exit; + if (b == NULL) { + rc = POPT_ERROR_MALLOC; + goto exit; + } + rc = 0; + + /* Trim out escaped newlines. */ +/*@-bitwisesigned@*/ + if (flags & POPT_READFILE_TRIMNEWLINES) +/*@=bitwisesigned@*/ + { + for (t = b, s = b, se = b + nb; *s && s < se; s++) { + switch (*s) { + case '\\': + if (s[1] == '\n') { + s++; + continue; + } + /*@fallthrough@*/ + default: + *t++ = *s; + /*@switchbreak@*/ break; + } + } + *t++ = '\0'; + nb = (off_t)(t - b); + } + +exit: + if (rc != 0) { +/*@-usedef@*/ + if (b) + free(b); +/*@=usedef@*/ + b = NULL; + nb = 0; + } + if (bp) + *bp = b; +/*@-usereleased@*/ + else if (b) + free(b); +/*@=usereleased@*/ + if (nbp) + *nbp = (size_t)nb; +/*@-compdef -nullstate @*/ /* XXX cannot annotate char ** correctly */ + return rc; +/*@=compdef =nullstate @*/ +} + +/** + * Check for application match. + * @param con context + * @param s config application name + * return 0 if config application matches + */ +static int configAppMatch(poptContext con, const char * s) + /*@*/ +{ + int rc = 1; + + if (con->appName == NULL) /* XXX can't happen. */ + return rc; + +#if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H) + if (glob_pattern_p(s, 1)) { +/*@-bitwisesigned@*/ + static int flags = FNM_PATHNAME | FNM_PERIOD; +#ifdef FNM_EXTMATCH + flags |= FNM_EXTMATCH; +#endif +/*@=bitwisesigned@*/ + rc = fnmatch(s, con->appName, flags); + } else +#endif + rc = strcmp(s, con->appName); + return rc; +} + +/*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */ +static int poptConfigLine(poptContext con, char * line) + /*@globals fileSystem, internalState @*/ + /*@modifies con, fileSystem, internalState @*/ +{ + char *b = NULL; + size_t nb = 0; + char * se = line; + const char * appName; + const char * entryType; + const char * opt; + struct poptItem_s item_buf; + poptItem item = &item_buf; + int i, j; + int rc = POPT_ERROR_BADCONFIG; + + if (con->appName == NULL) + goto exit; + + memset(item, 0, sizeof(*item)); + + appName = se; + while (*se != '\0' && !_isspaceptr(se)) se++; + if (*se == '\0') + goto exit; + else + *se++ = '\0'; + + if (configAppMatch(con, appName)) goto exit; + + while (*se != '\0' && _isspaceptr(se)) se++; + entryType = se; + while (*se != '\0' && !_isspaceptr(se)) se++; + if (*se != '\0') *se++ = '\0'; + + while (*se != '\0' && _isspaceptr(se)) se++; + if (*se == '\0') goto exit; + opt = se; + while (*se != '\0' && !_isspaceptr(se)) se++; + if (opt[0] == '-' && *se == '\0') goto exit; + if (*se != '\0') *se++ = '\0'; + + while (*se != '\0' && _isspaceptr(se)) se++; + if (opt[0] == '-' && *se == '\0') goto exit; + +/*@-temptrans@*/ /* FIX: line alias is saved */ + if (opt[0] == '-' && opt[1] == '-') + item->option.longName = opt + 2; + else if (opt[0] == '-' && opt[2] == '\0') + item->option.shortName = opt[1]; + else { + const char * fn = opt; + + /* XXX handle globs and directories in fn? */ + if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0) + goto exit; + if (b == NULL || nb == 0) + goto exit; + + /* Append remaining text to the interpolated file option text. */ + if (*se != '\0') { + size_t nse = strlen(se) + 1; + if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */ + goto exit; + (void) stpcpy( stpcpy(&b[nb-1], " "), se); + nb += nse; + } + se = b; + + /* Use the basename of the path as the long option name. */ + { const char * longName = strrchr(fn, '/'); + if (longName != NULL) + longName++; + else + longName = fn; + if (longName == NULL) /* XXX can't happen. */ + goto exit; + /* Single character basenames are treated as short options. */ + if (longName[1] != '\0') + item->option.longName = longName; + else + item->option.shortName = longName[0]; + } + } +/*@=temptrans@*/ + + if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit; + +/*@-modobserver@*/ + item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN; + for (i = 0, j = 0; i < item->argc; i++, j++) { + const char * f; + if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) { + f = item->argv[i] + sizeof("--POPTdesc="); + if (f[0] == '$' && f[1] == '"') f++; + item->option.descrip = f; + item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN; + j--; + } else + if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) { + f = item->argv[i] + sizeof("--POPTargs="); + if (f[0] == '$' && f[1] == '"') f++; + item->option.argDescrip = f; + item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN; + item->option.argInfo |= POPT_ARG_STRING; + j--; + } else + if (j != i) + item->argv[j] = item->argv[i]; + } + if (j != i) { + item->argv[j] = NULL; + item->argc = j; + } +/*@=modobserver@*/ + +/*@-nullstate@*/ /* FIX: item->argv[] may be NULL */ + if (!strcmp(entryType, "alias")) + rc = poptAddItem(con, item, 0); + else if (!strcmp(entryType, "exec")) + rc = poptAddItem(con, item, 1); +/*@=nullstate@*/ +exit: + rc = 0; /* XXX for now, always return success */ + if (b) + free(b); + return rc; +} +/*@=compmempass@*/ + +int poptReadConfigFile(poptContext con, const char * fn) +{ + char * b = NULL, *be; + size_t nb = 0; + const char *se; + char *t, *te; + int rc; + int xx; + + if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0) + return (errno == ENOENT ? 0 : rc); + if (b == NULL || nb == 0) + return POPT_ERROR_BADCONFIG; + + if ((t = malloc(nb + 1)) == NULL) + goto exit; + te = t; + + be = (b + nb); + for (se = b; se < be; se++) { + switch (*se) { + case '\n': + *te = '\0'; + te = t; + while (*te && _isspaceptr(te)) te++; + if (*te && *te != '#') + xx = poptConfigLine(con, te); + /*@switchbreak@*/ break; +/*@-usedef@*/ /* XXX *se may be uninitialized */ + case '\\': + *te = *se++; + /* \ at the end of a line does not insert a \n */ + if (se < be && *se != '\n') { + te++; + *te++ = *se; + } + /*@switchbreak@*/ break; + default: + *te++ = *se; + /*@switchbreak@*/ break; +/*@=usedef@*/ + } + } + + free(t); + rc = 0; + +exit: + if (b) + free(b); + return rc; +} + +int poptReadConfigFiles(poptContext con, const char * paths) +{ + char * buf = (paths ? xstrdup(paths) : NULL); + const char * p; + char * pe; + int rc = 0; /* assume success */ + + for (p = buf; p != NULL && *p != '\0'; p = pe) { + const char ** av = NULL; + int ac = 0; + int i; + int xx; + + /* locate start of next path element */ + pe = strchr(p, ':'); + if (pe != NULL && *pe == ':') + *pe++ = '\0'; + else + pe = (char *) (p + strlen(p)); + + xx = poptGlob(con, p, &ac, &av); + + /* work-off each resulting file from the path element */ + for (i = 0; i < ac; i++) { + const char * fn = av[i]; + if (av[i] == NULL) /* XXX can't happen */ + /*@innercontinue@*/ continue; + /* XXX should '@' attention be pushed into poptReadConfigFile? */ + if (p[0] == '@' && p[1] != '(') { + if (fn[0] == '@' && fn[1] != '(') + fn++; + xx = poptSaneFile(fn); + if (!xx && rc == 0) + rc = POPT_ERROR_BADCONFIG; + /*@innercontinue@*/ continue; + } + xx = poptReadConfigFile(con, fn); + if (xx && rc == 0) + rc = xx; + free((void *)av[i]); + av[i] = NULL; + } + free(av); + av = NULL; + } + +/*@-usedef@*/ + if (buf) + free(buf); +/*@=usedef@*/ + + return rc; +} + +int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv)) +{ + static const char _popt_sysconfdir[] = POPT_SYSCONFDIR "/popt"; + static const char _popt_etc[] = "/etc/popt"; + char * home; + struct stat sb; + int rc = 0; /* assume success */ + + if (con->appName == NULL) goto exit; + + if (strcmp(_popt_sysconfdir, _popt_etc)) { + rc = poptReadConfigFile(con, _popt_sysconfdir); + if (rc) goto exit; + } + + rc = poptReadConfigFile(con, _popt_etc); + if (rc) goto exit; + +#if defined(HAVE_GLOB_H) + if (!stat("/etc/popt.d", &sb) && S_ISDIR(sb.st_mode)) { + const char ** av = NULL; + int ac = 0; + int i; + + if ((rc = poptGlob(con, "/etc/popt.d/*", &ac, &av)) == 0) { + for (i = 0; rc == 0 && i < ac; i++) { + const char * fn = av[i]; + if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave")) + continue; + if (!stat(fn, &sb)) { + if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) + continue; + } + rc = poptReadConfigFile(con, fn); + free((void *)av[i]); + av[i] = NULL; + } + free(av); + av = NULL; + } + } + if (rc) goto exit; +#endif + + if ((home = getenv("HOME"))) { + char * fn = malloc(strlen(home) + 20); + if (fn != NULL) { + (void) stpcpy(stpcpy(fn, home), "/.popt"); + rc = poptReadConfigFile(con, fn); + free(fn); + } else + rc = POPT_ERROR_ERRNO; + if (rc) goto exit; + } + +exit: + return rc; +} + +poptContext +poptFini(poptContext con) +{ + return poptFreeContext(con); +} + +poptContext +poptInit(int argc, const char ** argv, + const struct poptOption * options, const char * configPaths) +{ + poptContext con = NULL; + const char * argv0; + + if (argv == NULL || argv[0] == NULL || options == NULL) + return con; + + if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++; + else argv0 = argv[0]; + + con = poptGetContext(argv0, argc, (const char **)argv, options, 0); + if (con != NULL&& poptReadConfigFiles(con, configPaths)) + con = poptFini(con); + + return con; +} |