2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: fileheader.c 6512 2007-05-24 17:00:24Z ian $
31 #include "fileheader.h"
35 static const char * filetype2str(filetype_t);
36 static filetype_t str2filetype(const char *);
37 static void strange_header(dumpfile_t *, const char *,
38 size_t, const char *, const char *);
39 static ssize_t hexdump(const char *buffer, size_t len);
40 static char *quote_heredoc(char *text, char *delimiter_prefix);
41 static char *parse_heredoc(char *line, char **saveptr);
47 memset(file, '\0', SIZEOF(*file));
65 g_fprintf(stderr, _("%s: strange amanda header: \"%.*s\"\n"), get_pname(),
68 g_fprintf(stderr, _("%s: Expected: \"%s\" Actual: \"%s\"\n"), get_pname(),
74 /* chop whitespace off of a string, in place */
83 /* trim leading space */
84 while (g_ascii_isspace(*s)) { s++; }
86 memmove(str, s, strlen(s)+1);
88 /* trim trailing space */
90 for (s = str+strlen(str)-1; s >= str; s--) {
91 if (!g_ascii_isspace(*s))
104 char *buf, *line, *tok, *line1;
108 char *saveptr = NULL;
110 /* put the buffer into a writable chunk of memory and nul-term it */
111 buf = alloc(buflen + 1);
112 memcpy(buf, buffer, buflen);
116 /* extract the first unquoted line */
118 for (line = buf, lsize = 0; lsize < buflen; line++) {
119 if ((*line == '\n') && !in_quotes)
123 in_quotes = !in_quotes;
124 } else if ((*line == '\\') && (*(line + 1) == '"')) {
131 line1 = alloc(lsize + 1);
132 strncpy(line1, buf, lsize);
136 tok = strtok_r(line1, " ", &saveptr);
138 g_fprintf(stderr, _("%s: Empty amanda header: buflen=%zu lsize=%zu\n"), get_pname(),
141 hexdump(buffer, lsize);
142 strange_header(file, buffer, buflen, _("<Non-empty line>"), tok);
146 if (strcmp(tok, "NETDUMP:") != 0 && strcmp(tok, "AMANDA:") != 0) {
148 file->type = F_UNKNOWN;
153 tok = strtok_r(NULL, " ", &saveptr);
155 strange_header(file, buffer, buflen, _("<file type>"), tok);
158 file->type = str2filetype(tok);
160 switch (file->type) {
162 tok = strtok_r(NULL, " ", &saveptr);
163 if ((tok == NULL) || (strcmp(tok, "DATE") != 0)) {
164 strange_header(file, buffer, buflen, "DATE", tok);
168 tok = strtok_r(NULL, " ", &saveptr);
170 strange_header(file, buffer, buflen, _("<date stamp>"), tok);
173 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
175 tok = strtok_r(NULL, " ", &saveptr);
176 if ((tok == NULL) || (strcmp(tok, "TAPE") != 0)) {
177 strange_header(file, buffer, buflen, "TAPE", tok);
181 tok = strtok_r(NULL, " ", &saveptr);
183 strange_header(file, buffer, buflen, _("<file type>"), tok);
186 strncpy(file->name, tok, SIZEOF(file->name) - 1);
190 case F_CONT_DUMPFILE:
191 case F_SPLIT_DUMPFILE:
192 tok = strtok_r(NULL, " ", &saveptr);
194 strange_header(file, buffer, buflen, _("<date stamp>"), tok);
197 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
199 tok = strtok_r(NULL, " ", &saveptr);
201 strange_header(file, buffer, buflen, _("<file name>"), tok);
204 strncpy(file->name, tok, SIZEOF(file->name) - 1);
206 tok = strquotedstr(&saveptr);
208 strange_header(file, buffer, buflen, _("<disk name>"), tok);
211 uqname = unquote_string(tok);
212 strncpy(file->disk, uqname, SIZEOF(file->disk) - 1);
215 if(file->type == F_SPLIT_DUMPFILE) {
216 tok = strtok_r(NULL, " ", &saveptr);
217 if (tok == NULL || strcmp(tok, "part") != 0) {
218 strange_header(file, buffer, buflen, "part", tok);
222 tok = strtok_r(NULL, "/", &saveptr);
223 if ((tok == NULL) || (sscanf(tok, "%d", &file->partnum) != 1)) {
224 strange_header(file, buffer, buflen, _("<part num param>"), tok);
228 /* If totalparts == -1, then the original dump was done in
229 streaming mode (no holding disk), thus we don't know how
230 many parts there are. */
231 tok = strtok_r(NULL, " ", &saveptr);
232 if((tok == NULL) || (sscanf(tok, "%d", &file->totalparts) != 1)) {
233 strange_header(file, buffer, buflen, _("<total parts param>"), tok);
238 tok = strtok_r(NULL, " ", &saveptr);
239 if ((tok == NULL) || (strcmp(tok, "lev") != 0)) {
240 strange_header(file, buffer, buflen, "lev", tok);
244 tok = strtok_r(NULL, " ", &saveptr);
245 if ((tok == NULL) || (sscanf(tok, "%d", &file->dumplevel) != 1)) {
246 strange_header(file, buffer, buflen, _("<dump level param>"), tok);
250 tok = strtok_r(NULL, " ", &saveptr);
251 if ((tok == NULL) || (strcmp(tok, "comp") != 0)) {
252 strange_header(file, buffer, buflen, "comp", tok);
256 tok = strtok_r(NULL, " ", &saveptr);
258 strange_header(file, buffer, buflen, _("<comp param>"), tok);
261 strncpy(file->comp_suffix, tok, SIZEOF(file->comp_suffix) - 1);
263 file->compressed = strcmp(file->comp_suffix, "N");
264 /* compatibility with pre-2.2 amanda */
265 if (strcmp(file->comp_suffix, "C") == 0)
266 strncpy(file->comp_suffix, ".Z", SIZEOF(file->comp_suffix) - 1);
268 tok = strtok_r(NULL, " ", &saveptr);
269 /* "program" is optional */
270 if (tok == NULL || strcmp(tok, "program") != 0) {
276 tok = strtok_r(NULL, " ", &saveptr);
278 strange_header(file, buffer, buflen, _("<program name>"), tok);
281 strncpy(file->program, tok, SIZEOF(file->program) - 1);
282 if (file->program[0] == '\0')
283 strncpy(file->program, "RESTORE", SIZEOF(file->program) - 1);
285 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
286 break; /* reached the end of the buffer */
288 /* "encryption" is optional */
289 if (BSTRNCMP(tok, "crypt") == 0) {
290 tok = strtok_r(NULL, " ", &saveptr);
292 strange_header(file, buffer, buflen, _("<crypt param>"), tok);
295 strncpy(file->encrypt_suffix, tok,
296 SIZEOF(file->encrypt_suffix) - 1);
297 file->encrypted = BSTRNCMP(file->encrypt_suffix, "N");
298 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
302 /* "srvcompprog" is optional */
303 if (BSTRNCMP(tok, "server_custom_compress") == 0) {
304 tok = strtok_r(NULL, " ", &saveptr);
306 strange_header(file, buffer, buflen,
307 _("<server custom compress param>"), tok);
310 strncpy(file->srvcompprog, tok, SIZEOF(file->srvcompprog) - 1);
311 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
315 /* "clntcompprog" is optional */
316 if (BSTRNCMP(tok, "client_custom_compress") == 0) {
317 tok = strtok_r(NULL, " ", &saveptr);
319 strange_header(file, buffer, buflen,
320 _("<client custom compress param>"), tok);
323 strncpy(file->clntcompprog, tok, SIZEOF(file->clntcompprog) - 1);
324 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
328 /* "srv_encrypt" is optional */
329 if (BSTRNCMP(tok, "server_encrypt") == 0) {
330 tok = strtok_r(NULL, " ", &saveptr);
332 strange_header(file, buffer, buflen,
333 _("<server encrypt param>"), tok);
336 strncpy(file->srv_encrypt, tok, SIZEOF(file->srv_encrypt) - 1);
337 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
341 /* "clnt_encrypt" is optional */
342 if (BSTRNCMP(tok, "client_encrypt") == 0) {
343 tok = strtok_r(NULL, " ", &saveptr);
345 strange_header(file, buffer, buflen,
346 _("<client encrypt param>"), tok);
349 strncpy(file->clnt_encrypt, tok, SIZEOF(file->clnt_encrypt) - 1);
350 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
354 /* "srv_decrypt_opt" is optional */
355 if (BSTRNCMP(tok, "server_decrypt_option") == 0) {
356 tok = strtok_r(NULL, " ", &saveptr);
358 strange_header(file, buffer, buflen,
359 _("<server decrypt param>"), tok);
362 strncpy(file->srv_decrypt_opt, tok,
363 SIZEOF(file->srv_decrypt_opt) - 1);
364 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
368 /* "clnt_decrypt_opt" is optional */
369 if (BSTRNCMP(tok, "client_decrypt_option") == 0) {
370 tok = strtok_r(NULL, " ", &saveptr);
372 strange_header(file, buffer, buflen,
373 _("<client decrypt param>"), tok);
376 strncpy(file->clnt_decrypt_opt, tok,
377 SIZEOF(file->clnt_decrypt_opt) - 1);
378 if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
385 tok = strtok_r(NULL, " ", &saveptr);
386 /* DATE is optional */
388 if (strcmp(tok, "DATE") == 0) {
389 tok = strtok_r(NULL, " ", &saveptr);
391 file->datestamp[0] = '\0';
393 strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
395 strange_header(file, buffer, buflen, _("<DATE>"), tok);
398 file->datestamp[0] = '\0';
403 strange_header(file, buffer, buflen,
404 _("TAPESTART|DUMPFILE|CONT_DUMPFILE|SPLIT_DUMPFILE|TAPEEND"), tok);
408 (void)strtok_r(buf, "\n", &saveptr); /* this is the first line */
409 /* iterate through the rest of the lines */
410 while ((line = strtok_r(NULL, "\n", &saveptr)) != NULL) {
411 #define SC "CONT_FILENAME="
412 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
413 line += SIZEOF(SC) - 1;
414 strncpy(file->cont_filename, line,
415 SIZEOF(file->cont_filename) - 1);
420 #define SC "PARTIAL="
421 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
422 line += SIZEOF(SC) - 1;
423 file->is_partial = !strcasecmp(line, "yes");
427 #define SC "APPLICATION="
428 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
429 line += SIZEOF(SC) - 1;
430 strncpy(file->application, line,
431 SIZEOF(file->application) - 1);
437 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
438 line += SIZEOF(SC) - 1;
439 file->dle_str = parse_heredoc(line, &saveptr);
443 #define SC _("To restore, position tape at start of file and run:")
444 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0)
448 #define SC "\tdd if=<tape> "
449 if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
450 char *cmd1, *cmd2, *cmd3=NULL;
452 /* skip over dd command */
453 if ((cmd1 = strchr(line, '|')) == NULL) {
455 strncpy(file->recover_cmd, "BUG",
456 SIZEOF(file->recover_cmd) - 1);
461 /* block out first pipeline command */
462 if ((cmd2 = strchr(cmd1, '|')) != NULL) {
464 if ((cmd3 = strchr(cmd2, '|')) != NULL)
468 /* clean up some extra spaces in various fields */
473 /* three cmds: decrypt | uncompress | recover
474 * two cmds: uncompress | recover
475 * XXX note that if there are two cmds, the first one
476 * XXX could be either uncompress or decrypt. Since no
477 * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok
478 * XXX for header information.
484 strncpy(file->recover_cmd, cmd1,
485 SIZEOF(file->recover_cmd) - 1);
487 g_snprintf(file->uncompress_cmd,
488 SIZEOF(file->uncompress_cmd), "%s |", cmd1);
489 strncpy(file->recover_cmd, cmd2,
490 SIZEOF(file->recover_cmd) - 1);
492 } else { /* cmd3 presents: decrypt | uncompress | recover */
493 g_snprintf(file->decrypt_cmd,
494 SIZEOF(file->decrypt_cmd), "%s |", cmd1);
495 g_snprintf(file->uncompress_cmd,
496 SIZEOF(file->uncompress_cmd), "%s |", cmd2);
497 strncpy(file->recover_cmd, cmd3,
498 SIZEOF(file->recover_cmd) - 1);
503 /* XXX complain about weird lines? */
513 const dumpfile_t *file)
515 dbprintf(_("Contents of *(dumpfile_t *)%p:\n"), file);
516 dbprintf(_(" type = %d (%s)\n"),
517 file->type, filetype2str(file->type));
518 dbprintf(_(" datestamp = '%s'\n"), file->datestamp);
519 dbprintf(_(" dumplevel = %d\n"), file->dumplevel);
520 dbprintf(_(" compressed = %d\n"), file->compressed);
521 dbprintf(_(" encrypted = %d\n"), file->encrypted);
522 dbprintf(_(" comp_suffix = '%s'\n"), file->comp_suffix);
523 dbprintf(_(" encrypt_suffix = '%s'\n"), file->encrypt_suffix);
524 dbprintf(_(" name = '%s'\n"), file->name);
525 dbprintf(_(" disk = '%s'\n"), file->disk);
526 dbprintf(_(" program = '%s'\n"), file->program);
527 dbprintf(_(" application = '%s'\n"), file->application);
528 dbprintf(_(" srvcompprog = '%s'\n"), file->srvcompprog);
529 dbprintf(_(" clntcompprog = '%s'\n"), file->clntcompprog);
530 dbprintf(_(" srv_encrypt = '%s'\n"), file->srv_encrypt);
531 dbprintf(_(" clnt_encrypt = '%s'\n"), file->clnt_encrypt);
532 dbprintf(_(" recover_cmd = '%s'\n"), file->recover_cmd);
533 dbprintf(_(" uncompress_cmd = '%s'\n"), file->uncompress_cmd);
534 dbprintf(_(" encrypt_cmd = '%s'\n"), file->encrypt_cmd);
535 dbprintf(_(" decrypt_cmd = '%s'\n"), file->decrypt_cmd);
536 dbprintf(_(" srv_decrypt_opt = '%s'\n"), file->srv_decrypt_opt);
537 dbprintf(_(" clnt_decrypt_opt = '%s'\n"), file->clnt_decrypt_opt);
538 dbprintf(_(" cont_filename = '%s'\n"), file->cont_filename);
540 dbprintf(_(" dle_str = %s\n"), file->dle_str);
542 dbprintf(_(" dle_str = (null)\n"));
543 dbprintf(_(" is_partial = %d\n"), file->is_partial);
544 dbprintf(_(" partnum = %d\n"), file->partnum);
545 dbprintf(_(" totalparts = %d\n"), file->totalparts);
547 dbprintf(_(" blocksize = %zu\n"), file->blocksize);
554 if (strlen(name) == 0) {
555 error(_("Invalid name '%s'\n"), name);
562 const char *datestamp)
564 if (strcmp(datestamp, "X") == 0) {
568 if ((strlen(datestamp) == 8) && match("^[0-9]{8}$", datestamp)) {
571 if ((strlen(datestamp) == 14) && match("^[0-9]{14}$", datestamp)) {
574 error(_("Invalid datestamp '%s'\n"), datestamp);
581 const int totalparts)
584 error(_("Invalid partnum (%d)\n"), partnum);
588 if (partnum > totalparts && totalparts >= 0) {
589 error(_("Invalid partnum (%d) > totalparts (%d)\n"),
590 partnum, totalparts);
596 build_header(const dumpfile_t * file, size_t size)
598 GString *rval, *split_data;
602 dbprintf(_("Building type %d (%s) header of size %zu using:\n"),
603 file->type, filetype2str(file->type), size);
604 dump_dumpfile_t(file);
606 rval = g_string_sized_new(size);
607 split_data = g_string_sized_new(10);
609 switch (file->type) {
611 validate_name(file->name);
612 validate_datestamp(file->datestamp);
613 g_string_printf(rval,
614 "AMANDA: TAPESTART DATE %s TAPE %s\n\014\n",
615 file->datestamp, file->name);
618 case F_SPLIT_DUMPFILE:
619 validate_parts(file->partnum, file->totalparts);
620 g_string_printf(split_data,
621 " part %d/%d ", file->partnum, file->totalparts);
625 case F_CONT_DUMPFILE:
627 validate_name(file->name);
628 validate_datestamp(file->datestamp);
629 qname = quote_string(file->disk);
630 program = stralloc(file->program);
631 if (match("^.*[.][Ee][Xx][Ee]$", program)) {
632 /* Trim ".exe" from program name */
633 program[strlen(program) - strlen(".exe")] = '\0';
635 g_string_printf(rval,
636 "AMANDA: %s %s %s %s %s lev %d comp %s program %s",
637 filetype2str(file->type),
638 file->datestamp, file->name, qname,
640 file->dumplevel, file->comp_suffix, program);
644 /* only output crypt if it's enabled */
645 if (strcmp(file->encrypt_suffix, "enc") == 0) {
646 g_string_append_printf(rval, " crypt %s", file->encrypt_suffix);
649 if (*file->srvcompprog) {
650 g_string_append_printf(rval, " server_custom_compress %s",
652 } else if (*file->clntcompprog) {
653 g_string_append_printf(rval, " client_custom_compress %s",
657 if (*file->srv_encrypt) {
658 g_string_append_printf(rval, " server_encrypt %s",
660 } else if (*file->clnt_encrypt) {
661 g_string_append_printf(rval, " client_encrypt %s",
665 if (*file->srv_decrypt_opt) {
666 g_string_append_printf(rval, " server_decrypt_option %s",
667 file->srv_decrypt_opt);
668 } else if (*file->clnt_decrypt_opt) {
669 g_string_append_printf(rval, " client_decrypt_option %s",
670 file->clnt_decrypt_opt);
673 g_string_append_printf(rval, "\n");
675 if (file->cont_filename[0] != '\0') {
676 g_string_append_printf(rval, "CONT_FILENAME=%s\n",
677 file->cont_filename);
679 if (file->application[0] != '\0') {
680 g_string_append_printf(rval, "APPLICATION=%s\n", file->application);
682 if (file->is_partial != 0) {
683 g_string_append_printf(rval, "PARTIAL=YES\n");
685 if (file->dle_str && strlen(file->dle_str) < size-2048) {
686 char *heredoc = quote_heredoc(file->dle_str, "ENDDLE");
687 g_string_append_printf(rval, "DLE=%s\n", heredoc);
691 g_string_append_printf(rval,
692 _("To restore, position tape at start of file and run:\n"));
694 g_string_append_printf(rval, "\tdd if=<tape> ");
696 g_string_append_printf(rval, "bs=%zuk ",
697 file->blocksize / 1024);
698 g_string_append_printf(rval, "skip=1 | ");
699 if (*file->decrypt_cmd)
700 g_string_append_printf(rval, "%s ", file->decrypt_cmd);
701 if (*file->uncompress_cmd)
702 g_string_append_printf(rval, "%s ", file->uncompress_cmd);
703 if (*file->recover_cmd)
704 g_string_append_printf(rval, "%s ", file->recover_cmd);
705 /* \014 == ^L == form feed */
706 g_string_append_printf(rval, "\n\014\n");
710 validate_datestamp(file->datestamp);
711 g_string_printf(rval, "AMANDA: TAPEEND DATE %s\n\014\n",
719 error(_("Invalid header type: %d (%s)"),
720 file->type, filetype2str(file->type));
724 g_string_free(split_data, TRUE);
726 /* Since we don't return the length, it is an error for the header to be
727 * more than 'size' bytes */
728 assert(rval->len <= size);
729 /* Clear extra bytes. */
730 if (rval->len < size) {
731 bzero(rval->str + rval->len, rval->allocated_len - rval->len);
733 return g_string_free(rval, FALSE);
737 * Prints the contents of the file structure.
742 const dumpfile_t * file)
745 char number[NUM_STR_SIZE*2];
749 g_fprintf(outf, _("EMPTY file\n"));
753 g_fprintf(outf, _("UNKNOWN file\n"));
757 g_fprintf(outf, _("WEIRD file\n"));
761 g_fprintf(outf, _("start of tape: date %s label %s\n"),
762 file->datestamp, file->name);
766 case F_CONT_DUMPFILE:
767 qdisk = quote_string(file->disk);
768 g_fprintf(outf, "%s: date %s host %s disk %s lev %d comp %s",
769 filetype2str(file->type), file->datestamp, file->name,
770 qdisk, file->dumplevel, file->comp_suffix);
772 g_fprintf(outf, " program %s", file->program);
773 if (strcmp(file->encrypt_suffix, "enc") == 0)
774 g_fprintf(outf, " crypt %s", file->encrypt_suffix);
775 if (*file->srvcompprog)
776 g_fprintf(outf, " server_custom_compress %s", file->srvcompprog);
777 if (*file->clntcompprog)
778 g_fprintf(outf, " client_custom_compress %s", file->clntcompprog);
779 if (*file->srv_encrypt)
780 g_fprintf(outf, " server_encrypt %s", file->srv_encrypt);
781 if (*file->clnt_encrypt)
782 g_fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
783 if (*file->srv_decrypt_opt)
784 g_fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
785 if (*file->clnt_decrypt_opt)
786 g_fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
787 g_fprintf(outf, "\n");
791 case F_SPLIT_DUMPFILE:
792 if(file->totalparts > 0){
793 g_snprintf(number, SIZEOF(number), "%d", file->totalparts);
795 else g_snprintf(number, SIZEOF(number), "UNKNOWN");
796 qdisk = quote_string(file->disk);
797 g_fprintf(outf, "split dumpfile: date %s host %s disk %s part %d/%s lev %d comp %s",
798 file->datestamp, file->name, qdisk, file->partnum,
799 number, file->dumplevel, file->comp_suffix);
801 g_fprintf(outf, " program %s",file->program);
802 if (strcmp(file->encrypt_suffix, "enc") == 0)
803 g_fprintf(outf, " crypt %s", file->encrypt_suffix);
804 if (*file->srvcompprog)
805 g_fprintf(outf, " server_custom_compress %s", file->srvcompprog);
806 if (*file->clntcompprog)
807 g_fprintf(outf, " client_custom_compress %s", file->clntcompprog);
808 if (*file->srv_encrypt)
809 g_fprintf(outf, " server_encrypt %s", file->srv_encrypt);
810 if (*file->clnt_encrypt)
811 g_fprintf(outf, " client_encrypt %s", file->clnt_encrypt);
812 if (*file->srv_decrypt_opt)
813 g_fprintf(outf, " server_decrypt_option %s", file->srv_decrypt_opt);
814 if (*file->clnt_decrypt_opt)
815 g_fprintf(outf, " client_decrypt_option %s", file->clnt_decrypt_opt);
816 g_fprintf(outf, "\n");
821 g_fprintf(outf, "end of tape: date %s\n", file->datestamp);
828 const dumpfile_t * file)
830 if(strcmp(file->comp_suffix, ".Z") == 0)
833 if(strcmp(file->comp_suffix, ".gz") == 0)
836 if(strcmp(file->comp_suffix, "cust") == 0)
841 static const struct {
845 { F_UNKNOWN, "UNKNOWN" },
846 { F_WEIRD, "WEIRD" },
847 { F_TAPESTART, "TAPESTART" },
848 { F_TAPEEND, "TAPEEND" },
849 { F_DUMPFILE, "FILE" },
850 { F_CONT_DUMPFILE, "CONT_FILE" },
851 { F_SPLIT_DUMPFILE, "SPLIT_FILE" }
853 #define NFILETYPES (size_t)(sizeof(filetypetab) / sizeof(filetypetab[0]))
861 for (i = 0; i < (int)NFILETYPES; i++)
862 if (filetypetab[i].type == type)
863 return (filetypetab[i].str);
873 for (i = 0; i < (int)NFILETYPES; i++)
874 if (strcmp(filetypetab[i].str, str) == 0)
875 return (filetypetab[i].type);
879 gboolean headers_are_equal(dumpfile_t * a, dumpfile_t * b) {
880 if (a == NULL && b == NULL)
883 if (a == NULL || b == NULL)
886 if (a->type != b->type) return FALSE;
887 if (strcmp(a->datestamp, b->datestamp)) return FALSE;
888 if (a->dumplevel != b->dumplevel) return FALSE;
889 if (a->compressed != b->compressed) return FALSE;
890 if (a->encrypted != b->encrypted) return FALSE;
891 if (strcmp(a->comp_suffix, b->comp_suffix)) return FALSE;
892 if (strcmp(a->encrypt_suffix, b->encrypt_suffix)) return FALSE;
893 if (strcmp(a->name, b->name)) return FALSE;
894 if (strcmp(a->disk, b->disk)) return FALSE;
895 if (strcmp(a->program, b->program)) return FALSE;
896 if (strcmp(a->application, b->application)) return FALSE;
897 if (strcmp(a->srvcompprog, b->srvcompprog)) return FALSE;
898 if (strcmp(a->clntcompprog, b->clntcompprog)) return FALSE;
899 if (strcmp(a->srv_encrypt, b->srv_encrypt)) return FALSE;
900 if (strcmp(a->clnt_encrypt, b->clnt_encrypt)) return FALSE;
901 if (strcmp(a->recover_cmd, b->recover_cmd)) return FALSE;
902 if (strcmp(a->uncompress_cmd, b->uncompress_cmd)) return FALSE;
903 if (strcmp(a->encrypt_cmd, b->encrypt_cmd)) return FALSE;
904 if (strcmp(a->decrypt_cmd, b->decrypt_cmd)) return FALSE;
905 if (strcmp(a->srv_decrypt_opt, b->srv_decrypt_opt)) return FALSE;
906 if (strcmp(a->clnt_decrypt_opt, b->clnt_decrypt_opt)) return FALSE;
907 if (strcmp(a->cont_filename, b->cont_filename)) return FALSE;
908 if (a->dle_str != b->dle_str && a->dle_str && b->dle_str
909 && strcmp(a->dle_str, b->dle_str)) return FALSE;
910 if (a->is_partial != b->is_partial) return FALSE;
911 if (a->partnum != b->partnum) return FALSE;
912 if (a->totalparts != b->totalparts) return FALSE;
913 if (a->blocksize != b->blocksize) return FALSE;
915 return TRUE; /* ok, they're the same */
918 dumpfile_t * dumpfile_copy(dumpfile_t* source) {
919 dumpfile_t* rval = malloc(sizeof(dumpfile_t));
920 memcpy(rval, source, sizeof(dumpfile_t));
921 if (rval->dle_str) rval->dle_str = stralloc(rval->dle_str);
926 dumpfile_copy_in_place(
930 memcpy(dest, source, sizeof(dumpfile_t));
931 if (dest->dle_str) dest->dle_str = stralloc(dest->dle_str);
934 void dumpfile_free_data(dumpfile_t* info) {
936 amfree(info->dle_str);
940 void dumpfile_free(dumpfile_t* info) {
941 dumpfile_free_data(info);
952 FILE *stream = popen("od -c -x -", "w");
954 if (stream != NULL) {
956 rc = (ssize_t)fwrite(buffer, len, 1, stream);
964 static char *quote_heredoc(
966 char *delimiter_prefix)
968 char *delimiter = stralloc(delimiter_prefix);
970 int delimiter_len = strlen(delimiter);
973 /* keep picking delimiters until we find one that's not a line in TEXT */
977 gboolean found_delimiter = FALSE;
980 if (*c == '\n' || *c == '\0') {
981 int linelen = c - line;
982 if (linelen == delimiter_len && 0 == strncmp(line, delimiter, linelen)) {
983 found_delimiter = TRUE;
992 if (!found_delimiter)
995 delimiter = newvstrallocf(delimiter, "%s%d", delimiter_prefix, ++delimiter_n);
996 delimiter_len = strlen(delimiter);
999 /* we have a delimiter .. now use it */
1000 quoted = vstrallocf("<<%s\n%s\n%s", delimiter, text, delimiter);
1005 static char *parse_heredoc(
1009 char *result = NULL;
1011 if (strncmp(line, "<<", 2) == 0) {
1012 char *keyword = line+2;
1015 while((new_line = strtok_r(NULL, "\n", saveptr)) != NULL &&
1016 strcmp(new_line, keyword) != 0) {
1017 result = vstrextend(&result, new_line, "\n", NULL);
1019 /* remove latest '\n' */
1020 if (strlen(result) > 0)
1021 result[strlen(result)-1] = '\0';
1023 result = stralloc(line);