• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** \ingroup popt
2  * @file
3  */
4 
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(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
glob_pattern_p(const char * pattern,int quote)28 glob_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 
55 static int poptGlobFlags = 0;
56 
poptGlob_error(UNUSED (const char * epath),UNUSED (int eerrno))57 static int poptGlob_error(UNUSED(const char * epath),
58 		UNUSED(int eerrno))
59 {
60     return 1;
61 }
62 #endif	/* HAVE_GLOB_H */
63 
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
71  */
poptGlob(UNUSED (poptContext con),const char * pattern,int * acp,const char *** avp)72 static 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
97 	    rc = POPT_ERROR_ERRNO;
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 
poptSaneFile(const char * fn)111 int 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 
poptReadFile(const char * fn,char ** bp,size_t * nbp,int flags)126 int 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      || (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)
143     {
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;
151     }
152     if (close(fdno) == -1)
153 	goto exit;
154     if (b == NULL) {
155 	rc = POPT_ERROR_MALLOC;
156 	goto exit;
157     }
158     rc = 0;
159 
160    /* Trim out escaped newlines. */
161     if (flags & POPT_READFILE_TRIMNEWLINES)
162     {
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;
169 		}
170 		/* fallthrough */
171 	    default:
172 		*t++ = *s;
173 		break;
174 	    }
175 	}
176 	*t++ = '\0';
177 	nb = (off_t)(t - b);
178     }
179 
180 exit:
181     if (rc != 0) {
182 	if (b)
183 	    free(b);
184 	b = NULL;
185 	nb = 0;
186     }
187     if (bp)
188 	*bp = b;
189     else if (b)
190 	free(b);
191     if (nbp)
192 	*nbp = (size_t)nb;
193     return rc;
194 }
195 
196 /**
197  * Check for application match.
198  * @param con		context
199  * @param s		config application name
200  * return		0 if config application matches
201  */
configAppMatch(poptContext con,const char * s)202 static int configAppMatch(poptContext con, const char * s)
203 {
204     int rc = 1;
205 
206     if (con->appName == NULL)	/* XXX can't happen. */
207 	return rc;
208 
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;
220 }
221 
poptConfigLine(poptContext con,char * line)222 static int poptConfigLine(poptContext con, char * line)
223 {
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;
234 
235     if (con->appName == NULL)
236 	goto exit;
237 
238     memset(item, 0, sizeof(*item));
239 
240     appName = se;
241     while (*se != '\0' && !_isspaceptr(se)) se++;
242     if (*se == '\0')
243 	goto exit;
244     else
245 	*se++ = '\0';
246 
247     if (configAppMatch(con, appName)) goto exit;
248 
249     while (*se != '\0' && _isspaceptr(se)) se++;
250     entryType = se;
251     while (*se != '\0' && !_isspaceptr(se)) se++;
252     if (*se != '\0') *se++ = '\0';
253 
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';
260 
261     while (*se != '\0' && _isspaceptr(se)) se++;
262     if (opt[0] == '-' && *se == '\0') goto exit;
263 
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;
270 
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;
276 
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;
284 	}
285 	se = b;
286 
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];
300 	}
301     }
302 
303     if (poptParseArgvString(se, &item->argc, &item->argv)) goto exit;
304 
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];
325     }
326     if (j != i) {
327 	item->argv[j] = NULL;
328 	item->argc = j;
329     }
330 
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;
340 }
341 
poptReadConfigFile(poptContext con,const char * fn)342 int poptReadConfigFile(poptContext con, const char * fn)
343 {
344     char * b = NULL, *be;
345     size_t nb = 0;
346     const char *se;
347     char *t = NULL, *te;
348     int rc;
349 
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;
355     }
356 
357     if ((t = malloc(nb + 1)) == NULL)
358 	goto exit;
359     te = t;
360 
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;
378 	    }
379 	    break;
380 	  default:
381 	    *te++ = *se;
382 	    break;
383 	}
384     }
385     rc = 0;
386 
387 exit:
388     free(t);
389     if (b)
390 	free(b);
391     return rc;
392 }
393 
poptReadConfigFiles(poptContext con,const char * paths)394 int poptReadConfigFiles(poptContext con, const char * paths)
395 {
396     char * buf = (paths ? xstrdup(paths) : NULL);
397     const char * p;
398     char * pe;
399     int rc = 0;		/* assume success */
400 
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 
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));
413 
414 	xx = poptGlob(con, p, &ac, &av);
415 
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;
426 	}
427 	free(av);
428 	av = NULL;
429     }
430 
431     if (buf)
432 	free(buf);
433 
434     return rc;
435 }
436 
poptReadDefaultConfig(poptContext con,UNUSED (int useEnv))437 int poptReadDefaultConfig(poptContext con, UNUSED(int useEnv))
438 {
439     char * home;
440     struct stat sb;
441     int rc = 0;		/* assume success */
442 
443     if (con->appName == NULL) goto exit;
444 
445     rc = poptReadConfigFile(con, POPT_SYSCONFDIR "/popt");
446     if (rc) goto exit;
447 
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;
453 
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;
462 	    }
463 	    free(av);
464 	    av = NULL;
465 	}
466     }
467     if (rc) goto exit;
468 #endif
469 
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;
479     }
480 
481 exit:
482     return rc;
483 }
484 
485 poptContext
poptFini(poptContext con)486 poptFini(poptContext con)
487 {
488     return poptFreeContext(con);
489 }
490 
491 poptContext
poptInit(int argc,const char ** argv,const struct poptOption * options,const char * configPaths)492 poptInit(int argc, const char ** argv,
493 		const struct poptOption * options, const char * configPaths)
494 {
495     poptContext con = NULL;
496     const char * argv0;
497 
498     if (argv == NULL || argv[0] == NULL || options == NULL)
499 	return con;
500 
501     if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++;
502     else argv0 = argv[0];
503 
504     con = poptGetContext(argv0, argc, (const char **)argv, options, 0);
505     if (con != NULL&& poptReadConfigFiles(con, configPaths))
506 	con = poptFini(con);
507 
508     return con;
509 }
510