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