popt
1.16
|
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 }