1248590Smm/*-
2248590Smm * Copyright (c) 2012 Michihiro NAKAJIMA
3248590Smm * All rights reserved.
4248590Smm *
5248590Smm * Redistribution and use in source and binary forms, with or without
6248590Smm * modification, are permitted provided that the following conditions
7248590Smm * are met:
8248590Smm * 1. Redistributions of source code must retain the above copyright
9248590Smm *    notice, this list of conditions and the following disclaimer.
10248590Smm * 2. Redistributions in binary form must reproduce the above copyright
11248590Smm *    notice, this list of conditions and the following disclaimer in the
12248590Smm *    documentation and/or other materials provided with the distribution.
13248590Smm *
14248590Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15248590Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16248590Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17248590Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18248590Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19248590Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20248590Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21248590Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22248590Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23248590Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24248590Smm */
25248590Smm
26248590Smm#include "archive_platform.h"
27248590Smm
28248590Smm__FBSDID("$FreeBSD$");
29248590Smm
30248590Smm#ifdef HAVE_STRING_H
31248590Smm#  include <string.h>
32248590Smm#endif
33248590Smm#ifdef HAVE_STDLIB_H
34248590Smm#  include <stdlib.h>
35248590Smm#endif
36248590Smm
37248590Smm#include "archive.h"
38248590Smm#include "archive_cmdline_private.h"
39248590Smm#include "archive_string.h"
40248590Smm
41248590Smmstatic int cmdline_set_path(struct archive_cmdline *, const char *);
42248590Smmstatic int cmdline_add_arg(struct archive_cmdline *, const char *);
43248590Smm
44248590Smmstatic ssize_t
45248590Smmextract_quotation(struct archive_string *as, const char *p)
46248590Smm{
47248590Smm	const char *s;
48248590Smm
49248590Smm	for (s = p + 1; *s;) {
50248590Smm		if (*s == '\\') {
51248590Smm			if (s[1] != '\0') {
52248590Smm				archive_strappend_char(as, s[1]);
53248590Smm				s += 2;
54248590Smm			} else
55248590Smm				s++;
56248590Smm		} else if (*s == '"')
57248590Smm			break;
58248590Smm		else {
59248590Smm			archive_strappend_char(as, s[0]);
60248590Smm			s++;
61248590Smm		}
62248590Smm	}
63248590Smm	if (*s != '"')
64248590Smm		return (ARCHIVE_FAILED);/* Invalid sequence. */
65248590Smm	return ((ssize_t)(s + 1 - p));
66248590Smm}
67248590Smm
68248590Smmstatic ssize_t
69248590Smmget_argument(struct archive_string *as, const char *p)
70248590Smm{
71248590Smm	const char *s = p;
72248590Smm
73248590Smm	archive_string_empty(as);
74248590Smm
75248590Smm	/* Skip beginning space characters. */
76248590Smm	while (*s != '\0' && *s == ' ')
77248590Smm		s++;
78248590Smm	/* Copy non-space characters. */
79248590Smm	while (*s != '\0' && *s != ' ') {
80248590Smm		if (*s == '\\') {
81248590Smm			if (s[1] != '\0') {
82248590Smm				archive_strappend_char(as, s[1]);
83248590Smm				s += 2;
84248590Smm			} else {
85248590Smm				s++;/* Ignore this character.*/
86248590Smm				break;
87248590Smm			}
88248590Smm		} else if (*s == '"') {
89248590Smm			ssize_t q = extract_quotation(as, s);
90248590Smm			if (q < 0)
91248590Smm				return (ARCHIVE_FAILED);/* Invalid sequence. */
92248590Smm			s += q;
93248590Smm		} else {
94248590Smm			archive_strappend_char(as, s[0]);
95248590Smm			s++;
96248590Smm		}
97248590Smm	}
98248590Smm	return ((ssize_t)(s - p));
99248590Smm}
100248590Smm
101248590Smm/*
102248590Smm * Set up command line arguments.
103248590Smm * Returns ARChIVE_OK if everything okey.
104248590Smm * Returns ARChIVE_FAILED if there is a lack of the `"' terminator or an
105248590Smm * empty command line.
106248590Smm * Returns ARChIVE_FATAL if no memory.
107248590Smm */
108248590Smmint
109248590Smm__archive_cmdline_parse(struct archive_cmdline *data, const char *cmd)
110248590Smm{
111248590Smm	struct archive_string as;
112248590Smm	const char *p;
113248590Smm	ssize_t al;
114248590Smm	int r;
115248590Smm
116248590Smm	archive_string_init(&as);
117248590Smm
118248590Smm	/* Get first argument as a command path. */
119248590Smm	al = get_argument(&as, cmd);
120248590Smm	if (al < 0) {
121248590Smm		r = ARCHIVE_FAILED;/* Invalid sequence. */
122248590Smm		goto exit_function;
123248590Smm	}
124248590Smm	if (archive_strlen(&as) == 0) {
125248590Smm		r = ARCHIVE_FAILED;/* An empty command path. */
126248590Smm		goto exit_function;
127248590Smm	}
128248590Smm	r = cmdline_set_path(data, as.s);
129248590Smm	if (r != ARCHIVE_OK)
130248590Smm		goto exit_function;
131248590Smm	p = strrchr(as.s, '/');
132248590Smm	if (p == NULL)
133248590Smm		p = as.s;
134248590Smm	else
135248590Smm		p++;
136248590Smm	r = cmdline_add_arg(data, p);
137248590Smm	if (r != ARCHIVE_OK)
138248590Smm		goto exit_function;
139248590Smm	cmd += al;
140248590Smm
141248590Smm	for (;;) {
142248590Smm		al = get_argument(&as, cmd);
143248590Smm		if (al < 0) {
144248590Smm			r = ARCHIVE_FAILED;/* Invalid sequence. */
145248590Smm			goto exit_function;
146248590Smm		}
147248590Smm		if (al == 0)
148248590Smm			break;
149248590Smm		cmd += al;
150248590Smm		if (archive_strlen(&as) == 0 && *cmd == '\0')
151248590Smm			break;
152248590Smm		r = cmdline_add_arg(data, as.s);
153248590Smm		if (r != ARCHIVE_OK)
154248590Smm			goto exit_function;
155248590Smm	}
156248590Smm	r = ARCHIVE_OK;
157248590Smmexit_function:
158248590Smm	archive_string_free(&as);
159248590Smm	return (r);
160248590Smm}
161248590Smm
162248590Smm/*
163248590Smm * Set the program path.
164248590Smm */
165248590Smmstatic int
166248590Smmcmdline_set_path(struct archive_cmdline *data, const char *path)
167248590Smm{
168248590Smm	char *newptr;
169248590Smm
170248590Smm	newptr = realloc(data->path, strlen(path) + 1);
171248590Smm	if (newptr == NULL)
172248590Smm		return (ARCHIVE_FATAL);
173248590Smm	data->path = newptr;
174248590Smm	strcpy(data->path, path);
175248590Smm	return (ARCHIVE_OK);
176248590Smm}
177248590Smm
178248590Smm/*
179248590Smm * Add a argument for the program.
180248590Smm */
181248590Smmstatic int
182248590Smmcmdline_add_arg(struct archive_cmdline *data, const char *arg)
183248590Smm{
184248590Smm	char **newargv;
185248590Smm
186248590Smm	if (data->path == NULL)
187248590Smm		return (ARCHIVE_FAILED);
188248590Smm
189248590Smm	newargv = realloc(data->argv, (data->argc + 2) * sizeof(char *));
190248590Smm	if (newargv == NULL)
191248590Smm		return (ARCHIVE_FATAL);
192248590Smm	data->argv = newargv;
193248590Smm	data->argv[data->argc] = strdup(arg);
194248590Smm	if (data->argv[data->argc] == NULL)
195248590Smm		return (ARCHIVE_FATAL);
196248590Smm	/* Set the terminator of argv. */
197248590Smm	data->argv[++data->argc] = NULL;
198248590Smm	return (ARCHIVE_OK);
199248590Smm}
200248590Smm
201248590Smmstruct archive_cmdline *
202248590Smm__archive_cmdline_allocate(void)
203248590Smm{
204248590Smm	return (struct archive_cmdline *)
205248590Smm		calloc(1, sizeof(struct archive_cmdline));
206248590Smm}
207248590Smm
208248590Smm/*
209248590Smm * Release the resources.
210248590Smm */
211248590Smmint
212248590Smm__archive_cmdline_free(struct archive_cmdline *data)
213248590Smm{
214248590Smm
215248590Smm	if (data) {
216248590Smm		free(data->path);
217248590Smm		if (data->argv != NULL) {
218248590Smm			int i;
219248590Smm			for (i = 0; data->argv[i] != NULL; i++)
220248590Smm				free(data->argv[i]);
221248590Smm			free(data->argv);
222248590Smm		}
223248590Smm		free(data);
224248590Smm	}
225248590Smm	return (ARCHIVE_OK);
226248590Smm}
227248590Smm
228