popt  1.16
poptconfig.c
Go to the documentation of this file.
00001 
00005 /* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
00006    file accompanying popt source distributions, available from 
00007    ftp://ftp.rpm.org/pub/rpm/dist. */
00008 
00009 #include "system.h"
00010 #include "poptint.h"
00011 #include <sys/stat.h>
00012 
00013 #if defined(HAVE_FNMATCH_H)
00014 #include <fnmatch.h>
00015 
00016 #if defined(__LCLINT__)
00017 /*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
00018 extern int fnmatch (const char *__pattern, const char *__name, int __flags)
00019         /*@*/;
00020 /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
00021 #endif  /* __LCLINT__ */
00022 #endif
00023 
00024 #if defined(HAVE_GLOB_H)
00025 #include <glob.h>
00026 
00027 #if defined(__LCLINT__)
00028 /*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
00029 extern int glob (const char *__pattern, int __flags,
00030                 /*@null@*/ int (*__errfunc) (const char *, int),
00031                 /*@out@*/ glob_t *__pglob)
00032         /*@globals errno, fileSystem @*/
00033         /*@modifies *__pglob, errno, fileSystem @*/;
00034 
00035 /* XXX only annotation is a white lie */
00036 extern void globfree (/*@only@*/ glob_t *__pglob)
00037         /*@modifies *__pglob @*/;
00038 
00039 /* XXX _GNU_SOURCE ifdef and/or retrofit is needed for portability. */
00040 extern int glob_pattern_p (const char *__pattern, int __quote)
00041         /*@*/;
00042 /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
00043 #endif  /* __LCLINT__ */
00044 
00045 #if !defined(__GLIBC__)
00046 /* Return nonzero if PATTERN contains any metacharacters.
00047    Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
00048 static int
00049 glob_pattern_p (const char * pattern, int quote)
00050         /*@*/
00051 {
00052     const char * p;
00053     int open = 0;
00054 
00055     for (p = pattern; *p != '\0'; ++p)
00056     switch (*p) {
00057     case '?':
00058     case '*':
00059         return 1;
00060         /*@notreached@*/ /*@switchbreak@*/ break;
00061     case '\\':
00062         if (quote && p[1] != '\0')
00063           ++p;
00064         /*@switchbreak@*/ break;
00065     case '[':
00066         open = 1;
00067         /*@switchbreak@*/ break;
00068     case ']':
00069         if (open)
00070           return 1;
00071         /*@switchbreak@*/ break;
00072     }
00073     return 0;
00074 }
00075 #endif  /* !defined(__GLIBC__) */
00076 
00077 /*@unchecked@*/
00078 static int poptGlobFlags = 0;
00079 
00080 static int poptGlob_error(/*@unused@*/ UNUSED(const char * epath),
00081                 /*@unused@*/ UNUSED(int eerrno))
00082         /*@*/
00083 {
00084     return 1;
00085 }
00086 #endif  /* HAVE_GLOB_H */
00087 
00096 static int poptGlob(/*@unused@*/ UNUSED(poptContext con), const char * pattern,
00097                 /*@out@*/ int * acp, /*@out@*/ const char *** avp)
00098         /*@modifies *acp, *avp @*/
00099 {
00100     const char * pat = pattern;
00101     int rc = 0;         /* assume success */
00102 
00103     /* XXX skip the attention marker. */
00104     if (pat[0] == '@' && pat[1] != '(')
00105         pat++;
00106 
00107 #if defined(HAVE_GLOB_H)
00108     if (glob_pattern_p(pat, 0)) {
00109         glob_t _g, *pglob = &_g;
00110 
00111         if (!glob(pat, poptGlobFlags, poptGlob_error, pglob)) {
00112             if (acp) {
00113                 *acp = (int) pglob->gl_pathc;
00114                 pglob->gl_pathc = 0;
00115             }
00116             if (avp) {
00117 /*@-onlytrans@*/
00118                 *avp = (const char **) pglob->gl_pathv;
00119 /*@=onlytrans@*/
00120                 pglob->gl_pathv = NULL;
00121             }
00122 /*@-nullstate@*/
00123             globfree(pglob);
00124 /*@=nullstate@*/
00125         } else
00126             rc = POPT_ERROR_ERRNO;
00127     } else
00128 #endif  /* HAVE_GLOB_H */
00129     {
00130         if (acp)
00131             *acp = 1;
00132         if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
00133             (*avp)[0] = xstrdup(pat);
00134     }
00135 
00136     return rc;
00137 }
00138 
00139 /*@access poptContext @*/
00140 
00141 int poptSaneFile(const char * fn)
00142 {
00143     struct stat sb;
00144     uid_t uid = getuid();
00145 
00146     if (stat(fn, &sb) == -1)
00147         return 1;
00148     if ((uid_t)sb.st_uid != uid)
00149         return 0;
00150     if (!S_ISREG(sb.st_mode))
00151         return 0;
00152 /*@-bitwisesigned@*/
00153     if (sb.st_mode & (S_IWGRP|S_IWOTH))
00154         return 0;
00155 /*@=bitwisesigned@*/
00156     return 1;
00157 }
00158 
00159 int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
00160 {
00161     int fdno;
00162     char * b = NULL;
00163     off_t nb = 0;
00164     char * s, * t, * se;
00165     int rc = POPT_ERROR_ERRNO;  /* assume failure */
00166 
00167     fdno = open(fn, O_RDONLY);
00168     if (fdno < 0)
00169         goto exit;
00170 
00171     if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
00172      || lseek(fdno, 0, SEEK_SET) == (off_t)-1
00173      || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
00174      || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
00175     {
00176         int oerrno = errno;
00177         (void) close(fdno);
00178         errno = oerrno;
00179         goto exit;
00180     }
00181     if (close(fdno) == -1)
00182         goto exit;
00183     if (b == NULL) {
00184         rc = POPT_ERROR_MALLOC;
00185         goto exit;
00186     }
00187     rc = 0;
00188 
00189    /* Trim out escaped newlines. */
00190 /*@-bitwisesigned@*/
00191     if (flags & POPT_READFILE_TRIMNEWLINES)
00192 /*@=bitwisesigned@*/
00193     {
00194         for (t = b, s = b, se = b + nb; *s && s < se; s++) {
00195             switch (*s) {
00196             case '\\':
00197                 if (s[1] == '\n') {
00198                     s++;
00199                     continue;
00200                 }
00201                 /*@fallthrough@*/
00202             default:
00203                 *t++ = *s;
00204                 /*@switchbreak@*/ break;
00205             }
00206         }
00207         *t++ = '\0';
00208         nb = (off_t)(t - b);
00209     }
00210 
00211 exit:
00212     if (rc != 0) {
00213 /*@-usedef@*/
00214         if (b)
00215             free(b);
00216 /*@=usedef@*/
00217         b = NULL;
00218         nb = 0;
00219     }
00220     if (bp)
00221         *bp = b;
00222 /*@-usereleased@*/
00223     else if (b)
00224         free(b);
00225 /*@=usereleased@*/
00226     if (nbp)
00227         *nbp = (size_t)nb;
00228 /*@-compdef -nullstate @*/      /* XXX cannot annotate char ** correctly */
00229     return rc;
00230 /*@=compdef =nullstate @*/
00231 }
00232 
00239 static int configAppMatch(poptContext con, const char * s)
00240         /*@*/
00241 {
00242     int rc = 1;
00243 
00244     if (con->appName == NULL)   /* XXX can't happen. */
00245         return rc;
00246 
00247 #if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
00248     if (glob_pattern_p(s, 1)) {
00249 /*@-bitwisesigned@*/
00250         static int flags = FNM_PATHNAME | FNM_PERIOD;
00251 #ifdef FNM_EXTMATCH
00252         flags |= FNM_EXTMATCH;
00253 #endif
00254 /*@=bitwisesigned@*/
00255         rc = fnmatch(s, con->appName, flags);
00256     } else
00257 #endif
00258         rc = strcmp(s, con->appName);
00259     return rc;
00260 }
00261 
00262 /*@-compmempass@*/      /* FIX: item->option.longName kept, not dependent. */
00263 static int poptConfigLine(poptContext con, char * line)
00264         /*@globals fileSystem, internalState @*/
00265         /*@modifies con, fileSystem, internalState @*/
00266 {
00267     char *b = NULL;
00268     size_t nb = 0;
00269     char * se = line;
00270     const char * appName;
00271     const char * entryType;
00272     const char * opt;
00273     struct poptItem_s item_buf;
00274     poptItem item = &item_buf;
00275     int i, j;
00276     int rc = POPT_ERROR_BADCONFIG;
00277 
00278     if (con->appName == NULL)
00279         goto exit;
00280     
00281     memset(item, 0, sizeof(*item));
00282 
00283     appName = se;
00284     while (*se != '\0' && !_isspaceptr(se)) se++;
00285     if (*se == '\0')
00286         goto exit;
00287     else
00288         *se++ = '\0';
00289 
00290     if (configAppMatch(con, appName)) goto exit;
00291 
00292     while (*se != '\0' && _isspaceptr(se)) se++;
00293     entryType = se;
00294     while (*se != '\0' && !_isspaceptr(se)) se++;
00295     if (*se != '\0') *se++ = '\0';
00296 
00297     while (*se != '\0' && _isspaceptr(se)) se++;
00298     if (*se == '\0') goto exit;
00299     opt = se;
00300     while (*se != '\0' && !_isspaceptr(se)) se++;
00301     if (opt[0] == '-' && *se == '\0') goto exit;
00302     if (*se != '\0') *se++ = '\0';
00303 
00304     while (*se != '\0' && _isspaceptr(se)) se++;
00305     if (opt[0] == '-' && *se == '\0') goto exit;
00306 
00307 /*@-temptrans@*/ /* FIX: line alias is saved */
00308     if (opt[0] == '-' && opt[1] == '-')
00309         item->option.longName = opt + 2;
00310     else if (opt[0] == '-' && opt[2] == '\0')
00311         item->option.shortName = opt[1];
00312     else {
00313         const char * fn = opt;
00314 
00315         /* XXX handle globs and directories in fn? */
00316         if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
00317             goto exit;
00318         if (b == NULL || nb == 0)
00319             goto exit;
00320 
00321         /* Append remaining text to the interpolated file option text. */
00322         if (*se != '\0') {
00323             size_t nse = strlen(se) + 1;
00324             if ((b = realloc(b, (nb + nse))) == NULL)   /* XXX can't happen */
00325                 goto exit;
00326             (void) stpcpy( stpcpy(&b[nb-1], " "), se);
00327             nb += nse;
00328         }
00329         se = b;
00330 
00331         /* Use the basename of the path as the long option name. */
00332         {   const char * longName = strrchr(fn, '/');
00333             if (longName != NULL)
00334                 longName++;
00335             else
00336                 longName = fn;
00337             if (longName == NULL)       /* XXX can't happen. */
00338                 goto exit;
00339             /* Single character basenames are treated as short options. */
00340             if (longName[1] != '\0')
00341                 item->option.longName = longName;
00342             else
00343                 item->option.shortName = longName[0];
00344         }
00345     }
00346 /*@=temptrans@*/
00347 
00348     if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
00349 
00350 /*@-modobserver@*/
00351     item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
00352     for (i = 0, j = 0; i < item->argc; i++, j++) {
00353         const char * f;
00354         if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
00355             f = item->argv[i] + sizeof("--POPTdesc=");
00356             if (f[0] == '$' && f[1] == '"') f++;
00357             item->option.descrip = f;
00358             item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
00359             j--;
00360         } else
00361         if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
00362             f = item->argv[i] + sizeof("--POPTargs=");
00363             if (f[0] == '$' && f[1] == '"') f++;
00364             item->option.argDescrip = f;
00365             item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
00366             item->option.argInfo |= POPT_ARG_STRING;
00367             j--;
00368         } else
00369         if (j != i)
00370             item->argv[j] = item->argv[i];
00371     }
00372     if (j != i) {
00373         item->argv[j] = NULL;
00374         item->argc = j;
00375     }
00376 /*@=modobserver@*/
00377         
00378 /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
00379     if (!strcmp(entryType, "alias"))
00380         rc = poptAddItem(con, item, 0);
00381     else if (!strcmp(entryType, "exec"))
00382         rc = poptAddItem(con, item, 1);
00383 /*@=nullstate@*/
00384 exit:
00385     rc = 0;     /* XXX for now, always return success */
00386     if (b)
00387         free(b);
00388     return rc;
00389 }
00390 /*@=compmempass@*/
00391 
00392 int poptReadConfigFile(poptContext con, const char * fn)
00393 {
00394     char * b = NULL, *be;
00395     size_t nb = 0;
00396     const char *se;
00397     char *t, *te;
00398     int rc;
00399     int xx;
00400 
00401     if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
00402         return (errno == ENOENT ? 0 : rc);
00403     if (b == NULL || nb == 0)
00404         return POPT_ERROR_BADCONFIG;
00405 
00406     if ((t = malloc(nb + 1)) == NULL)
00407         goto exit;
00408     te = t;
00409 
00410     be = (b + nb);
00411     for (se = b; se < be; se++) {
00412         switch (*se) {
00413           case '\n':
00414             *te = '\0';
00415             te = t;
00416             while (*te && _isspaceptr(te)) te++;
00417             if (*te && *te != '#')
00418                 xx = poptConfigLine(con, te);
00419             /*@switchbreak@*/ break;
00420 /*@-usedef@*/   /* XXX *se may be uninitialized */
00421           case '\\':
00422             *te = *se++;
00423             /* \ at the end of a line does not insert a \n */
00424             if (se < be && *se != '\n') {
00425                 te++;
00426                 *te++ = *se;
00427             }
00428             /*@switchbreak@*/ break;
00429           default:
00430             *te++ = *se;
00431             /*@switchbreak@*/ break;
00432 /*@=usedef@*/
00433         }
00434     }
00435 
00436     free(t);
00437     rc = 0;
00438 
00439 exit:
00440     if (b)
00441         free(b);
00442     return rc;
00443 }
00444 
00445 int poptReadConfigFiles(poptContext con, const char * paths)
00446 {
00447     char * buf = (paths ? xstrdup(paths) : NULL);
00448     const char * p;
00449     char * pe;
00450     int rc = 0;         /* assume success */
00451 
00452     for (p = buf; p != NULL && *p != '\0'; p = pe) {
00453         const char ** av = NULL;
00454         int ac = 0;
00455         int i;
00456         int xx;
00457 
00458         /* locate start of next path element */
00459         pe = strchr(p, ':');
00460         if (pe != NULL && *pe == ':')
00461             *pe++ = '\0';
00462         else
00463             pe = (char *) (p + strlen(p));
00464 
00465         xx = poptGlob(con, p, &ac, &av);
00466 
00467         /* work-off each resulting file from the path element */
00468         for (i = 0; i < ac; i++) {
00469             const char * fn = av[i];
00470             if (av[i] == NULL)  /* XXX can't happen */
00471                 /*@innercontinue@*/ continue;
00472             /* XXX should '@' attention be pushed into poptReadConfigFile? */
00473             if (p[0] == '@' && p[1] != '(') {
00474                 if (fn[0] == '@' && fn[1] != '(')
00475                     fn++;
00476                 xx = poptSaneFile(fn);
00477                 if (!xx && rc == 0)
00478                     rc = POPT_ERROR_BADCONFIG;
00479                 /*@innercontinue@*/ continue;
00480             }
00481             xx = poptReadConfigFile(con, fn);
00482             if (xx && rc == 0)
00483                 rc = xx;
00484             free((void *)av[i]);
00485             av[i] = NULL;
00486         }
00487         free(av);
00488         av = NULL;
00489     }
00490 
00491 /*@-usedef@*/
00492     if (buf)
00493         free(buf);
00494 /*@=usedef@*/
00495 
00496     return rc;
00497 }
00498 
00499 int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
00500 {
00501     static const char _popt_sysconfdir[] = POPT_SYSCONFDIR "/popt";
00502     static const char _popt_etc[] = "/etc/popt";
00503     char * home;
00504     struct stat sb;
00505     int rc = 0;         /* assume success */
00506 
00507     if (con->appName == NULL) goto exit;
00508 
00509     if (strcmp(_popt_sysconfdir, _popt_etc)) {
00510         rc = poptReadConfigFile(con, _popt_sysconfdir);
00511         if (rc) goto exit;
00512     }
00513 
00514     rc = poptReadConfigFile(con, _popt_etc);
00515     if (rc) goto exit;
00516 
00517 #if defined(HAVE_GLOB_H)
00518     if (!stat("/etc/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
00519         const char ** av = NULL;
00520         int ac = 0;
00521         int i;
00522 
00523         if ((rc = poptGlob(con, "/etc/popt.d/*", &ac, &av)) == 0) {
00524             for (i = 0; rc == 0 && i < ac; i++) {
00525                 const char * fn = av[i];
00526                 if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
00527                     continue;
00528                 if (!stat(fn, &sb)) {
00529                     if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))
00530                         continue;
00531                 }
00532                 rc = poptReadConfigFile(con, fn);
00533                 free((void *)av[i]);
00534                 av[i] = NULL;
00535             }
00536             free(av);
00537             av = NULL;
00538         }
00539     }
00540     if (rc) goto exit;
00541 #endif
00542 
00543     if ((home = getenv("HOME"))) {
00544         char * fn = malloc(strlen(home) + 20);
00545         if (fn != NULL) {
00546             (void) stpcpy(stpcpy(fn, home), "/.popt");
00547             rc = poptReadConfigFile(con, fn);
00548             free(fn);
00549         } else
00550             rc = POPT_ERROR_ERRNO;
00551         if (rc) goto exit;
00552     }
00553 
00554 exit:
00555     return rc;
00556 }
00557 
00558 poptContext
00559 poptFini(poptContext con)
00560 {
00561     return poptFreeContext(con);
00562 }
00563 
00564 poptContext
00565 poptInit(int argc, const char ** argv,
00566                 const struct poptOption * options, const char * configPaths)
00567 {
00568     poptContext con = NULL;
00569     const char * argv0;
00570 
00571     if (argv == NULL || argv[0] == NULL || options == NULL)
00572         return con;
00573 
00574     if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
00575     else argv0 = argv[0];
00576    
00577     con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
00578     if (con != NULL&& poptReadConfigFiles(con, configPaths))
00579         con = poptFini(con);
00580 
00581     return con;
00582 }

Generated for popt by  doxygen 1.7.6.1