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