fix typo in manual page
[rsync.git] / popt / poptconfig.c
blobbf201e26fdb615b6b7b2e8718c07ac3494cf4e71
1 /** \ingroup popt
2 * @file
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>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <errno.h>
16 #if defined(HAVE_FNMATCH_H)
17 #include <fnmatch.h>
19 #endif
21 #if defined(HAVE_GLOB_H)
22 #include <glob.h>
24 #if !defined(HAVE_GLOB_PATTERN_P)
25 /* Return nonzero if PATTERN contains any metacharacters.
26 Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
27 static int
28 glob_pattern_p (const char * pattern, int quote)
30 const char * p;
31 int open = 0;
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;
51 return 0;
53 #endif /* !defined(__GLIBC__) */
55 static int poptGlobFlags = 0;
57 static int poptGlob_error(UNUSED(const char * epath),
58 UNUSED(int eerrno))
60 return 1;
62 #endif /* HAVE_GLOB_H */
64 /**
65 * Return path(s) from a glob pattern.
66 * @param con context
67 * @param pattern glob pattern
68 * @retval *acp no. of paths
69 * @retval *avp array of paths
70 * @return 0 on success
72 static int poptGlob(UNUSED(poptContext con), const char * pattern,
73 int * acp, const char *** avp)
75 const char * pat = pattern;
76 int rc = 0; /* assume success */
78 #if defined(HAVE_GLOB_H)
79 if (glob_pattern_p(pat, 0)) {
80 glob_t _g, *pglob = &_g;
82 if (!(rc = glob(pat, poptGlobFlags, poptGlob_error, pglob))) {
83 if (acp) {
84 *acp = (int) pglob->gl_pathc;
85 pglob->gl_pathc = 0;
87 if (avp) {
88 *avp = (const char **) pglob->gl_pathv;
89 pglob->gl_pathv = NULL;
91 globfree(pglob);
92 } else if (rc == GLOB_NOMATCH) {
93 *avp = NULL;
94 *acp = 0;
95 rc = 0;
96 } else
97 rc = POPT_ERROR_ERRNO;
98 } else
99 #endif /* HAVE_GLOB_H */
101 if (acp)
102 *acp = 1;
103 if (avp && (*avp = calloc((size_t)(1 + 1), sizeof (**avp))) != NULL)
104 (*avp)[0] = xstrdup(pat);
107 return rc;
111 int poptSaneFile(const char * fn)
113 struct stat sb;
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;
126 int poptReadFile(const char * fn, char ** bp, size_t * nbp, int flags)
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 */
134 fdno = open(fn, O_RDONLY);
135 if (fdno < 0)
136 goto exit;
138 if ((nb = lseek(fdno, 0, SEEK_END)) == (off_t)-1
139 || (uintmax_t)nb >= SIZE_MAX
140 || lseek(fdno, 0, SEEK_SET) == (off_t)-1
141 || (b = calloc(sizeof(*b), (size_t)nb + 1)) == NULL
142 || read(fdno, (char *)b, (size_t)nb) != (ssize_t)nb)
144 int oerrno = errno;
145 (void) close(fdno);
146 if (nb != (off_t)-1 && (uintmax_t)nb >= SIZE_MAX)
147 errno = -EOVERFLOW;
148 else
149 errno = oerrno;
150 goto exit;
152 if (close(fdno) == -1)
153 goto exit;
154 if (b == NULL) {
155 rc = POPT_ERROR_MALLOC;
156 goto exit;
158 rc = 0;
160 /* Trim out escaped newlines. */
161 if (flags & POPT_READFILE_TRIMNEWLINES)
163 for (t = b, s = b, se = b + nb; *s && s < se; s++) {
164 switch (*s) {
165 case '\\':
166 if (s[1] == '\n') {
167 s++;
168 continue;
170 /* fallthrough */
171 default:
172 *t++ = *s;
173 break;
176 *t++ = '\0';
177 nb = (off_t)(t - b);
180 exit:
181 if (rc != 0) {
182 if (b)
183 free(b);
184 b = NULL;
185 nb = 0;
187 if (bp)
188 *bp = b;
189 else if (b)
190 free(b);
191 if (nbp)
192 *nbp = (size_t)nb;
193 return rc;
197 * Check for application match.
198 * @param con context
199 * @param s config application name
200 * return 0 if config application matches
202 static int configAppMatch(poptContext con, const char * s)
204 int rc = 1;
206 if (con->appName == NULL) /* XXX can't happen. */
207 return rc;
209 #if defined(HAVE_GLOB_H) && defined(HAVE_FNMATCH_H)
210 if (glob_pattern_p(s, 1)) {
211 static int flags = FNM_PATHNAME | FNM_PERIOD;
212 #ifdef FNM_EXTMATCH
213 flags |= FNM_EXTMATCH;
214 #endif
215 rc = fnmatch(s, con->appName, flags);
216 } else
217 #endif
218 rc = strcmp(s, con->appName);
219 return rc;
222 static int poptConfigLine(poptContext con, char * line)
224 char *b = NULL;
225 size_t nb = 0;
226 char * se = line;
227 const char * appName;
228 const char * entryType;
229 const char * opt;
230 struct poptItem_s item_buf;
231 poptItem item = &item_buf;
232 int i, j;
233 int rc = POPT_ERROR_BADCONFIG;
235 if (con->appName == NULL)
236 goto exit;
238 memset(item, 0, sizeof(*item));
240 appName = se;
241 while (*se != '\0' && !_isspaceptr(se)) se++;
242 if (*se == '\0')
243 goto exit;
244 else
245 *se++ = '\0';
247 if (configAppMatch(con, appName)) goto exit;
249 while (*se != '\0' && _isspaceptr(se)) se++;
250 entryType = se;
251 while (*se != '\0' && !_isspaceptr(se)) se++;
252 if (*se != '\0') *se++ = '\0';
254 while (*se != '\0' && _isspaceptr(se)) se++;
255 if (*se == '\0') goto exit;
256 opt = se;
257 while (*se != '\0' && !_isspaceptr(se)) se++;
258 if (opt[0] == '-' && *se == '\0') goto exit;
259 if (*se != '\0') *se++ = '\0';
261 while (*se != '\0' && _isspaceptr(se)) se++;
262 if (opt[0] == '-' && *se == '\0') goto exit;
264 if (opt[0] == '-' && opt[1] == '-')
265 item->option.longName = opt + 2;
266 else if (opt[0] == '-' && opt[2] == '\0')
267 item->option.shortName = opt[1];
268 else {
269 const char * fn = opt;
271 /* XXX handle globs and directories in fn? */
272 if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
273 goto exit;
274 if (b == NULL || nb == 0)
275 goto exit;
277 /* Append remaining text to the interpolated file option text. */
278 if (*se != '\0') {
279 size_t nse = strlen(se) + 1;
280 if ((b = realloc(b, (nb + nse))) == NULL) /* XXX can't happen */
281 goto exit;
282 (void) stpcpy( stpcpy(&b[nb-1], " "), se);
283 nb += nse;
285 se = b;
287 /* Use the basename of the path as the long option name. */
288 { const char * longName = strrchr(fn, '/');
289 if (longName != NULL)
290 longName++;
291 else
292 longName = fn;
293 if (longName == NULL) /* XXX can't happen. */
294 goto exit;
295 /* Single character basenames are treated as short options. */
296 if (longName[1] != '\0')
297 item->option.longName = longName;
298 else
299 item->option.shortName = longName[0];
303 if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
305 item->option.argInfo = POPT_ARGFLAG_DOC_HIDDEN;
306 for (i = 0, j = 0; i < item->argc; i++, j++) {
307 const char * f;
308 if (!strncmp(item->argv[i], "--POPTdesc=", sizeof("--POPTdesc=")-1)) {
309 f = item->argv[i] + sizeof("--POPTdesc=");
310 if (f[0] == '$' && f[1] == '"') f++;
311 item->option.descrip = f;
312 item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
313 j--;
314 } else
315 if (!strncmp(item->argv[i], "--POPTargs=", sizeof("--POPTargs=")-1)) {
316 f = item->argv[i] + sizeof("--POPTargs=");
317 if (f[0] == '$' && f[1] == '"') f++;
318 item->option.argDescrip = f;
319 item->option.argInfo &= ~POPT_ARGFLAG_DOC_HIDDEN;
320 item->option.argInfo |= POPT_ARG_STRING;
321 j--;
322 } else
323 if (j != i)
324 item->argv[j] = item->argv[i];
326 if (j != i) {
327 item->argv[j] = NULL;
328 item->argc = j;
331 if (!strcmp(entryType, "alias"))
332 rc = poptAddItem(con, item, 0);
333 else if (!strcmp(entryType, "exec"))
334 rc = poptAddItem(con, item, 1);
335 exit:
336 rc = 0; /* XXX for now, always return success */
337 if (b)
338 free(b);
339 return rc;
342 int poptReadConfigFile(poptContext con, const char * fn)
344 char * b = NULL, *be;
345 size_t nb = 0;
346 const char *se;
347 char *t = NULL, *te;
348 int rc;
350 if ((rc = poptReadFile(fn, &b, &nb, POPT_READFILE_TRIMNEWLINES)) != 0)
351 return (errno == ENOENT ? 0 : rc);
352 if (b == NULL || nb == 0) {
353 rc = POPT_ERROR_BADCONFIG;
354 goto exit;
357 if ((t = malloc(nb + 1)) == NULL)
358 goto exit;
359 te = t;
361 be = (b + nb);
362 for (se = b; se < be; se++) {
363 switch (*se) {
364 case '\n':
365 *te = '\0';
366 te = t;
367 while (*te && _isspaceptr(te)) te++;
368 if (*te && *te != '#')
369 if ((rc = poptConfigLine(con, te)) != 0)
370 goto exit;
371 break;
372 case '\\':
373 *te = *se++;
374 /* \ at the end of a line does not insert a \n */
375 if (se < be && *se != '\n') {
376 te++;
377 *te++ = *se;
379 break;
380 default:
381 *te++ = *se;
382 break;
385 rc = 0;
387 exit:
388 free(t);
389 if (b)
390 free(b);
391 return rc;
394 int poptReadConfigFiles(poptContext con, const char * paths)
396 char * buf = (paths ? xstrdup(paths) : NULL);
397 const char * p;
398 char * pe;
399 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;
407 /* locate start of next path element */
408 pe = strchr(p, ':');
409 if (pe != NULL && *pe == ':')
410 *pe++ = '\0';
411 else
412 pe = (char *) (p + strlen(p));
414 xx = poptGlob(con, p, &ac, &av);
416 /* work-off each resulting file from the path element */
417 for (i = 0; i < ac; i++) {
418 const char * fn = av[i];
419 if (!poptSaneFile(fn))
420 continue;
421 xx = poptReadConfigFile(con, fn);
422 if (xx && rc == 0)
423 rc = xx;
424 free((void *)av[i]);
425 av[i] = NULL;
427 free(av);
428 av = NULL;
431 if (buf)
432 free(buf);
434 return rc;
437 int poptReadDefaultConfig(poptContext con, UNUSED(int useEnv))
439 char * home;
440 struct stat sb;
441 int rc = 0; /* assume success */
443 if (con->appName == NULL) goto exit;
445 rc = poptReadConfigFile(con, POPT_SYSCONFDIR "/popt");
446 if (rc) goto exit;
448 #if defined(HAVE_GLOB_H)
449 if (!stat(POPT_SYSCONFDIR "/popt.d", &sb) && S_ISDIR(sb.st_mode)) {
450 const char ** av = NULL;
451 int ac = 0;
452 int i;
454 if ((rc = poptGlob(con, POPT_SYSCONFDIR "/popt.d/*", &ac, &av)) == 0) {
455 for (i = 0; rc == 0 && i < ac; i++) {
456 const char * fn = av[i];
457 if (!poptSaneFile(fn))
458 continue;
459 rc = poptReadConfigFile(con, fn);
460 free((void *)av[i]);
461 av[i] = NULL;
463 free(av);
464 av = NULL;
467 if (rc) goto exit;
468 #endif
470 if ((home = getenv("HOME"))) {
471 char * fn = malloc(strlen(home) + 20);
472 if (fn != NULL) {
473 (void) stpcpy(stpcpy(fn, home), "/.popt");
474 rc = poptReadConfigFile(con, fn);
475 free(fn);
476 } else
477 rc = POPT_ERROR_ERRNO;
478 if (rc) goto exit;
481 exit:
482 return rc;
485 poptContext
486 poptFini(poptContext con)
488 return poptFreeContext(con);
491 poptContext
492 poptInit(int argc, const char ** argv,
493 const struct poptOption * options, const char * configPaths)
495 poptContext con = NULL;
496 const char * argv0;
498 if (argv == NULL || argv[0] == NULL || options == NULL)
499 return con;
501 if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
502 else argv0 = argv[0];
504 con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
505 if (con != NULL&& poptReadConfigFiles(con, configPaths))
506 con = poptFini(con);
508 return con;