poptconfig.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /** \ingroup popt
  2. * \file popt/poptconfig.c
  3. */
  4. /* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
  5. file accompanying popt source distributions, available from
  6. ftp://ftp.rpm.org/pub/rpm/dist. */
  7. #include "system.h"
  8. #include "poptint.h"
  9. #include <sys/stat.h>
  10. #if defined(HAVE_FNMATCH_H)
  11. #include <fnmatch.h>
  12. #if defined(__LCLINT__)
  13. /*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
  14. extern int fnmatch (const char *__pattern, const char *__name, int __flags)
  15. /*@*/;
  16. /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
  17. #endif /* __LCLINT__ */
  18. #endif
  19. #if defined(HAVE_GLOB_H)
  20. #include <glob.h>
  21. #if defined(__LCLINT__)
  22. /*@-declundef -exportheader -incondefs -protoparammatch -redecl -type @*/
  23. extern int glob (const char *__pattern, int __flags,
  24. /*@null@*/ int (*__errfunc) (const char *, int),
  25. /*@out@*/ glob_t *__pglob)
  26. /*@globals errno, fileSystem @*/
  27. /*@modifies *__pglob, errno, fileSystem @*/;
  28. /* XXX only annotation is a white lie */
  29. extern void globfree (/*@only@*/ glob_t *__pglob)
  30. /*@modifies *__pglob @*/;
  31. /* XXX _GNU_SOURCE ifdef and/or retrofit is needed for portability. */
  32. extern int glob_pattern_p (const char *__pattern, int __quote)
  33. /*@*/;
  34. /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
  35. #endif /* __LCLINT__ */
  36. #if !defined(__GLIBC__)
  37. /* Return nonzero if PATTERN contains any metacharacters.
  38. Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
  39. static int
  40. glob_pattern_p (const char * pattern, int quote)
  41. /*@*/
  42. {
  43. const char * p;
  44. int open = 0;
  45. for (p = pattern; *p != '\0'; ++p)
  46. switch (*p) {
  47. case '?':
  48. case '*':
  49. return 1;
  50. /*@notreached@*/ /*@switchbreak@*/ break;
  51. case '\\':
  52. if (quote && p[1] != '\0')
  53. ++p;
  54. /*@switchbreak@*/ break;
  55. case '[':
  56. open = 1;
  57. /*@switchbreak@*/ break;
  58. case ']':
  59. if (open)
  60. return 1;
  61. /*@switchbreak@*/ break;
  62. }
  63. return 0;
  64. }
  65. #endif /* !defined(__GLIBC__) */
  66. /*@unchecked@*/
  67. static int poptGlobFlags = 0;
  68. static int poptGlob_error(/*@unused@*/ UNUSED(const char * epath),
  69. /*@unused@*/ UNUSED(int eerrno))
  70. /*@*/
  71. {
  72. return 1;
  73. }
  74. #endif /* HAVE_GLOB_H */
  75. /**
  76. * Return path(s) from a glob pattern.
  77. * @param con context
  78. * @param pattern glob pattern
  79. * @retval *acp no. of paths
  80. * @retval *avp array of paths
  81. * @return 0 on success
  82. */
  83. static int poptGlob(/*@unused@*/ UNUSED(poptContext con), const char * pattern,
  84. /*@out@*/ int * acp, /*@out@*/ const char *** avp)
  85. /*@modifies *acp, *avp @*/
  86. {
  87. const char * pat = pattern;
  88. int rc = 0; /* assume success */
  89. /* XXX skip the attention marker. */
  90. if (pat[0] == '@' && pat[1] != '(')
  91. pat++;
  92. #if defined(HAVE_GLOB_H)
  93. if (glob_pattern_p(pat, 0)) {
  94. glob_t _g, *pglob = &_g;
  95. if (!glob(pat, poptGlobFlags, poptGlob_error, pglob)) {
  96. if (acp) {
  97. *acp = (int) pglob->gl_pathc;
  98. pglob->gl_pathc = 0;
  99. }
  100. if (avp) {
  101. /*@-onlytrans@*/
  102. *avp = (const char **) pglob->gl_pathv;
  103. /*@=onlytrans@*/
  104. pglob->gl_pathv = NULL;
  105. }
  106. /*@-nullstate@*/
  107. globfree(pglob);
  108. /*@=nullstate@*/
  109. } else
  110. rc = POPT_ERROR_ERRNO;
  111. } else
  112. #endif /* HAVE_GLOB_H */
  113. {
  114. if (acp)
  115. *acp = 1;
  116. if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
  117. (*avp)[0] = xstrdup(pat);
  118. }
  119. return rc;
  120. }
  121. /*@access poptContext @*/
  122. int poptSaneFile(const char * fn)
  123. {
  124. struct stat sb;
  125. uid_t uid = getuid();
  126. if (stat(fn, &sb) == -1)
  127. return 1;
  128. if ((uid_t)sb.st_uid != uid)
  129. return 0;
  130. if (!S_ISREG(sb.st_mode))
  131. return 0;
  132. /*@-bitwisesigned@*/
  133. if (sb.st_mode & (S_IWGRP|S_IWOTH))
  134. return 0;
  135. /*@=bitwisesigned@*/
  136. return 1;
  137. }
  138. int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
  139. {
  140. int fdno;
  141. char * b = NULL;
  142. off_t nb = 0;
  143. char * s, * t, * se;
  144. int rc = POPT_ERROR_ERRNO; /* assume failure */
  145. fdno = open(fn, O_RDONLY);
  146. if (fdno < 0)
  147. goto exit;
  148. if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
  149. || lseek(fdno, 0, SEEK_SET) == (off_t)-1
  150. || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
  151. || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
  152. {
  153. int oerrno = errno;
  154. (void) close(fdno);
  155. errno = oerrno;
  156. goto exit;
  157. }
  158. if (close(fdno) == -1)
  159. goto exit;
  160. if (b == NULL) {
  161. rc = POPT_ERROR_MALLOC;
  162. goto exit;
  163. }
  164. rc = 0;
  165. /* Trim out escaped newlines. */
  166. /*@-bitwisesigned@*/
  167. if (flags & POPT_READFILE_TRIMNEWLINES)
  168. /*@=bitwisesigned@*/
  169. {
  170. for (t = b, s = b, se = b + nb; *s && s < se; s++) {
  171. switch (*s) {
  172. case '\\':
  173. if (s[1] == '\n') {
  174. s++;
  175. continue;
  176. }
  177. /*@fallthrough@*/
  178. default:
  179. *t++ = *s;
  180. /*@switchbreak@*/ break;
  181. }
  182. }
  183. *t++ = '\0';
  184. nb = (off_t)(t - b);
  185. }
  186. exit:
  187. if (rc != 0) {
  188. /*@-usedef@*/
  189. if (b)
  190. free(b);
  191. /*@=usedef@*/
  192. b = NULL;
  193. nb = 0;
  194. }
  195. if (bp)
  196. *bp = b;
  197. /*@-usereleased@*/
  198. else if (b)
  199. free(b);
  200. /*@=usereleased@*/
  201. if (nbp)
  202. *nbp = (size_t)nb;
  203. /*@-compdef -nullstate @*/ /* XXX cannot annotate char ** correctly */
  204. return rc;
  205. /*@=compdef =nullstate @*/
  206. }
  207. /**
  208. * Check for application match.
  209. * @param con context
  210. * @param s config application name
  211. * return 0 if config application matches
  212. */
  213. static int configAppMatch(poptContext con, const char * s)
  214. /*@*/
  215. {
  216. int rc = 1;
  217. if (con->appName == NULL) /* XXX can't happen. */
  218. return rc;
  219. #if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
  220. if (glob_pattern_p(s, 1)) {
  221. /*@-bitwisesigned@*/
  222. static int flags = FNM_PATHNAME | FNM_PERIOD;
  223. #ifdef FNM_EXTMATCH
  224. flags |= FNM_EXTMATCH;
  225. #endif
  226. /*@=bitwisesigned@*/
  227. rc = fnmatch(s, con->appName, flags);
  228. } else
  229. #endif
  230. rc = strcmp(s, con->appName);
  231. return rc;
  232. }
  233. /*@-compmempass@*/ /* FIX: item->option.longName kept, not dependent. */
  234. static int poptConfigLine(poptContext con, char * line)
  235. /*@globals fileSystem, internalState @*/
  236. /*@modifies con, fileSystem, internalState @*/
  237. {
  238. char *b = NULL;
  239. size_t nb = 0;
  240. char * se = line;
  241. const char * appName;
  242. const char * entryType;
  243. const char * opt;
  244. struct poptItem_s item_buf;
  245. poptItem item = &item_buf;
  246. int i, j;
  247. int rc = POPT_ERROR_BADCONFIG;
  248. if (con->appName == NULL)
  249. goto exit;
  250. memset(item, 0, sizeof(*item));
  251. appName = se;
  252. while (*se != '\0' && !_isspaceptr(se)) se++;
  253. if (*se == '\0')
  254. goto exit;
  255. else
  256. *se++ = '\0';
  257. if (configAppMatch(con, appName)) goto exit;
  258. while (*se != '\0' && _isspaceptr(se)) se++;
  259. entryType = se;
  260. while (*se != '\0' && !_isspaceptr(se)) se++;
  261. if (*se != '\0') *se++ = '\0';
  262. while (*se != '\0' && _isspaceptr(se)) se++;
  263. if (*se == '\0') goto exit;
  264. opt = se;
  265. while (*se != '\0' && !_isspaceptr(se)) se++;
  266. if (opt[0] == '-' && *se == '\0') goto exit;
  267. if (*se != '\0') *se++ = '\0';
  268. while (*se != '\0' && _isspaceptr(se)) se++;
  269. if (opt[0] == '-' && *se == '\0') goto exit;
  270. /*@-temptrans@*/ /* FIX: line alias is saved */
  271. if (opt[0] == '-' && opt[1] == '-')
  272. item->option.longName = opt + 2;
  273. else if (opt[0] == '-' && opt[2] == '\0')
  274. item->option.shortName = opt[1];
  275. else {
  276. const char * fn = opt;
  277. /* XXX handle globs and directories in fn? */
  278. if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
  279. goto exit;
  280. if (b == NULL || nb == 0)
  281. goto exit;
  282. /* Append remaining text to the interpolated file option text. */
  283. if (*se != '\0') {
  284. size_t nse = strlen(se) + 1;
  285. if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
  286. goto exit;
  287. (void) stpcpy( stpcpy(&b[nb-1], " "), se);
  288. nb += nse;
  289. }
  290. se = b;
  291. /* Use the basename of the path as the long option name. */
  292. { const char * longName = strrchr(fn, '/');
  293. if (longName != NULL)
  294. longName++;
  295. else
  296. longName = fn;
  297. if (longName == NULL) /* XXX can't happen. */
  298. goto exit;
  299. /* Single character basenames are treated as short options. */
  300. if (longName[1] != '\0')
  301. item->option.longName = longName;
  302. else
  303. item->option.shortName = longName[0];
  304. }
  305. }
  306. /*@=temptrans@*/
  307. if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
  308. /*@-modobserver@*/
  309. item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
  310. for (i = 0, j = 0; i < item->argc; i++, j++) {
  311. const char * f;
  312. if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
  313. f = item->argv[i] + sizeof("--POPTdesc=");
  314. if (f[0] == '$' && f[1] == '"') f++;
  315. item->option.descrip = f;
  316. item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
  317. j--;
  318. } else
  319. if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
  320. f = item->argv[i] + sizeof("--POPTargs=");
  321. if (f[0] == '$' && f[1] == '"') f++;
  322. item->option.argDescrip = f;
  323. item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
  324. item->option.argInfo |= POPT_ARG_STRING;
  325. j--;
  326. } else
  327. if (j != i)
  328. item->argv[j] = item->argv[i];
  329. }
  330. if (j != i) {
  331. item->argv[j] = NULL;
  332. item->argc = j;
  333. }
  334. /*@=modobserver@*/
  335. /*@-nullstate@*/ /* FIX: item->argv[] may be NULL */
  336. if (!strcmp(entryType, "alias"))
  337. rc = poptAddItem(con, item, 0);
  338. else if (!strcmp(entryType, "exec"))
  339. rc = poptAddItem(con, item, 1);
  340. /*@=nullstate@*/
  341. exit:
  342. rc = 0; /* XXX for now, always return success */
  343. if (b)
  344. free(b);
  345. return rc;
  346. }
  347. /*@=compmempass@*/
  348. int poptReadConfigFile(poptContext con, const char * fn)
  349. {
  350. char * b = NULL, *be;
  351. size_t nb = 0;
  352. const char *se;
  353. char *t, *te;
  354. int rc;
  355. int xx;
  356. if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
  357. return (errno == ENOENT ? 0 : rc);
  358. if (b == NULL || nb == 0)
  359. return POPT_ERROR_BADCONFIG;
  360. if ((t = malloc(nb + 1)) == NULL)
  361. goto exit;
  362. te = t;
  363. be = (b + nb);
  364. for (se = b; se < be; se++) {
  365. switch (*se) {
  366. case '\n':
  367. *te = '\0';
  368. te = t;
  369. while (*te && _isspaceptr(te)) te++;
  370. if (*te && *te != '#')
  371. xx = poptConfigLine(con, te);
  372. /*@switchbreak@*/ break;
  373. /*@-usedef@*/ /* XXX *se may be uninitialized */
  374. case '\\':
  375. *te = *se++;
  376. /* \ at the end of a line does not insert a \n */
  377. if (se < be && *se != '\n') {
  378. te++;
  379. *te++ = *se;
  380. }
  381. /*@switchbreak@*/ break;
  382. default:
  383. *te++ = *se;
  384. /*@switchbreak@*/ break;
  385. /*@=usedef@*/
  386. }
  387. }
  388. free(t);
  389. rc = 0;
  390. exit:
  391. if (b)
  392. free(b);
  393. return rc;
  394. }
  395. int poptReadConfigFiles(poptContext con, const char * paths)
  396. {
  397. char * buf = (paths ? xstrdup(paths) : NULL);
  398. const char * p;
  399. char * pe;
  400. int rc = 0; /* assume success */
  401. for (p = buf; p != NULL && *p != '\0'; p = pe) {
  402. const char ** av = NULL;
  403. int ac = 0;
  404. int i;
  405. int xx;
  406. /* locate start of next path element */
  407. pe = strchr(p, ':');
  408. if (pe != NULL && *pe == ':')
  409. *pe++ = '\0';
  410. else
  411. pe = (char *) (p + strlen(p));
  412. xx = poptGlob(con, p, &ac, &av);
  413. /* work-off each resulting file from the path element */
  414. for (i = 0; i < ac; i++) {
  415. const char * fn = av[i];
  416. if (av[i] == NULL) /* XXX can't happen */
  417. /*@innercontinue@*/ continue;
  418. /* XXX should '@' attention be pushed into poptReadConfigFile? */
  419. if (p[0] == '@' && p[1] != '(') {
  420. if (fn[0] == '@' && fn[1] != '(')
  421. fn++;
  422. xx = poptSaneFile(fn);
  423. if (!xx && rc == 0)
  424. rc = POPT_ERROR_BADCONFIG;
  425. /*@innercontinue@*/ continue;
  426. }
  427. xx = poptReadConfigFile(con, fn);
  428. if (xx && rc == 0)
  429. rc = xx;
  430. free((void *)av[i]);
  431. av[i] = NULL;
  432. }
  433. free(av);
  434. av = NULL;
  435. }
  436. /*@-usedef@*/
  437. if (buf)
  438. free(buf);
  439. /*@=usedef@*/
  440. return rc;
  441. }
  442. int poptReadDefaultConfig(poptContext con, /*@unused@*/ UNUSED(int useEnv))
  443. {
  444. static const char _popt_sysconfdir[] = POPT_SYSCONFDIR "/popt";
  445. static const char _popt_etc[] = "/etc/popt";
  446. char * home;
  447. struct stat sb;
  448. int rc = 0; /* assume success */
  449. if (con->appName == NULL) goto exit;
  450. if (strcmp(_popt_sysconfdir, _popt_etc)) {
  451. rc = poptReadConfigFile(con, _popt_sysconfdir);
  452. if (rc) goto exit;
  453. }
  454. rc = poptReadConfigFile(con, _popt_etc);
  455. if (rc) goto exit;
  456. #if defined(HAVE_GLOB_H)
  457. if (!stat("/etc/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
  458. const char ** av = NULL;
  459. int ac = 0;
  460. int i;
  461. if ((rc = poptGlob(con, "/etc/popt.d/*", &ac, &av)) == 0) {
  462. for (i = 0; rc == 0 && i < ac; i++) {
  463. const char * fn = av[i];
  464. if (fn == NULL || strstr(fn, ".rpmnew") || strstr(fn, ".rpmsave"))
  465. continue;
  466. if (!stat(fn, &sb)) {
  467. if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))
  468. continue;
  469. }
  470. rc = poptReadConfigFile(con, fn);
  471. free((void *)av[i]);
  472. av[i] = NULL;
  473. }
  474. free(av);
  475. av = NULL;
  476. }
  477. }
  478. if (rc) goto exit;
  479. #endif
  480. if ((home = getenv("HOME"))) {
  481. char * fn = malloc(strlen(home) + 20);
  482. if (fn != NULL) {
  483. (void) stpcpy(stpcpy(fn, home), "/.popt");
  484. rc = poptReadConfigFile(con, fn);
  485. free(fn);
  486. } else
  487. rc = POPT_ERROR_ERRNO;
  488. if (rc) goto exit;
  489. }
  490. exit:
  491. return rc;
  492. }
  493. poptContext
  494. poptFini(poptContext con)
  495. {
  496. return poptFreeContext(con);
  497. }
  498. poptContext
  499. poptInit(int argc, const char ** argv,
  500. const struct poptOption * options, const char * configPaths)
  501. {
  502. poptContext con = NULL;
  503. const char * argv0;
  504. if (argv == NULL || argv[0] == NULL || options == NULL)
  505. return con;
  506. if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
  507. else argv0 = argv[0];
  508. con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
  509. if (con != NULL&& poptReadConfigFiles(con, configPaths))
  510. con = poptFini(con);
  511. return con;
  512. }