892b6a3c6b600120081ff99388febf2426ebfccf
[debian/amanda] / server-src / cmdline.c
1 /*
2  * Copyright (c) 2005-2008 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 Mathlida 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 <ctype.h>
29 #include "amanda.h"
30 #include "cmdline.h"
31 #include "holding.h"
32
33 dumpspec_t *
34 dumpspec_new(
35     char *host, 
36     char *disk, 
37     char *datestamp,
38     char *level)
39 {
40     dumpspec_t *rv;
41
42     rv = g_new0(dumpspec_t, 1);
43     if (host) rv->host = stralloc(host);
44     if (disk) rv->disk = stralloc(disk);
45     if (datestamp) rv->datestamp = stralloc(datestamp);
46     if (level) rv->level = stralloc(level);
47
48     return rv;
49 }
50
51 void
52 dumpspec_free(
53     dumpspec_t *dumpspec)
54 {
55     if (!dumpspec) return;
56     if (dumpspec->host) free(dumpspec->host);
57     if (dumpspec->disk) free(dumpspec->disk);
58     if (dumpspec->datestamp) free(dumpspec->datestamp);
59     if (dumpspec->level) free(dumpspec->level);
60     free(dumpspec);
61 }
62
63 void
64 dumpspec_list_free(
65     GSList *dumpspec_list)
66 {
67     /* first free all of the individual dumpspecs */
68     g_slist_foreach_nodata(dumpspec_list, dumpspec_free);
69
70     /* then free the list itself */
71     g_slist_free(dumpspec_list);
72 }
73
74 GSList *
75 cmdline_parse_dumpspecs(
76     int argc,
77     char **argv,
78     int flags)
79 {
80     dumpspec_t *dumpspec = NULL;
81     GSList *list = NULL;
82     char *errstr;
83     char *name;
84     int optind = 0;
85     enum { ARG_GET_HOST, ARG_GET_DISK, ARG_GET_DATESTAMP, ARG_GET_LEVEL } arg_state = ARG_GET_HOST;
86
87     while (optind < argc) {
88         name = argv[optind];
89         switch (arg_state) {
90             case ARG_GET_HOST:
91                 arg_state = ARG_GET_DISK;
92                 if (name[0] != '\0'
93                     && (errstr=validate_regexp(name)) != NULL) {
94                     error(_("bad hostname regex \"%s\": %s\n"), name, errstr);
95                 }
96                 dumpspec = dumpspec_new(name, 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                 if (name[0] != '\0'
103                     && (errstr=validate_regexp(name)) != NULL) {
104                     error(_("bad diskname regex \"%s\": %s\n"), name, errstr);
105                 }
106                 dumpspec->disk = stralloc(name);
107                 break;
108
109             case ARG_GET_DATESTAMP:
110                 arg_state = ARG_GET_LEVEL;
111                 if (!(flags & CMDLINE_PARSE_DATESTAMP)) continue;
112                 if (name[0] != '\0'
113                     && (errstr=validate_regexp(name)) != NULL) {
114                     error(_("bad datestamp regex \"%s\": %s\n"), name, errstr);
115                 }
116                 dumpspec->datestamp = stralloc(name);
117                 break;
118
119             case ARG_GET_LEVEL:
120                 arg_state = ARG_GET_HOST;
121                 if (!(flags & CMDLINE_PARSE_LEVEL)) continue;
122                 if (name[0] != '\0'
123                     && (errstr=validate_regexp(name)) != NULL) {
124                     error(_("bad level regex \"%s\": %s\n"), name, errstr);
125                 }
126                 dumpspec->level = stralloc(name);
127                 break;
128         }
129
130         optind++;
131     }
132
133     /* if nothing was processed and the caller has requested it, 
134      * then add an "empty" element */
135     if (list == NULL && (flags & CMDLINE_EMPTY_TO_WILDCARD)) {
136         dumpspec = dumpspec_new("", "", 
137                 (flags & CMDLINE_PARSE_DATESTAMP)?"":NULL,
138                 (flags & CMDLINE_PARSE_LEVEL)?"":NULL);
139         list = g_slist_append(list, (gpointer)dumpspec);
140     }
141
142     return list;
143 }
144
145 char *
146 cmdline_format_dumpspec(
147     dumpspec_t *dumpspec)
148 {
149     if (!dumpspec) return NULL;
150     return cmdline_format_dumpspec_components(
151         dumpspec->host,
152         dumpspec->disk,
153         dumpspec->datestamp,
154         dumpspec->level);
155 }
156
157 /* Quote str for shell interpretation, being conservative.
158  * Any non-alphanumeric charcacters other than '.' and '/'
159  * trigger surrounding single quotes, and single quotes and
160  * backslashes within those single quotes are escaped.
161  */
162 static char *
163 quote_dumpspec_string(char *str)
164 {
165     char *rv;
166     char *p, *q;
167     int len = 0;
168     int need_single_quotes = 0;
169
170     if (!str[0])
171         return stralloc("''"); /* special-case the empty string */
172
173     for (p = str; *p; p++) {
174         if (!isalnum((int)*p) && *p != '.' && *p != '/') need_single_quotes=1;
175         if (*p == '\'' || *p == '\\') len++; /* extra byte for '\' */
176         len++;
177     }
178     if (need_single_quotes) len += 2;
179
180     q = rv = malloc(len+1);
181     if (need_single_quotes) *(q++) = '\'';
182     for (p = str; *p; p++) {
183         if (*p == '\'' || *p == '\\') *(q++) = '\\';
184         *(q++) = *p;
185     }
186     if (need_single_quotes) *(q++) = '\'';
187     *(q++) = '\0';
188
189     return rv;
190 }
191
192 char *
193 cmdline_format_dumpspec_components(
194     char *host,
195     char *disk,
196     char *datestamp,
197     char *level)
198 {
199     char *rv = NULL;
200
201     host = host? quote_dumpspec_string(host):NULL;
202     disk = disk? quote_dumpspec_string(disk):NULL;
203     datestamp = datestamp? quote_dumpspec_string(datestamp):NULL;
204     level = level? quote_dumpspec_string(level):NULL;
205
206     if (host) {
207         rv = host;
208         host = NULL;
209         if (disk) {
210             rv = newvstralloc(rv, rv, " ", disk, NULL);
211             if (datestamp) {
212                 rv = newvstralloc(rv, rv, " ", datestamp, NULL);
213                 if (level) {
214                     rv = newvstralloc(rv, rv, " ", level, NULL);
215                 }
216             }
217         }
218     }
219
220     if (host) amfree(host);
221     if (disk) amfree(disk);
222     if (datestamp) amfree(datestamp);
223     if (level) amfree(level);
224
225     return rv;
226 }
227
228 GSList *
229 cmdline_match_holding(
230     GSList *dumpspec_list)
231 {
232     dumpspec_t *de;
233     GSList *li, *hi;
234     GSList *holding_files;
235     GSList *matching_files = NULL;
236     dumpfile_t file;
237
238     holding_files = holding_get_files(NULL, 1);
239
240     for (hi = holding_files; hi != NULL; hi = hi->next) {
241         /* TODO add level */
242         if (!holding_file_get_dumpfile((char *)hi->data, &file)) continue;
243         if (file.type != F_DUMPFILE) {
244             dumpfile_free_data(&file);
245             continue;
246         }
247         for (li = dumpspec_list; li != NULL; li = li->next) {
248             de = (dumpspec_t *)(li->data);
249             if (de->host && de->host[0] && !match_host(de->host, file.name)) continue;
250             if (de->disk && de->disk[0] && !match_disk(de->disk, file.disk)) continue;
251             if (de->datestamp && de->datestamp[0] && !match_datestamp(de->datestamp, file.datestamp)) continue;
252             matching_files = g_slist_append(matching_files, g_strdup((char *)hi->data));
253             break;
254         }
255         dumpfile_free_data(&file);
256     }
257
258     g_slist_free_full(holding_files);
259
260     return matching_files;
261 }