diff options
author | jbj <jbj> | 2013-07-26 06:09:05 +0000 |
---|---|---|
committer | jbj <jbj> | 2013-07-26 06:09:05 +0000 |
commit | e43c9bc1918103d7e82f917598446a5052fa2e24 (patch) | |
tree | 4d0d603798064363a354544fa9aa1a58c3799802 | |
parent | ec2c8709a44195af1f2eb3efdf7791e622d47d1b (diff) | |
download | libpopt-rpm-5_4.tar.gz |
- coverity fixes.rpm-5_4
-rw-r--r-- | poptconfig.c | 573 |
1 files changed, 480 insertions, 93 deletions
diff --git a/poptconfig.c b/poptconfig.c index e84e607..8a56769 100644 --- a/poptconfig.c +++ b/poptconfig.c @@ -3,58 +3,347 @@ */ /* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING - file accompanying popt source distributions, available from + 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 = (const char**) 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 = (char*) 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@*/ + b=_free(b); +/*@=usedef@*/ + 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 void configLine(poptContext con, char * line) - /*@modifies con @*/ +static int poptConfigLine(poptContext con, char * line) + /*@globals fileSystem, internalState @*/ + /*@modifies con, fileSystem, internalState @*/ { - size_t nameLength; + 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) - return; - nameLength = strlen(con->appName); - -/*@-boundswrite@*/ + goto exit; + memset(item, 0, sizeof(*item)); - if (strncmp(line, con->appName, nameLength)) return; + appName = se; + while (*se != '\0' && !_isspaceptr(se)) se++; + if (*se == '\0') + goto exit; + else + *se++ = '\0'; - line += nameLength; - if (*line == '\0' || !isspace(*line)) return; + if (configAppMatch(con, appName)) goto exit; - while (*line != '\0' && isspace(*line)) line++; - entryType = line; - while (*line == '\0' || !isspace(*line)) line++; - *line++ = '\0'; + while (*se != '\0' && _isspaceptr(se)) se++; + entryType = se; + while (*se != '\0' && !_isspaceptr(se)) se++; + if (*se != '\0') *se++ = '\0'; - while (*line != '\0' && isspace(*line)) line++; - if (*line == '\0') return; - opt = line; - while (*line == '\0' || !isspace(*line)) line++; - *line++ = '\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 (*line != '\0' && isspace(*line)) line++; - if (*line == '\0') return; + 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 = (char*) 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(line, &item->argc, &item->argv)) return; + if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit; /*@-modobserver@*/ item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN; @@ -83,107 +372,205 @@ static void configLine(poptContext con, char * line) item->argc = j; } /*@=modobserver@*/ -/*@=boundswrite@*/ - + /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */ if (!strcmp(entryType, "alias")) - (void) poptAddItem(con, item, 0); + rc = poptAddItem(con, item, 0); else if (!strcmp(entryType, "exec")) - (void) poptAddItem(con, item, 1); + rc = poptAddItem(con, item, 1); /*@=nullstate@*/ +exit: + rc = 0; /* XXX for now, always return success */ + b=_free(b); + return rc; } /*@=compmempass@*/ int poptReadConfigFile(poptContext con, const char * fn) { - char * file = NULL, * chptr, * end; - char * buf = NULL; -/*@dependent@*/ char * dst; - int fd, rc; - off_t fileLength; - - fd = open(fn, O_RDONLY); - if (fd < 0) - return (errno == ENOENT ? 0 : POPT_ERROR_ERRNO); - - fileLength = lseek(fd, 0, SEEK_END); - if (fileLength == -1 || lseek(fd, 0, 0) == -1) { - rc = errno; - (void) close(fd); - errno = rc; - return POPT_ERROR_ERRNO; - } + char * b = NULL, *be; + size_t nb = 0; + const char *se; + char *t, *te; + int rc; - file = malloc(fileLength + 1); - if (read(fd, (char *)file, fileLength) != fileLength) { - rc = errno; - (void) close(fd); - errno = rc; - return POPT_ERROR_ERRNO; - } - if (close(fd) == -1) { - free(file); - return POPT_ERROR_ERRNO; + if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0) + return (errno == ENOENT ? 0 : rc); + if (b == NULL || nb == 0) { + rc = POPT_ERROR_BADCONFIG; + goto exit; } -/*@-boundswrite@*/ - dst = buf = malloc(fileLength + 1); + if ((t = (char*) malloc(nb + 1)) == NULL) + goto exit; + te = t; - chptr = file; - end = (file + fileLength); -/*@-infloops@*/ /* LCL: can't detect chptr++ */ - while (chptr < end) { - switch (*chptr) { + be = (b + nb); + for (se = b; se < be; se++) { + switch (*se) { case '\n': - *dst = '\0'; - dst = buf; - while (*dst && isspace(*dst)) dst++; - if (*dst && *dst != '#') - configLine(con, dst); - chptr++; + *te = '\0'; + te = t; + while (*te && _isspaceptr(te)) te++; + if (*te && *te != '#') { + int xx; + xx = poptConfigLine(con, te); /* XXX: unchecked */ + } /*@switchbreak@*/ break; +/*@-usedef@*/ /* XXX *se may be uninitialized */ case '\\': - *dst++ = *chptr++; - if (chptr < end) { - if (*chptr == '\n') - dst--, chptr++; - /* \ at the end of a line does not insert a \n */ - else - *dst++ = *chptr++; + *te = *se++; + /* \ at the end of a line does not insert a \n */ + if (se < be && *se != '\n') { + te++; + *te++ = *se; } /*@switchbreak@*/ break; default: - *dst++ = *chptr++; + *te++ = *se; /*@switchbreak@*/ break; +/*@=usedef@*/ } } -/*@=infloops@*/ -/*@=boundswrite@*/ - free(file); - free(buf); + t = _free(t); + rc = 0; - return 0; +exit: + b = _free(b); + return rc; } -int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv) +int poptReadConfigFiles(poptContext con, const char * paths) { - char * fn, * home; - int rc; + 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; + av[i]=_free((void *)av[i]); + } + av=_free(av); + } - if (con->appName == NULL) return 0; +/*@-usedef@*/ + buf=_free(buf); +/*@=usedef@*/ - rc = poptReadConfigFile(con, "/etc/popt"); - if (rc) return rc; + 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; + + if ((rc = poptGlob(con, "/etc/popt.d/*", &ac, &av)) == 0) { + int i; + 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); + av[i]=_free((void *)av[i]); + } + av=_free(av); + } + } + if (rc) goto exit; +#endif if ((home = getenv("HOME"))) { - fn = malloc(strlen(home) + 20); - strcpy(fn, home); - strcat(fn, "/.popt"); - rc = poptReadConfigFile(con, fn); - free(fn); - if (rc) return rc; + char * fn = (char*) malloc(strlen(home) + 20); + if (fn != NULL) { + (void) stpcpy(stpcpy(fn, home), "/.popt"); + rc = poptReadConfigFile(con, fn); + fn=_free(fn); + } else + rc = POPT_ERROR_ERRNO; + if (rc) goto exit; } - return 0; +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; } |