| /*- |
| * Copyright (c) 2012 Michihiro NAKAJIMA |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "archive_platform.h" |
| |
| __FBSDID("$FreeBSD$"); |
| |
| #ifdef HAVE_STRING_H |
| # include <string.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| # include <stdlib.h> |
| #endif |
| |
| #include "archive.h" |
| #include "archive_cmdline_private.h" |
| #include "archive_string.h" |
| |
| static int cmdline_set_path(struct archive_cmdline *, const char *); |
| static int cmdline_add_arg(struct archive_cmdline *, const char *); |
| |
| static ssize_t |
| extract_quotation(struct archive_string *as, const char *p) |
| { |
| const char *s; |
| |
| for (s = p + 1; *s;) { |
| if (*s == '\\') { |
| if (s[1] != '\0') { |
| archive_strappend_char(as, s[1]); |
| s += 2; |
| } else |
| s++; |
| } else if (*s == '"') |
| break; |
| else { |
| archive_strappend_char(as, s[0]); |
| s++; |
| } |
| } |
| if (*s != '"') |
| return (ARCHIVE_FAILED);/* Invalid sequence. */ |
| return ((ssize_t)(s + 1 - p)); |
| } |
| |
| static ssize_t |
| get_argument(struct archive_string *as, const char *p) |
| { |
| const char *s = p; |
| |
| archive_string_empty(as); |
| |
| /* Skip beginning space characters. */ |
| while (*s != '\0' && *s == ' ') |
| s++; |
| /* Copy non-space characters. */ |
| while (*s != '\0' && *s != ' ') { |
| if (*s == '\\') { |
| if (s[1] != '\0') { |
| archive_strappend_char(as, s[1]); |
| s += 2; |
| } else { |
| s++;/* Ignore this character.*/ |
| break; |
| } |
| } else if (*s == '"') { |
| ssize_t q = extract_quotation(as, s); |
| if (q < 0) |
| return (ARCHIVE_FAILED);/* Invalid sequence. */ |
| s += q; |
| } else { |
| archive_strappend_char(as, s[0]); |
| s++; |
| } |
| } |
| return ((ssize_t)(s - p)); |
| } |
| |
| /* |
| * Set up command line arguments. |
| * Returns ARChIVE_OK if everything okey. |
| * Returns ARChIVE_FAILED if there is a lack of the `"' terminator or an |
| * empty command line. |
| * Returns ARChIVE_FATAL if no memory. |
| */ |
| int |
| __archive_cmdline_parse(struct archive_cmdline *data, const char *cmd) |
| { |
| struct archive_string as; |
| const char *p; |
| ssize_t al; |
| int r; |
| |
| archive_string_init(&as); |
| |
| /* Get first argument as a command path. */ |
| al = get_argument(&as, cmd); |
| if (al < 0) { |
| r = ARCHIVE_FAILED;/* Invalid sequence. */ |
| goto exit_function; |
| } |
| if (archive_strlen(&as) == 0) { |
| r = ARCHIVE_FAILED;/* An empty command path. */ |
| goto exit_function; |
| } |
| r = cmdline_set_path(data, as.s); |
| if (r != ARCHIVE_OK) |
| goto exit_function; |
| p = strrchr(as.s, '/'); |
| if (p == NULL) |
| p = as.s; |
| else |
| p++; |
| r = cmdline_add_arg(data, p); |
| if (r != ARCHIVE_OK) |
| goto exit_function; |
| cmd += al; |
| |
| for (;;) { |
| al = get_argument(&as, cmd); |
| if (al < 0) { |
| r = ARCHIVE_FAILED;/* Invalid sequence. */ |
| goto exit_function; |
| } |
| if (al == 0) |
| break; |
| cmd += al; |
| if (archive_strlen(&as) == 0 && *cmd == '\0') |
| break; |
| r = cmdline_add_arg(data, as.s); |
| if (r != ARCHIVE_OK) |
| goto exit_function; |
| } |
| r = ARCHIVE_OK; |
| exit_function: |
| archive_string_free(&as); |
| return (r); |
| } |
| |
| /* |
| * Set the program path. |
| */ |
| static int |
| cmdline_set_path(struct archive_cmdline *data, const char *path) |
| { |
| char *newptr; |
| |
| newptr = realloc(data->path, strlen(path) + 1); |
| if (newptr == NULL) |
| return (ARCHIVE_FATAL); |
| data->path = newptr; |
| strcpy(data->path, path); |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Add a argument for the program. |
| */ |
| static int |
| cmdline_add_arg(struct archive_cmdline *data, const char *arg) |
| { |
| char **newargv; |
| |
| if (data->path == NULL) |
| return (ARCHIVE_FAILED); |
| |
| newargv = realloc(data->argv, (data->argc + 2) * sizeof(char *)); |
| if (newargv == NULL) |
| return (ARCHIVE_FATAL); |
| data->argv = newargv; |
| data->argv[data->argc] = strdup(arg); |
| if (data->argv[data->argc] == NULL) |
| return (ARCHIVE_FATAL); |
| /* Set the terminator of argv. */ |
| data->argv[++data->argc] = NULL; |
| return (ARCHIVE_OK); |
| } |
| |
| struct archive_cmdline * |
| __archive_cmdline_allocate(void) |
| { |
| return (struct archive_cmdline *) |
| calloc(1, sizeof(struct archive_cmdline)); |
| } |
| |
| /* |
| * Release the resources. |
| */ |
| int |
| __archive_cmdline_free(struct archive_cmdline *data) |
| { |
| |
| if (data) { |
| free(data->path); |
| if (data->argv != NULL) { |
| int i; |
| for (i = 0; data->argv[i] != NULL; i++) |
| free(data->argv[i]); |
| free(data->argv); |
| } |
| free(data); |
| } |
| return (ARCHIVE_OK); |
| } |
| |