2 * \file popt/poptconfig.c
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. */
13 #if defined(HAVE_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
)
20 /*@=declundef =exportheader =incondefs =protoparammatch =redecl =type @*/
21 #endif /* __LCLINT__ */
24 #if defined(HAVE_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
)
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. */
49 glob_pattern_p (const char * pattern
, int quote
)
55 for (p
= pattern
; *p
!= '\0'; ++p
)
60 /*@notreached@*/ /*@switchbreak@*/ break;
62 if (quote
&& p
[1] != '\0')
64 /*@switchbreak@*/ break;
67 /*@switchbreak@*/ break;
71 /*@switchbreak@*/ break;
75 #endif /* !defined(__GLIBC__) */
78 static int poptGlobFlags
= 0;
80 static int poptGlob_error(/*@unused@*/ UNUSED(const char * epath
),
81 /*@unused@*/ UNUSED(int eerrno
))
86 #endif /* HAVE_GLOB_H */
89 * Return path(s) from a glob pattern.
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] != '(')
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
)) {
113 *acp
= (int) pglob
->gl_pathc
;
118 *avp
= (const char **) pglob
->gl_pathv
;
120 pglob
->gl_pathv
= NULL
;
126 rc
= POPT_ERROR_ERRNO
;
128 #endif /* HAVE_GLOB_H */
132 if (avp
&& (*avp
= calloc((size_t)(1 + 1), sizeof (**avp
))) != NULL
)
133 (*avp
)[0] = xstrdup(pat
);
139 /*@access poptContext @*/
141 int poptSaneFile(const char * fn
)
144 uid_t uid
= getuid();
146 if (stat(fn
, &sb
) == -1)
148 if ((uid_t
)sb
.st_uid
!= uid
)
150 if (!S_ISREG(sb
.st_mode
))
153 if (sb
.st_mode
& (S_IWGRP
|S_IWOTH
))
159 int poptReadFile(const char * fn
, char ** bp
, size_t * nbp
, int flags
)
165 int rc
= POPT_ERROR_ERRNO
; /* assume failure */
167 fdno
= open(fn
, O_RDONLY
);
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
)
181 if (close(fdno
) == -1)
184 rc
= POPT_ERROR_MALLOC
;
189 /* Trim out escaped newlines. */
191 if (flags
& POPT_READFILE_TRIMNEWLINES
)
194 for (t
= b
, s
= b
, se
= b
+ nb
; *s
&& s
< se
; s
++) {
204 /*@switchbreak@*/ break;
228 /*@-compdef -nullstate @*/ /* XXX cannot annotate char ** correctly */
230 /*@=compdef =nullstate @*/
234 * Check for application match.
236 * @param s config application name
237 * return 0 if config application matches
239 static int configAppMatch(poptContext con
, const char * s
)
244 if (con
->appName
== NULL
) /* XXX can't happen. */
247 #if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
248 if (glob_pattern_p(s
, 1)) {
250 static int flags
= FNM_PATHNAME
| FNM_PERIOD
;
252 flags
|= FNM_EXTMATCH
;
255 rc
= fnmatch(s
, con
->appName
, flags
);
258 rc
= strcmp(s
, con
->appName
);
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 @*/
270 const char * appName
;
271 const char * entryType
;
273 struct poptItem_s item_buf
;
274 poptItem item
= &item_buf
;
276 int rc
= POPT_ERROR_BADCONFIG
;
278 if (con
->appName
== NULL
)
281 memset(item
, 0, sizeof(*item
));
284 while (*se
!= '\0' && !_isspaceptr(se
)) se
++;
290 if (configAppMatch(con
, appName
)) goto exit
;
292 while (*se
!= '\0' && _isspaceptr(se
)) 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
;
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];
313 const char * fn
= opt
;
315 /* XXX handle globs and directories in fn? */
316 if ((rc
= poptReadFile(fn
, &b
, &nb
, POPT_READFILE_TRIMNEWLINES
)) != 0)
318 if (b
== NULL
|| nb
== 0)
321 /* Append remaining text to the interpolated file option text. */
323 size_t nse
= strlen(se
) + 1;
324 if ((b
= realloc(b
, (nb
+ nse
))) == NULL
) /* XXX can't happen */
326 (void) stpcpy( stpcpy(&b
[nb
-1], " "), se
);
331 /* Use the basename of the path as the long option name. */
332 { const char * longName
= strrchr(fn
, '/');
333 if (longName
!= NULL
)
337 if (longName
== NULL
) /* XXX can't happen. */
339 /* Single character basenames are treated as short options. */
340 if (longName
[1] != '\0')
341 item
->option
.longName
= longName
;
343 item
->option
.shortName
= longName
[0];
348 if (poptParseArgvString(se
, &item
->argc
, &item
->argv
)) goto exit
;
351 item
->option
.argInfo
= POPT_ARGFLAG_DOC_HIDDEN
;
352 for (i
= 0, j
= 0; i
< item
->argc
; i
++, j
++) {
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
;
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
;
370 item
->argv
[j
] = item
->argv
[i
];
373 item
->argv
[j
] = NULL
;
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);
385 rc
= 0; /* XXX for now, always return success */
392 int poptReadConfigFile(poptContext con
, const char * fn
)
394 char * b
= NULL
, *be
;
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
)
411 for (se
= b
; se
< be
; se
++) {
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 */
423 /* \ at the end of a line does not insert a \n */
424 if (se
< be
&& *se
!= '\n') {
428 /*@switchbreak@*/ break;
431 /*@switchbreak@*/ break;
445 int poptReadConfigFiles(poptContext con
, const char * paths
)
447 char * buf
= (paths
? xstrdup(paths
) : NULL
);
450 int rc
= 0; /* assume success */
452 for (p
= buf
; p
!= NULL
&& *p
!= '\0'; p
= pe
) {
453 const char ** av
= NULL
;
458 /* locate start of next path element */
460 if (pe
!= NULL
&& *pe
== ':')
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] != '(')
476 xx
= poptSaneFile(fn
);
478 rc
= POPT_ERROR_BADCONFIG
;
479 /*@innercontinue@*/ continue;
481 xx
= poptReadConfigFile(con
, fn
);
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";
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
);
514 rc
= poptReadConfigFile(con
, _popt_etc
);
517 #if defined(HAVE_GLOB_H)
518 if (!stat("/etc/popt.d", &sb
) && S_ISDIR(sb
.st_mode
)) {
519 const char ** av
= NULL
;
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"))
528 if (!stat(fn
, &sb
)) {
529 if (!S_ISREG(sb
.st_mode
) && !S_ISLNK(sb
.st_mode
))
532 rc
= poptReadConfigFile(con
, fn
);
543 if ((home
= getenv("HOME"))) {
544 char * fn
= malloc(strlen(home
) + 20);
546 (void) stpcpy(stpcpy(fn
, home
), "/.popt");
547 rc
= poptReadConfigFile(con
, fn
);
550 rc
= POPT_ERROR_ERRNO
;
559 poptFini(poptContext con
)
561 return poptFreeContext(con
);
565 poptInit(int argc
, const char ** argv
,
566 const struct poptOption
* options
, const char * configPaths
)
568 poptContext con
= NULL
;
571 if (argv
== NULL
|| argv
[0] == NULL
|| options
== NULL
)
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
))