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