Imported Upstream version 3.2.0
[debian/amanda] / server-src / cmdline.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc.  All Rights Reserved.
3  * 
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation.
7  * 
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  * 
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16  * 
17  * Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
18  * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19  *
20  * Author: Dustin J. Mitchell <dustin@zmanda.com>
21  */
22 /*
23  * $Id$
24  *
25  * Utility routines for handling command lines.
26  */
27
28 #include "amanda.h"
29 #include <ctype.h>
30 #include "match.h"
31 #include "cmdline.h"
32 #include "holding.h"
33
34 dumpspec_t *
35 dumpspec_new(
36     char *host, 
37     char *disk, 
38     char *datestamp,
39     char *level,
40     char *write_timestamp)
41 {
42     dumpspec_t *rv;
43
44     rv = g_new0(dumpspec_t, 1);
45     if (host) rv->host = stralloc(host);
46     if (disk) rv->disk = stralloc(disk);
47     if (datestamp) rv->datestamp = stralloc(datestamp);
48     if (level) rv->level = stralloc(level);
49     if (write_timestamp) rv->write_timestamp = stralloc(write_timestamp);
50
51     return rv;
52 }
53
54 void
55 dumpspec_free(
56     dumpspec_t *dumpspec)
57 {
58     if (!dumpspec) return;
59     if (dumpspec->host) free(dumpspec->host);
60     if (dumpspec->disk) free(dumpspec->disk);
61     if (dumpspec->datestamp) free(dumpspec->datestamp);
62     if (dumpspec->level) free(dumpspec->level);
63     if (dumpspec->write_timestamp) free(dumpspec->write_timestamp);
64     free(dumpspec);
65 }
66
67 void
68 dumpspec_list_free(
69     GSList *dumpspec_list)
70 {
71     /* first free all of the individual dumpspecs */
72     g_slist_foreach_nodata(dumpspec_list, dumpspec_free);
73
74     /* then free the list itself */
75     g_slist_free(dumpspec_list);
76 }
77
78 GSList *
79 cmdline_parse_dumpspecs(
80     int argc,
81     char **argv,
82     int flags)
83 {
84     dumpspec_t *dumpspec = NULL;
85     GSList *list = NULL;
86     char *errstr;
87     char *name;
88     int optind = 0;
89     enum { ARG_GET_HOST, ARG_GET_DISK, ARG_GET_DATESTAMP, ARG_GET_LEVEL } arg_state = ARG_GET_HOST;
90
91     while (optind < argc) {
92         name = argv[optind];
93         switch (arg_state) {
94             case ARG_GET_HOST:
95                 arg_state = ARG_GET_DISK;
96                 dumpspec = dumpspec_new(name, NULL, NULL, NULL, NULL);
97                 list = g_slist_append(list, (gpointer)dumpspec);
98                 break;
99
100             case ARG_GET_DISK:
101                 arg_state = ARG_GET_DATESTAMP;
102                 dumpspec->disk = stralloc(name);
103                 break;
104
105             case ARG_GET_DATESTAMP:
106                 arg_state = ARG_GET_LEVEL;
107                 if (!(flags & CMDLINE_PARSE_DATESTAMP)) continue;
108                 dumpspec->datestamp = stralloc(name);
109                 break;
110
111             case ARG_GET_LEVEL:
112                 arg_state = ARG_GET_HOST;
113                 if (!(flags & CMDLINE_PARSE_LEVEL)) continue;
114                 if (name[0] != '\0'
115                     && (errstr=validate_regexp(name)) != NULL) {
116                     error(_("bad level regex \"%s\": %s\n"), name, errstr);
117                 }
118                 dumpspec->level = stralloc(name);
119                 break;
120         }
121
122         optind++;
123     }
124
125     /* if nothing was processed and the caller has requested it, 
126      * then add an "empty" element */
127     if (list == NULL && (flags & CMDLINE_EMPTY_TO_WILDCARD)) {
128         dumpspec = dumpspec_new("", "", 
129                 (flags & CMDLINE_PARSE_DATESTAMP)?"":NULL,
130                 (flags & CMDLINE_PARSE_LEVEL)?"":NULL, "");
131         list = g_slist_append(list, (gpointer)dumpspec);
132     }
133
134     return list;
135 }
136
137 char *
138 cmdline_format_dumpspec(
139     dumpspec_t *dumpspec)
140 {
141     if (!dumpspec) return NULL;
142     return cmdline_format_dumpspec_components(
143         dumpspec->host,
144         dumpspec->disk,
145         dumpspec->datestamp,
146         dumpspec->level);
147 }
148
149 /* Quote str for shell interpretation, being conservative.
150  * Any non-alphanumeric charcacters other than '.' and '/'
151  * trigger surrounding single quotes, and single quotes and
152  * backslashes within those single quotes are escaped.
153  */
154 static char *
155 quote_dumpspec_string(char *str)
156 {
157     char *rv;
158     char *p, *q;
159     int len = 0;
160     int need_single_quotes = 0;
161
162     if (!str[0])
163         return stralloc("''"); /* special-case the empty string */
164
165     for (p = str; *p; p++) {
166         if (!isalnum((int)*p) && *p != '.' && *p != '/') need_single_quotes=1;
167         if (*p == '\'' || *p == '\\') len++; /* extra byte for '\' */
168         len++;
169     }
170     if (need_single_quotes) len += 2;
171
172     q = rv = malloc(len+1);
173     if (need_single_quotes) *(q++) = '\'';
174     for (p = str; *p; p++) {
175         if (*p == '\'' || *p == '\\') *(q++) = '\\';
176         *(q++) = *p;
177     }
178     if (need_single_quotes) *(q++) = '\'';
179     *(q++) = '\0';
180
181     return rv;
182 }
183
184 char *
185 cmdline_format_dumpspec_components(
186     char *host,
187     char *disk,
188     char *datestamp,
189     char *level)
190 {
191     char *rv = NULL;
192
193     host = host? quote_dumpspec_string(host):NULL;
194     disk = disk? quote_dumpspec_string(disk):NULL;
195     datestamp = datestamp? quote_dumpspec_string(datestamp):NULL;
196     level = level? quote_dumpspec_string(level):NULL;
197
198     if (host) {
199         rv = host;
200         host = NULL;
201         if (disk) {
202             rv = newvstralloc(rv, rv, " ", disk, NULL);
203             if (datestamp) {
204                 rv = newvstralloc(rv, rv, " ", datestamp, NULL);
205                 if (level) {
206                     rv = newvstralloc(rv, rv, " ", level, NULL);
207                 }
208             }
209         }
210     }
211
212     if (host) amfree(host);
213     if (disk) amfree(disk);
214     if (datestamp) amfree(datestamp);
215     if (level) amfree(level);
216
217     return rv;
218 }
219
220 GSList *
221 cmdline_match_holding(
222     GSList *dumpspec_list)
223 {
224     dumpspec_t *de;
225     GSList *li, *hi;
226     GSList *holding_files;
227     GSList *matching_files = NULL;
228     dumpfile_t file;
229
230     holding_files = holding_get_files(NULL, 1);
231
232     for (hi = holding_files; hi != NULL; hi = hi->next) {
233         /* TODO add level */
234         if (!holding_file_get_dumpfile((char *)hi->data, &file)) continue;
235         if (file.type != F_DUMPFILE) {
236             dumpfile_free_data(&file);
237             continue;
238         }
239         for (li = dumpspec_list; li != NULL; li = li->next) {
240             de = (dumpspec_t *)(li->data);
241             if (de->host && de->host[0] && !match_host(de->host, file.name)) continue;
242             if (de->disk && de->disk[0] && !match_disk(de->disk, file.disk)) continue;
243             if (de->datestamp && de->datestamp[0] && !match_datestamp(de->datestamp, file.datestamp)) continue;
244             matching_files = g_slist_append(matching_files, g_strdup((char *)hi->data));
245             break;
246         }
247         dumpfile_free_data(&file);
248     }
249
250     g_slist_free_full(holding_files);
251
252     return matching_files;
253 }