f766845f8a2e2001e22a4d7deddedc47ddf8738c
[debian/amanda] / client-src / client_util.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* 
27  * $Id: client_util.c,v 1.32 2006/03/09 16:51:41 martinea Exp $
28  *
29  */
30
31 #include "client_util.h"
32 #include "getfsent.h"
33 #include "util.h"
34
35 #define MAXMAXDUMPS 16
36
37 static char *fixup_relative(name, device)
38 char *name;
39 char *device;
40 {
41     char *newname;
42     if(*name != '/') {
43         char *dirname = amname_to_dirname(device);
44         newname = vstralloc(dirname, "/", name , NULL);
45         amfree(dirname);
46     }
47     else {
48         newname = stralloc(name);
49     }
50     return newname;
51 }
52
53
54 static char *get_name(diskname, exin, t, n)
55 char *diskname, *exin;
56 time_t t;
57 int n;
58 {
59     char number[NUM_STR_SIZE];
60     char *filename;
61     char *ts;
62
63     ts = construct_timestamp(&t);
64     if(n == 0)
65         number[0] = '\0';
66     else
67         snprintf(number, sizeof(number), "%03d", n - 1);
68         
69     filename = vstralloc(get_pname(), ".", diskname, ".", ts, number, ".",
70                          exin, NULL);
71     amfree(ts);
72     return filename;
73 }
74
75
76 static char *build_name(disk, exin, verbose)
77 char *disk, *exin;
78 int verbose;
79 {
80     int n=0, fd=-1;
81     char *filename = NULL;
82     char *afilename = NULL;
83     char *diskname;
84     time_t curtime;
85     char *dbgdir = NULL;
86     char *e = NULL;
87     DIR *d;
88     struct dirent *entry;
89     char *test_name = NULL;
90     int match_len, d_name_len;
91
92
93     time(&curtime);
94     diskname = sanitise_filename(disk);
95
96     dbgdir = stralloc2(AMANDA_TMPDIR, "/");
97     if((d = opendir(AMANDA_TMPDIR)) == NULL) {
98         error("open debug directory \"%s\": %s",
99         AMANDA_TMPDIR, strerror(errno));
100     }
101     test_name = get_name(diskname, exin,
102                          curtime - (AMANDA_DEBUG_DAYS * 24 * 60 * 60), 0);
103     match_len = strlen(get_pname()) + strlen(diskname) + 2;
104     while((entry = readdir(d)) != NULL) {
105         if(is_dot_or_dotdot(entry->d_name)) {
106             continue;
107         }
108         d_name_len = strlen(entry->d_name);
109         if(strncmp(test_name, entry->d_name, match_len) != 0
110            || d_name_len < match_len + 14 + 8
111            || strcmp(entry->d_name+ d_name_len - 7, exin) != 0) {
112             continue;                           /* not one of our files */
113         }
114         if(strcmp(entry->d_name, test_name) < 0) {
115             e = newvstralloc(e, dbgdir, entry->d_name, NULL);
116             (void) unlink(e);                   /* get rid of old file */
117         }
118     }
119     amfree(test_name);
120     amfree(e);
121     closedir(d);
122
123     n=0;
124     do {
125         filename = get_name(diskname, exin, curtime, n);
126         afilename = newvstralloc(afilename, dbgdir, filename, NULL);
127         if((fd=open(afilename, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0600)) < 0){
128             amfree(afilename);
129             n++;
130         }
131         else {
132             close(fd);
133         }
134         amfree(filename);
135     } while(!afilename && n < 1000);
136
137     if(afilename == NULL) {
138         filename = get_name(diskname, exin, curtime, 0);
139         afilename = newvstralloc(afilename, dbgdir, filename, NULL);
140         dbprintf(("%s: Cannot create '%s'\n", debug_prefix(NULL), afilename));
141         if(verbose)
142             printf("ERROR [cannot create: %s]\n", afilename);
143         amfree(filename);
144         amfree(afilename);
145     }
146
147     amfree(dbgdir);
148     amfree(diskname);
149
150     return afilename;
151 }
152
153
154 static int add_exclude(file_exclude, aexc, verbose)
155 FILE *file_exclude;
156 char *aexc;
157 int verbose;
158 {
159     int l;
160
161     l = strlen(aexc);
162     if(aexc[l-1] == '\n') {
163         aexc[l-1] = '\0';
164         l--;
165     }
166     fprintf(file_exclude, "%s\n", aexc);
167     return 1;
168 }
169
170 static int add_include(disk, device, file_include, ainc, verbose)
171 char *disk, *device;
172 FILE *file_include;
173 char *ainc;
174 int verbose;
175 {
176     int l;
177     int nb_exp=0;
178
179     l = strlen(ainc);
180     if(ainc[l-1] == '\n') {
181         ainc[l-1] = '\0';
182         l--;
183     }
184     if(l < 3) {
185         dbprintf(("%s: include must be at least 3 character long: %s\n",
186                   debug_prefix(NULL), ainc));
187         if(verbose)
188             printf("ERROR [include must be at least 3 character long: %s]\n", ainc);
189         return 0;
190     }
191     else if(ainc[0] != '.' && ainc[0] != '\0' && ainc[1] != '/') {
192         dbprintf(("%s: include must start with './': %s\n",
193                   debug_prefix(NULL), ainc));
194         if(verbose)
195             printf("ERROR [include must start with './': %s]\n", ainc);
196         return 0;
197     }
198     else {
199         char *incname = ainc+2;
200         if(strchr(incname, '/')) {
201             fprintf(file_include, "./%s\n", incname);
202             nb_exp++;
203         }
204         else {
205             char *regex;
206             DIR *d;
207             struct dirent *entry;
208
209             regex = glob_to_regex(incname);
210             if((d = opendir(device)) == NULL) {
211                 dbprintf(("%s: Can't open disk '%s']\n",
212                       debug_prefix(NULL), device));
213                 if(verbose)
214                     printf("ERROR [Can't open disk '%s']\n", device);
215                 amfree(regex);
216                 return 0;
217             }
218             else {
219                 while((entry = readdir(d)) != NULL) {
220                     if(is_dot_or_dotdot(entry->d_name)) {
221                         continue;
222                     }
223                     if(match(regex, entry->d_name)) {
224                         fprintf(file_include, "./%s\n", entry->d_name);
225                         nb_exp++;
226                     }
227                 }
228                 closedir(d);
229             }
230             amfree(regex);
231         }
232     }
233     return nb_exp;
234 }
235
236 char *build_exclude(disk, device, options, verbose)
237 char *disk, *device;
238 option_t *options;
239 int verbose;
240 {
241     char *filename;
242     FILE *file_exclude;
243     FILE *exclude;
244     char *aexc = NULL;
245     sle_t *excl;
246     int nb_exclude = 0;
247
248     if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
249     if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
250
251     if(nb_exclude == 0) return NULL;
252
253     if((filename = build_name(disk, "exclude", verbose)) != NULL) {
254         if((file_exclude = fopen(filename,"w")) != NULL) {
255
256             if(options->exclude_file) {
257                 for(excl = options->exclude_file->first; excl != NULL;
258                     excl = excl->next) {
259                     add_exclude(file_exclude, excl->name,
260                                 verbose && options->exclude_optional == 0);
261                 }
262             }
263
264             if(options->exclude_list) {
265                 for(excl = options->exclude_list->first; excl != NULL;
266                     excl = excl->next) {
267                     char *exclname = fixup_relative(excl->name, device);
268                     if((exclude = fopen(exclname, "r")) != NULL) {
269                         while ((aexc = agets(exclude)) != NULL) {
270                             add_exclude(file_exclude, aexc,
271                                         verbose && options->exclude_optional == 0);
272                             amfree(aexc);
273                         }
274                         fclose(exclude);
275                     }
276                     else {
277                         dbprintf(("%s: Can't open exclude file '%s': %s\n",
278                                   debug_prefix(NULL),
279                                   exclname, strerror(errno)));
280                         if(verbose && (options->exclude_optional == 0 ||
281                                        errno != ENOENT))
282                             printf("ERROR [Can't open exclude file '%s': %s]\n",
283                                    exclname, strerror(errno));
284                     }
285                     amfree(exclname);
286                 }
287             }
288             fclose(file_exclude);
289         }
290         else {
291             dbprintf(("%s: Can't create exclude file '%s': %s\n",
292                       debug_prefix(NULL),
293                       filename, strerror(errno)));
294             if(verbose)
295                 printf("ERROR [Can't create exclude file '%s': %s]\n", filename,
296                         strerror(errno));
297         }
298     }
299
300     return filename;
301 }
302
303 char *build_include(disk, device, options, verbose)
304 char *disk;
305 char *device;
306 option_t *options;
307 int verbose;
308 {
309     char *filename;
310     FILE *file_include;
311     FILE *include;
312     char *ainc = NULL;
313     sle_t *incl;
314     int nb_include = 0;
315     int nb_exp = 0;
316
317     if(options->include_file) nb_include += options->include_file->nb_element;
318     if(options->include_list) nb_include += options->include_list->nb_element;
319
320     if(nb_include == 0) return NULL;
321
322     if((filename = build_name(disk, "include", verbose)) != NULL) {
323         if((file_include = fopen(filename,"w")) != NULL) {
324
325             if(options->include_file) {
326                 for(incl = options->include_file->first; incl != NULL;
327                     incl = incl->next) {
328                     nb_exp += add_include(disk, device, file_include,
329                                   incl->name,
330                                    verbose && options->include_optional == 0);
331                 }
332             }
333
334             if(options->include_list) {
335                 for(incl = options->include_list->first; incl != NULL;
336                     incl = incl->next) {
337                     char *inclname = fixup_relative(incl->name, device);
338                     if((include = fopen(inclname, "r")) != NULL) {
339                         while ((ainc = agets(include)) != NULL) {
340                             nb_exp += add_include(disk, device,
341                                                   file_include, ainc,
342                                                   verbose && options->include_optional == 0);
343                             amfree(ainc);
344                         }
345                         fclose(include);
346                     }
347                     else {
348                         dbprintf(("%s: Can't open include file '%s': %s\n",
349                                   debug_prefix(NULL),
350                                   inclname, strerror(errno)));
351                         if(verbose && (options->include_optional == 0 ||
352                                        errno != ENOENT))
353                             printf("ERROR [Can't open include file '%s': %s]\n",
354                                    inclname, strerror(errno));
355                    }
356                    amfree(inclname);
357                 }
358             }
359             fclose(file_include);
360         }
361         else {
362             dbprintf(("%s: Can't create include file '%s': %s\n",
363                       debug_prefix(NULL),
364                       filename, strerror(errno)));
365             if(verbose)
366                 printf("ERROR [Can't create include file '%s': %s]\n", filename,
367                         strerror(errno));
368         }
369     }
370         
371     if(nb_exp == 0) {
372         dbprintf(("%s: No include for '%s'\n", debug_prefix(NULL), disk));
373         if(verbose && options->include_optional == 0)
374             printf("ERROR [No include for '%s']\n", disk);
375     }
376
377     return filename;
378 }
379
380
381 void init_options(options)
382 option_t *options;
383 {
384     options->str = NULL;
385     options->compress = NO_COMPR;
386     options->srvcompprog = NULL;
387     options->clntcompprog = NULL;
388     options->encrypt = ENCRYPT_NONE;
389     options->srv_encrypt = NULL;
390     options->clnt_encrypt = NULL;
391     options->srv_decrypt_opt = NULL;
392     options->clnt_decrypt_opt = NULL;
393     options->no_record = 0;
394     options->createindex = 0;
395     options->auth = NULL;
396     options->exclude_file = NULL;
397     options->exclude_list = NULL;
398     options->include_file = NULL;
399     options->include_list = NULL;
400     options->exclude_optional = 0;
401     options->include_optional = 0;
402 }
403
404
405 option_t *parse_options(str, disk, device, fs, verbose)
406 char *str;
407 char *disk, *device;
408 am_feature_t *fs;
409 int verbose;
410 {
411     char *exc;
412     option_t *options;
413     char *p, *tok;
414
415     options = alloc(sizeof(option_t));
416     init_options(options);
417     options->str = stralloc(str);
418
419     p = stralloc(str);
420     tok = strtok(p,";");
421
422     while (tok != NULL) {
423         if(am_has_feature(fs, fe_options_auth)
424            && BSTRNCMP(tok,"auth=") == 0) {
425             if(options->auth != NULL) {
426                 dbprintf(("%s: multiple auth option \"%s\"\n",
427                           debug_prefix(NULL), tok+5));
428                 if(verbose) {
429                     printf("ERROR [multiple auth option \"%s\"\n", tok+5);
430                 }
431             }
432             options->auth = stralloc(&tok[5]);
433         }
434         else if(am_has_feature(fs, fe_options_bsd_auth)
435            && BSTRNCMP(tok, "bsd-auth") == 0) {
436             if(options->auth != NULL) {
437                 dbprintf(("%s: multiple auth option \n",
438                           debug_prefix(NULL)));
439                 if(verbose) {
440                     printf("ERROR [multiple auth option \n");
441                 }
442             }
443             options->auth = stralloc("bsd");
444         }
445         else if(am_has_feature(fs, fe_options_krb4_auth)
446            && BSTRNCMP(tok, "krb4-auth") == 0) {
447             if(options->auth != NULL) {
448                 dbprintf(("%s: multiple auth option \n",
449                           debug_prefix(NULL)));
450                 if(verbose) {
451                     printf("ERROR [multiple auth option \n");
452                 }
453             }
454             options->auth = stralloc("krb4");
455         }
456         else if(BSTRNCMP(tok, "compress-fast") == 0) {
457             if(options->compress != NO_COMPR) {
458                 dbprintf(("%s: multiple compress option \n",
459                           debug_prefix(NULL)));
460                 if(verbose) {
461                     printf("ERROR [multiple compress option \n");
462                 }
463             }
464             options->compress = COMPR_FAST;
465         }
466         else if(BSTRNCMP(tok, "compress-best") == 0) {
467             if(options->compress != NO_COMPR) {
468                 dbprintf(("%s: multiple compress option \n",
469                           debug_prefix(NULL)));
470                 if(verbose) {
471                     printf("ERROR [multiple compress option \n");
472                 }
473             }
474             options->compress = COMPR_BEST;
475         }
476         else if(BSTRNCMP(tok, "srvcomp-fast") == 0) {
477             if(options->compress != NO_COMPR) {
478                 dbprintf(("%s: multiple compress option \n",
479                           debug_prefix(NULL)));
480                 if(verbose) {
481                     printf("ERROR [multiple compress option \n");
482                 }
483             }
484             options->compress = COMPR_SERVER_FAST;
485         }
486         else if(BSTRNCMP(tok, "srvcomp-best") == 0) {
487             if(options->compress != NO_COMPR) {
488                 dbprintf(("%s: multiple compress option \n",
489                           debug_prefix(NULL)));
490                 if(verbose) {
491                     printf("ERROR [multiple compress option \n");
492                 }
493             }
494             options->compress = COMPR_SERVER_BEST;
495         }
496         else if(BSTRNCMP(tok, "srvcomp-cust=") == 0) {
497             if(options->compress != NO_COMPR) {
498                 dbprintf(("%s: multiple compress option\n", 
499                           debug_prefix(NULL)));
500                 if(verbose) {
501                     printf("ERROR [multiple compress option]\n");
502                 }
503             }
504             options->srvcompprog = stralloc(tok + sizeof("srvcomp-cust=") -1);
505             options->compress = COMPR_SERVER_CUST;
506         }
507         else if(BSTRNCMP(tok, "comp-cust=") == 0) {
508             if(options->compress != NO_COMPR) {
509                 dbprintf(("%s: multiple compress option\n", 
510                           debug_prefix(NULL)));
511                 if(verbose) {
512                     printf("ERROR [multiple compress option]\n");
513                 }
514             }
515             options->clntcompprog = stralloc(tok + sizeof("comp-cust=") -1);
516             options->compress = COMPR_CUST;
517             /* parse encryption options */
518         } 
519         else if(BSTRNCMP(tok, "encrypt-serv-cust=") == 0) {
520             if(options->encrypt != ENCRYPT_NONE) {
521                 dbprintf(("%s: multiple encrypt option\n", 
522                           debug_prefix(NULL)));
523                 if(verbose) {
524                     printf("ERROR [multiple encrypt option]\n");
525                 }
526             }
527             options->srv_encrypt = stralloc(tok + sizeof("encrypt-serv-cust=") -1);
528             options->encrypt = ENCRYPT_SERV_CUST;
529         } 
530         else if(BSTRNCMP(tok, "encrypt-cust=") == 0) {
531             if(options->encrypt != ENCRYPT_NONE) {
532                 dbprintf(("%s: multiple encrypt option\n", 
533                           debug_prefix(NULL)));
534                 if(verbose) {
535                     printf("ERROR [multiple encrypt option]\n");
536                 }
537             }
538             options->clnt_encrypt= stralloc(tok + sizeof("encrypt-cust=") -1);
539             options->encrypt = ENCRYPT_CUST;
540         } 
541         else if(BSTRNCMP(tok, "server-decrypt-option=") == 0) {
542           options->srv_decrypt_opt = stralloc(tok + sizeof("server-decrypt-option=") -1);
543         }
544         else if(BSTRNCMP(tok, "client-decrypt-option=") == 0) {
545           options->clnt_decrypt_opt = stralloc(tok + sizeof("client-decrypt-option=") -1);
546         }
547         else if(BSTRNCMP(tok, "no-record") == 0) {
548             if(options->no_record != 0) {
549                 dbprintf(("%s: multiple no-record option \n",
550                           debug_prefix(NULL)));
551                 if(verbose) {
552                     printf("ERROR [multiple no-record option \n");
553                 }
554             }
555             options->no_record = 1;
556         }
557         else if(BSTRNCMP(tok, "index") == 0) {
558             if(options->createindex != 0) {
559                 dbprintf(("%s: multiple index option \n",
560                           debug_prefix(NULL)));
561                 if(verbose) {
562                     printf("ERROR [multiple index option \n");
563                 }
564             }
565             options->createindex = 1;
566         }
567         else if(BSTRNCMP(tok, "exclude-optional") == 0) {
568             if(options->exclude_optional != 0) {
569                 dbprintf(("%s: multiple exclude-optional option \n",
570                           debug_prefix(NULL)));
571                 if(verbose) {
572                     printf("ERROR [multiple exclude-optional option \n");
573                 }
574             }
575             options->exclude_optional = 1;
576         }
577         else if(strcmp(tok, "include-optional") == 0) {
578             if(options->include_optional != 0) {
579                 dbprintf(("%s: multiple include-optional option \n",
580                           debug_prefix(NULL)));
581                 if(verbose) {
582                     printf("ERROR [multiple include-optional option \n");
583                 }
584             }
585             options->include_optional = 1;
586         }
587         else if(BSTRNCMP(tok,"exclude-file=") == 0) {
588             exc = &tok[13];
589             options->exclude_file = append_sl(options->exclude_file,exc);
590         }
591         else if(BSTRNCMP(tok,"exclude-list=") == 0) {
592             exc = &tok[13];
593             options->exclude_list = append_sl(options->exclude_list, exc);
594         }
595         else if(BSTRNCMP(tok,"include-file=") == 0) {
596             exc = &tok[13];
597             options->include_file = append_sl(options->include_file,exc);
598         }
599         else if(BSTRNCMP(tok,"include-list=") == 0) {
600             exc = &tok[13];
601             options->include_list = append_sl(options->include_list, exc);
602         }
603         else if(strcmp(tok,"|") == 0) {
604         }
605         else {
606             dbprintf(("%s: unknown option \"%s\"\n", debug_prefix(NULL), tok));
607             if(verbose) {
608                 printf("ERROR [unknown option \"%s\"]\n", tok);
609             }
610         }
611         tok = strtok(NULL, ";");
612     }
613     amfree(p);
614     return options;
615 }
616
617
618 void init_g_options(g_options)
619 g_option_t *g_options;
620 {
621     g_options->features = NULL;
622     g_options->hostname = NULL;
623     g_options->maxdumps = 0;
624 }
625
626
627 g_option_t *parse_g_options(str, verbose)
628 char *str;
629 int verbose;
630 {
631     g_option_t *g_options;
632     char *p, *tok;
633     int new_maxdumps;
634
635     g_options = alloc(sizeof(g_option_t));
636     init_g_options(g_options);
637     g_options->str = stralloc(str);
638
639     p = stralloc(str);
640     tok = strtok(p,";");
641
642     while (tok != NULL) {
643         if(strncmp(tok,"features=", 9) == 0) {
644             if(g_options->features != NULL) {
645                 dbprintf(("%s: multiple features option\n", 
646                           debug_prefix(NULL)));
647                 if(verbose) {
648                     printf("ERROR [multiple features option]\n");
649                 }
650             }
651             if((g_options->features = am_string_to_feature(tok+9)) == NULL) {
652                 dbprintf(("%s: bad features value \"%s\n",
653                           debug_prefix(NULL), tok+10));
654                 if(verbose) {
655                     printf("ERROR [bad features value \"%s\"]\n", tok+10);
656                 }
657             }
658         }
659         else if(strncmp(tok,"hostname=", 9) == 0) {
660             if(g_options->hostname != NULL) {
661                 dbprintf(("%s: multiple hostname option\n", 
662                           debug_prefix(NULL)));
663                 if(verbose) {
664                     printf("ERROR [multiple hostname option]\n");
665                 }
666             }
667             g_options->hostname = stralloc(tok+9);
668         }
669         else if(strncmp(tok,"maxdumps=", 9) == 0) {
670             if(g_options->maxdumps != 0) {
671                 dbprintf(("%s: multiple maxdumps option\n", 
672                           debug_prefix(NULL)));
673                 if(verbose) {
674                     printf("ERROR [multiple maxdumps option]\n");
675                 }
676             }
677             if(sscanf(tok+9, "%d;", &new_maxdumps) == 1) {
678                 if (new_maxdumps > MAXMAXDUMPS) {
679                     g_options->maxdumps = MAXMAXDUMPS;
680                 }
681                 else if (new_maxdumps > 0) {
682                     g_options->maxdumps = new_maxdumps;
683                 }
684                 else {
685                     dbprintf(("%s: bad maxdumps value \"%s\"\n",
686                               debug_prefix(NULL), tok+9));
687                     if(verbose) {
688                         printf("ERROR [bad maxdumps value \"%s\"]\n",
689                                tok+9);
690                     }
691                 }
692             }
693             else {
694                 dbprintf(("%s: bad maxdumps value \"%s\"\n",
695                           debug_prefix(NULL), tok+9));
696                 if(verbose) {
697                     printf("ERROR [bad maxdumps value \"%s\"]\n",
698                            tok+9);
699                 }
700             }
701         }
702         else {
703             dbprintf(("%s: unknown option \"%s\"\n",
704                                   debug_prefix(NULL), tok));
705             if(verbose) {
706                 printf("ERROR [unknown option \"%s\"]\n", tok);
707             }
708         }
709         tok = strtok(NULL, ";");
710     }
711     if(g_options->features == NULL) {
712         g_options->features = am_set_default_feature_set();
713     }
714     if(g_options->maxdumps == 0) /* default */
715         g_options->maxdumps = 1;
716     amfree(p);
717     return g_options;
718 }