/*
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Carbonite Inc., 756 N Pastoria Ave
* Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
*
* Author: Dustin J. Mitchell <dustin@zmanda.com>
*/
/*
* $Id$
*
* Utility routines for handling command lines.
*/
#include "amanda.h"
#include <ctype.h>
#include "match.h"
#include "cmdline.h"
#include "holding.h"
dumpspec_t *
dumpspec_new(
char *host,
char *disk,
char *datestamp,
char *level,
char *write_timestamp)
{
dumpspec_t *rv;
rv = g_new0(dumpspec_t, 1);
if (host) rv->host = g_strdup(host);
if (disk) rv->disk = g_strdup(disk);
if (datestamp) rv->datestamp = g_strdup(datestamp);
if (level) rv->level = g_strdup(level);
if (write_timestamp) rv->write_timestamp = g_strdup(write_timestamp);
return rv;
}
void
dumpspec_free(
dumpspec_t *dumpspec)
{
if (!dumpspec) return;
if (dumpspec->host) free(dumpspec->host);
if (dumpspec->disk) free(dumpspec->disk);
if (dumpspec->datestamp) free(dumpspec->datestamp);
if (dumpspec->level) free(dumpspec->level);
if (dumpspec->write_timestamp) free(dumpspec->write_timestamp);
free(dumpspec);
}
void
dumpspec_list_free(
GSList *dumpspec_list)
{
/* first free all of the individual dumpspecs */
g_slist_foreach_nodata(dumpspec_list, dumpspec_free);
/* then free the list itself */
g_slist_free(dumpspec_list);
}
GSList *
cmdline_parse_dumpspecs(
int argc,
char **argv,
int flags)
{
dumpspec_t *dumpspec = NULL;
GSList *list = NULL;
char *errstr;
char *name;
int optind = 0;
enum { ARG_GET_HOST, ARG_GET_DISK, ARG_GET_DATESTAMP, ARG_GET_LEVEL } arg_state = ARG_GET_HOST;
while (optind < argc) {
char *new_name = NULL;
name = argv[optind];
if (flags & CMDLINE_EXACT_MATCH && *name != '=') {
new_name = g_strconcat("=", name, NULL);
name = new_name;
}
switch (arg_state) {
case ARG_GET_HOST:
arg_state = ARG_GET_DISK;
dumpspec = dumpspec_new(name, NULL, NULL, NULL, NULL);
list = g_slist_append(list, (gpointer)dumpspec);
break;
case ARG_GET_DISK:
arg_state = ARG_GET_DATESTAMP;
dumpspec->disk = g_strdup(name);
break;
case ARG_GET_DATESTAMP:
arg_state = ARG_GET_LEVEL;
if (!(flags & CMDLINE_PARSE_DATESTAMP)) continue;
dumpspec->datestamp = g_strdup(name);
break;
case ARG_GET_LEVEL:
arg_state = ARG_GET_HOST;
if (!(flags & CMDLINE_PARSE_LEVEL)) continue;
if (name[0] != '\0'
&& !(flags & CMDLINE_EXACT_MATCH)
&& (errstr=validate_regexp(name)) != NULL) {
error(_("bad level regex \"%s\": %s\n"), name, errstr);
}
dumpspec->level = g_strdup(name);
break;
}
amfree(new_name);
optind++;
}
/* if nothing was processed and the caller has requested it,
* then add an "empty" element */
if (list == NULL && (flags & CMDLINE_EMPTY_TO_WILDCARD)) {
dumpspec = dumpspec_new("", "",
(flags & CMDLINE_PARSE_DATESTAMP)?"":NULL,
(flags & CMDLINE_PARSE_LEVEL)?"":NULL, "");
list = g_slist_append(list, (gpointer)dumpspec);
}
return list;
}
char *
cmdline_format_dumpspec(
dumpspec_t *dumpspec)
{
if (!dumpspec) return NULL;
return cmdline_format_dumpspec_components(
dumpspec->host,
dumpspec->disk,
dumpspec->datestamp,
dumpspec->level);
}
/* Quote str for shell interpretation, being conservative.
* Any non-alphanumeric charcacters other than '.' and '/'
* trigger surrounding single quotes, and single quotes and
* backslashes within those single quotes are escaped.
*/
static char *
quote_dumpspec_string(char *str)
{
char *rv;
char *p, *q;
int len = 0;
int need_single_quotes = 0;
if (!str[0])
return g_strdup("''"); /* special-case the empty string */
for (p = str; *p; p++) {
if (!isalnum((int)*p) && *p != '.' && *p != '/') need_single_quotes=1;
if (*p == '\'' || *p == '\\') len++; /* extra byte for '\' */
len++;
}
if (need_single_quotes) len += 2;
q = rv = malloc(len+1);
if (need_single_quotes) *(q++) = '\'';
for (p = str; *p; p++) {
if (*p == '\'' || *p == '\\') *(q++) = '\\';
*(q++) = *p;
}
if (need_single_quotes) *(q++) = '\'';
*(q++) = '\0';
return rv;
}
char *
cmdline_format_dumpspec_components(
char *host,
char *disk,
char *datestamp,
char *level)
{
GPtrArray *array = g_ptr_array_new();
gchar **strings;
char *ret = NULL;
if (!host)
goto out;
g_ptr_array_add(array, quote_dumpspec_string(host));
if (!disk)
goto out;
g_ptr_array_add(array, quote_dumpspec_string(disk));
if (!datestamp)
goto out;
g_ptr_array_add(array, quote_dumpspec_string(datestamp));
if (level)
g_ptr_array_add(array, quote_dumpspec_string(level));
out:
g_ptr_array_add(array, NULL);
strings = (gchar **) g_ptr_array_free(array, FALSE);
/*
* Note that if the only element of the GPtrArray is a NULL pointer, the
* result will be an array with only a NULL pointer, in which case
* g_strjoinv() will return an empty string. But we want to return NULL to
* the caller in that case.
*/
ret = (*strings) ? g_strjoinv(" ", strings) : NULL;
g_strfreev(strings);
return ret;
}
GSList *
cmdline_match_holding(
GSList *dumpspec_list)
{
dumpspec_t *de;
GSList *li, *hi;
GSList *holding_files;
GSList *matching_files = NULL;
dumpfile_t file;
holding_files = holding_get_files(NULL, 1, 0);
for (hi = holding_files; hi != NULL; hi = hi->next) {
/* TODO add level */
if (!holding_file_get_dumpfile((char *)hi->data, &file)) continue;
if (file.type != F_DUMPFILE) {
dumpfile_free_data(&file);
continue;
}
for (li = dumpspec_list; li != NULL; li = li->next) {
de = (dumpspec_t *)(li->data);
if (de->host && de->host[0] && !match_host(de->host, file.name)) continue;
if (de->disk && de->disk[0] && !match_disk(de->disk, file.disk)) continue;
if (de->datestamp && de->datestamp[0] && !match_datestamp(de->datestamp, file.datestamp)) continue;
matching_files = g_slist_append(matching_files, g_strdup((char *)hi->data));
break;
}
dumpfile_free_data(&file);
}
slist_free_full(holding_files, g_free);
return matching_files;
}