1/*-
2 * Copyright (c) 2011 Tim Kientzle
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "archive_platform.h"
27__FBSDID("$FreeBSD$");
28
29#ifdef HAVE_ERRNO_H
30#include <errno.h>
31#endif
32
33#include "archive_options_private.h"
34
35static const char *
36parse_option(const char **str,
37    const char **mod, const char **opt, const char **val);
38
39int
40_archive_set_option(struct archive *a,
41    const char *m, const char *o, const char *v,
42    int magic, const char *fn, option_handler use_option)
43{
44	const char *mp, *op, *vp;
45	int r;
46
47	archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
48
49	mp = (m != NULL && m[0] != '\0') ? m : NULL;
50	op = (o != NULL && o[0] != '\0') ? o : NULL;
51	vp = (v != NULL && v[0] != '\0') ? v : NULL;
52
53	if (op == NULL && vp == NULL)
54		return (ARCHIVE_OK);
55	if (op == NULL) {
56		archive_set_error(a, ARCHIVE_ERRNO_MISC, "Empty option");
57		return (ARCHIVE_FAILED);
58	}
59
60	r = use_option(a, mp, op, vp);
61	if (r == ARCHIVE_WARN - 1) {
62		archive_set_error(a, ARCHIVE_ERRNO_MISC,
63		    "Unknown module name: `%s'", mp);
64		return (ARCHIVE_FAILED);
65	}
66	if (r == ARCHIVE_WARN) {
67		archive_set_error(a, ARCHIVE_ERRNO_MISC,
68		    "Undefined option: `%s%s%s%s%s%s'",
69		    vp?"":"!", mp?mp:"", mp?":":"", op, vp?"=":"", vp?vp:"");
70		return (ARCHIVE_FAILED);
71	}
72	return (r);
73}
74
75int
76_archive_set_either_option(struct archive *a, const char *m, const char *o, const char *v,
77    option_handler use_format_option, option_handler use_filter_option)
78{
79	int r1, r2;
80
81	if (o == NULL && v == NULL)
82		return (ARCHIVE_OK);
83	if (o == NULL)
84		return (ARCHIVE_FAILED);
85
86	r1 = use_format_option(a, m, o, v);
87	if (r1 == ARCHIVE_FATAL)
88		return (ARCHIVE_FATAL);
89
90	r2 = use_filter_option(a, m, o, v);
91	if (r2 == ARCHIVE_FATAL)
92		return (ARCHIVE_FATAL);
93
94	if (r2 == ARCHIVE_WARN - 1)
95		return r1;
96	return r1 > r2 ? r1 : r2;
97}
98
99int
100_archive_set_options(struct archive *a, const char *options,
101    int magic, const char *fn, option_handler use_option)
102{
103	int allok = 1, anyok = 0, ignore_mod_err = 0, r;
104	char *data;
105	const char *s, *mod, *opt, *val;
106
107	archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
108
109	if (options == NULL || options[0] == '\0')
110		return ARCHIVE_OK;
111
112	if ((data = strdup(options)) == NULL) {
113		archive_set_error(a,
114		    ENOMEM, "Out of memory adding file to list");
115		return (ARCHIVE_FATAL);
116	}
117	s = (const char *)data;
118
119	do {
120		mod = opt = val = NULL;
121
122		parse_option(&s, &mod, &opt, &val);
123		if (mod == NULL && opt != NULL &&
124		    strcmp("__ignore_wrong_module_name__", opt) == 0) {
125			/* Ignore module name error */
126			if (val != NULL) {
127				ignore_mod_err = 1;
128				anyok = 1;
129			}
130			continue;
131		}
132
133		r = use_option(a, mod, opt, val);
134		if (r == ARCHIVE_FATAL) {
135			free(data);
136			return (ARCHIVE_FATAL);
137		}
138		if (r == ARCHIVE_FAILED && mod != NULL) {
139			free(data);
140			return (ARCHIVE_FAILED);
141		}
142		if (r == ARCHIVE_WARN - 1) {
143			if (ignore_mod_err)
144				continue;
145			/* The module name is wrong. */
146			archive_set_error(a, ARCHIVE_ERRNO_MISC,
147			    "Unknown module name: `%s'", mod);
148			free(data);
149			return (ARCHIVE_FAILED);
150		}
151		if (r == ARCHIVE_WARN) {
152			/* The option name is wrong. No-one used this. */
153			archive_set_error(a, ARCHIVE_ERRNO_MISC,
154			    "Undefined option: `%s%s%s'",
155			    mod?mod:"", mod?":":"", opt);
156			free(data);
157			return (ARCHIVE_FAILED);
158		}
159		if (r == ARCHIVE_OK)
160			anyok = 1;
161		else
162			allok = 0;
163	} while (s != NULL);
164
165	free(data);
166	return allok ? ARCHIVE_OK : anyok ? ARCHIVE_WARN : ARCHIVE_FAILED;
167}
168
169static const char *
170parse_option(const char **s, const char **m, const char **o, const char **v)
171{
172	const char *end, *mod, *opt, *val;
173	char *p;
174
175	end = NULL;
176	mod = NULL;
177	opt = *s;
178	val = "1";
179
180	p = strchr(opt, ',');
181
182	if (p != NULL) {
183		*p = '\0';
184		end = ((const char *)p) + 1;
185	}
186
187	if (0 == strlen(opt)) {
188		*s = end;
189		*m = NULL;
190		*o = NULL;
191		*v = NULL;
192		return end;
193	}
194
195	p = strchr(opt, ':');
196	if (p != NULL) {
197		*p = '\0';
198		mod = opt;
199		opt = ++p;
200	}
201
202	p = strchr(opt, '=');
203	if (p != NULL) {
204		*p = '\0';
205		val = ++p;
206	} else if (opt[0] == '!') {
207		++opt;
208		val = NULL;
209	}
210
211	*s = end;
212	*m = mod;
213	*o = opt;
214	*v = val;
215
216	return end;
217}
218
219