1/*-
2 * Copyright (c) 2009 Michihiro NAKAJIMA
3 * Copyright (c) 2003-2007 Tim Kientzle
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "archive_platform.h"
28__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
29
30#ifdef HAVE_SYS_TYPES_H
31#include <sys/types.h>
32#endif
33#ifdef HAVE_STDLIB_H
34#include <stdlib.h>
35#endif
36#ifdef HAVE_STRING_H
37#include <string.h>
38#endif
39
40#include "archive.h"
41#include "archive_private.h"
42#include "archive_string.h"
43
44#if ARCHIVE_VERSION_NUMBER < 3000000
45/* These disappear in libarchive 3.0 */
46/* Deprecated. */
47int
48archive_api_feature(void)
49{
50	return (ARCHIVE_API_FEATURE);
51}
52
53/* Deprecated. */
54int
55archive_api_version(void)
56{
57	return (ARCHIVE_API_VERSION);
58}
59
60/* Deprecated synonym for archive_version_number() */
61int
62archive_version_stamp(void)
63{
64	return (archive_version_number());
65}
66
67/* Deprecated synonym for archive_version_string() */
68const char *
69archive_version(void)
70{
71	return (archive_version_string());
72}
73#endif
74
75int
76archive_version_number(void)
77{
78	return (ARCHIVE_VERSION_NUMBER);
79}
80
81const char *
82archive_version_string(void)
83{
84	return (ARCHIVE_VERSION_STRING);
85}
86
87int
88archive_errno(struct archive *a)
89{
90	return (a->archive_error_number);
91}
92
93const char *
94archive_error_string(struct archive *a)
95{
96
97	if (a->error != NULL  &&  *a->error != '\0')
98		return (a->error);
99	else
100		return ("(Empty error message)");
101}
102
103int
104archive_file_count(struct archive *a)
105{
106	return (a->file_count);
107}
108
109int
110archive_format(struct archive *a)
111{
112	return (a->archive_format);
113}
114
115const char *
116archive_format_name(struct archive *a)
117{
118	return (a->archive_format_name);
119}
120
121
122int
123archive_compression(struct archive *a)
124{
125	return (a->compression_code);
126}
127
128const char *
129archive_compression_name(struct archive *a)
130{
131	return (a->compression_name);
132}
133
134
135/*
136 * Return a count of the number of compressed bytes processed.
137 */
138int64_t
139archive_position_compressed(struct archive *a)
140{
141	return (a->raw_position);
142}
143
144/*
145 * Return a count of the number of uncompressed bytes processed.
146 */
147int64_t
148archive_position_uncompressed(struct archive *a)
149{
150	return (a->file_position);
151}
152
153void
154archive_clear_error(struct archive *a)
155{
156	archive_string_empty(&a->error_string);
157	a->error = NULL;
158}
159
160void
161archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
162{
163	va_list ap;
164
165	a->archive_error_number = error_number;
166	if (fmt == NULL) {
167		a->error = NULL;
168		return;
169	}
170
171	va_start(ap, fmt);
172	archive_string_vsprintf(&(a->error_string), fmt, ap);
173	va_end(ap);
174	a->error = a->error_string.s;
175}
176
177void
178archive_copy_error(struct archive *dest, struct archive *src)
179{
180	dest->archive_error_number = src->archive_error_number;
181
182	archive_string_copy(&dest->error_string, &src->error_string);
183	dest->error = dest->error_string.s;
184}
185
186void
187__archive_errx(int retvalue, const char *msg)
188{
189	static const char *msg1 = "Fatal Internal Error in libarchive: ";
190	size_t s;
191
192	s = write(2, msg1, strlen(msg1));
193	(void)s; /* UNUSED */
194	s = write(2, msg, strlen(msg));
195	(void)s; /* UNUSED */
196	s = write(2, "\n", 1);
197	(void)s; /* UNUSED */
198	exit(retvalue);
199}
200
201/*
202 * Parse option strings
203 *  Detail of option format.
204 *    - The option can accept:
205 *     "opt-name", "!opt-name", "opt-name=value".
206 *
207 *    - The option entries are separated by comma.
208 *        e.g  "compression=9,opt=XXX,opt-b=ZZZ"
209 *
210 *    - The name of option string consist of '-' and alphabet
211 *      but character '-' cannot be used for the first character.
212 *      (Regular expression is [a-z][-a-z]+)
213 *
214 *    - For a specfic format/filter, using the format name with ':'.
215 *        e.g  "zip:compression=9"
216 *        (This "compression=9" option entry is for "zip" format only)
217 *
218 *      If another entries follow it, those are not for
219 *      the specfic format/filter.
220 *        e.g  handle "zip:compression=9,opt=XXX,opt-b=ZZZ"
221 *          "zip" format/filter handler will get "compression=9"
222 *          all format/filter handler will get "opt=XXX"
223 *          all format/filter handler will get "opt-b=ZZZ"
224 *
225 *    - Whitespace and tab are bypassed.
226 *
227 */
228int
229__archive_parse_options(const char *p, const char *fn, int keysize, char *key,
230    int valsize, char *val)
231{
232	const char *p_org;
233	int apply;
234	int kidx, vidx;
235	int negative;
236	enum {
237		/* Requested for initialization. */
238		INIT,
239		/* Finding format/filter-name and option-name. */
240		F_BOTH,
241		/* Finding option-name only.
242		 * (already detected format/filter-name) */
243		F_NAME,
244		/* Getting option-value. */
245		G_VALUE,
246	} state;
247
248	p_org = p;
249	state = INIT;
250	kidx = vidx = negative = 0;
251	apply = 1;
252	while (*p) {
253		switch (state) {
254		case INIT:
255			kidx = vidx = 0;
256			negative = 0;
257			apply = 1;
258			state = F_BOTH;
259			break;
260		case F_BOTH:
261		case F_NAME:
262			if ((*p >= 'a' && *p <= 'z') ||
263			    (*p >= '0' && *p <= '9') || *p == '-') {
264				if (kidx == 0 && !(*p >= 'a' && *p <= 'z'))
265					/* Illegal sequence. */
266					return (-1);
267				if (kidx >= keysize -1)
268					/* Too many characters. */
269					return (-1);
270				key[kidx++] = *p++;
271			} else if (*p == '!') {
272				if (kidx != 0)
273					/* Illegal sequence. */
274					return (-1);
275				negative = 1;
276				++p;
277			} else if (*p == ',') {
278				if (kidx == 0)
279					/* Illegal sequence. */
280					return (-1);
281				if (!negative)
282					val[vidx++] = '1';
283				/* We have got boolean option data. */
284				++p;
285				if (apply)
286					goto complete;
287				else
288					/* This option does not apply to the
289					 * format which the fn variable
290					 * indicate. */
291					state = INIT;
292			} else if (*p == ':') {
293				/* obuf data is format name */
294				if (state == F_NAME)
295					/* We already found it. */
296					return (-1);
297				if (kidx == 0)
298					/* Illegal sequence. */
299					return (-1);
300				if (negative)
301					/* We cannot accept "!format-name:". */
302					return (-1);
303				key[kidx] = '\0';
304				if (strcmp(fn, key) != 0)
305					/* This option does not apply to the
306					 * format which the fn variable
307					 * indicate. */
308					apply = 0;
309				kidx = 0;
310				++p;
311				state = F_NAME;
312			} else if (*p == '=') {
313				if (kidx == 0)
314					/* Illegal sequence. */
315					return (-1);
316				if (negative)
317					/* We cannot accept "!opt-name=value". */
318					return (-1);
319				++p;
320				state = G_VALUE;
321			} else if (*p == ' ') {
322				/* Pass the space character */
323				++p;
324			} else {
325				/* Illegal character. */
326				return (-1);
327			}
328			break;
329		case G_VALUE:
330			if (*p == ',') {
331				if (vidx == 0)
332					/* Illegal sequence. */
333					return (-1);
334				/* We have got option data. */
335				++p;
336				if (apply)
337					goto complete;
338				else
339					/* This option does not apply to the
340					 * format which the fn variable
341					 * indicate. */
342					state = INIT;
343			} else if (*p == ' ') {
344				/* Pass the space character */
345				++p;
346			} else {
347				if (vidx >= valsize -1)
348					/* Too many characters. */
349					return (-1);
350				val[vidx++] = *p++;
351			}
352			break;
353		}
354	}
355
356	switch (state) {
357	case F_BOTH:
358	case F_NAME:
359		if (kidx != 0) {
360			if (!negative)
361				val[vidx++] = '1';
362			/* We have got boolean option. */
363			if (apply)
364				/* This option apply to the format which the
365				 * fn variable indicate. */
366				goto complete;
367		}
368		break;
369	case G_VALUE:
370		if (vidx == 0)
371			/* Illegal sequence. */
372			return (-1);
373		/* We have got option value. */
374		if (apply)
375			/* This option apply to the format which the fn
376			 * variable indicate. */
377			goto complete;
378		break;
379	case INIT:/* nothing */
380		break;
381	}
382
383	/* End of Option string. */
384	return (0);
385
386complete:
387	key[kidx] = '\0';
388	val[vidx] = '\0';
389	/* Return a size which we've consumed for detecting option */
390	return ((int)(p - p_org));
391}
392