source: branches/stable/mondo/src/common/libmondo-filelist.c @ 1123

Last change on this file since 1123 was 1123, checked in by Bruno Cornec, 13 years ago

Again linker fixes (hopefully last time for now)

  • Property svn:keywords set to Id
File size: 48.2 KB
Line 
1/* libmondo-filelist.c
2   $Id: libmondo-filelist.c 1123 2007-02-09 22:41:03Z bruno $
3*/
4
5/**
6 * @file
7 * Functions which create, chop, and edit the filelist.
8 */
9
10#include "my-stuff.h"
11#include "mondostructures.h"
12#include "libmondo-filelist.h"
13#include "libmondo-string-EXT.h"
14#include "libmondo-files-EXT.h"
15#include "libmondo-fork-EXT.h"
16#include "newt-specific-EXT.h"
17#include "libmondo-tools-EXT.h"
18#include "mr_str.h"
19#include "mr_mem.h"
20#include "mr_msg.h"
21
22#include <time.h>
23#include <stdio.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <dirent.h>
27#include <errno.h>
28#include <stdio.h>
29
30
31/**
32 * The maximum depth of directories to put in the skeleton filelist.
33 * This is a balance between performance and a good progress indicator.
34 */
35#define MAX_SKEL_DEPTH 3
36
37
38extern ssize_t getline(char **lineptr, size_t * n, FILE * stream);
39
40
41int mondo_makefilelist(char *logfile, char *tmpdir, char *scratchdir,
42                       char *include_paths, char *excp, int differential,
43                       char *userdef_filelist);
44
45
46/*@unused@*/
47//static char cvsid[] = "$Id: libmondo-filelist.c 1123 2007-02-09 22:41:03Z bruno $";
48
49/**
50 * Number of lines in the filelist last loaded.
51 * @warning This implies that two filesets cannot be loaded at once.
52 * @ingroup globalGroup
53 */
54long g_original_noof_lines_in_filelist = 0;
55
56/**
57 * Number of filesets in the current backup.
58 * @ingroup globalGroup
59 */
60long g_noof_sets = 0;
61
62extern bool g_text_mode;
63extern newtComponent g_progressForm;
64extern int g_currentY;
65extern int g_noof_rows;
66
67extern char *g_getfacl;
68extern char *g_getfattr;
69
70/**
71 * @addtogroup filelistGroup
72 * @{
73 */
74/**
75 * Call chop_filelist() to chop the filelist into sets.
76 * @param bkpinfo The backup information structure. Fields used:
77 * - @c bkpinfo->image_devs
78 * - @c bkpinfo->optimal_set_size
79 * - @c bkpinfo->scratchdir
80 * - @c bkpinfo->tmpdir
81 * @see chop_filelist
82 */
83int call_filelist_chopper(struct s_bkpinfo *bkpinfo)
84{
85    /*@ buffers *********************** */
86    char *dev;
87    char *filelist;
88    char *tempfile;
89    long noof_sets;
90
91    /*@ pointers ********************** */
92    char *ptr;
93    FILE *fout;
94
95    /*@ int *************************** */
96    int i, retval = 0;
97
98    mvaddstr_and_log_it(g_currentY, 0, _("Dividing filelist into sets"));
99
100    log_to_screen(_("Dividing filelist into sets. Please wait."));
101    i = 0;
102    mr_asprintf(&filelist, "%s/archives/filelist.full", bkpinfo->scratchdir);
103    if (!does_file_exist(filelist)) {
104        log_it("filelist %s not found", filelist);
105        fatal_error("call_filelist_chopper() -- filelist not found!");
106    }
107
108    noof_sets =
109        chop_filelist(filelist, bkpinfo->tmpdir,
110                      bkpinfo->optimal_set_size);
111    mr_free(filelist);
112
113    estimate_noof_media_required(bkpinfo, noof_sets);   // for cosmetic purposes
114
115    mr_asprintf(&tempfile, "%s/biggielist.txt", bkpinfo->tmpdir);
116    if (!(fout = fopen(tempfile, "a"))) {
117        log_OS_error("Cannot append to biggielist");
118        retval++;
119    }
120    mr_free(tempfile);
121    log_it(bkpinfo->image_devs);
122
123    ptr = bkpinfo->image_devs;
124
125    while (ptr && *ptr) {
126        mr_asprintf(&dev, ptr);
127        log_it("Examining imagedev %s", dev);
128        for (i = 0; i < (int) strlen(dev) && dev[i] != ' '; i++);
129        dev[i] = '\0';
130        if (!strlen(dev)) {
131            continue;
132        }
133        fprintf(fout, "%s\n", dev);
134        log_it("Adding '%s' to biggielist", dev);
135        mr_free(dev);
136
137        if ((ptr = strchr(ptr, ' '))) {
138            ptr++;
139        }
140    }
141    paranoid_fclose(fout);
142    mvaddstr_and_log_it(g_currentY++, 74, "Done.");
143
144    return (retval);
145}
146
147
148
149int sort_file(char *orig_fname)
150{
151    char *tmp_fname;
152    char *command;
153    int retval = 0;
154
155    mr_msg(1, "Sorting file %s", orig_fname);
156    mr_asprintf(&tmp_fname, "/tmp/sort.%d.%d.%d", (int) (random() % 32768),
157             (int) (random() % 32768), (int) (random() % 32768));
158
159    if (!does_file_exist(orig_fname)) {
160        return (0);
161    }                           // no sense in trying to sort an empty file
162
163    mr_asprintf(&command, "sort %s > %s 2>> %s", orig_fname, tmp_fname,
164             MONDO_LOGFILE);
165    retval = system(command);
166    mr_free(command);
167
168    if (retval) {
169        mr_msg(2, "Failed to sort %s - oh dear", orig_fname);
170    } else {
171        mr_msg(2, "Sorted %s --> %s OK. Copying it back to %s now",
172                orig_fname, tmp_fname, orig_fname);
173        mr_asprintf(&command, "mv -f %s %s", tmp_fname, orig_fname);
174        retval += run_program_and_log_output(command, 2);
175        mr_free(command);
176
177        if (retval) {
178            mr_msg(2, "Failed to copy %s back to %s - oh dear", tmp_fname,
179                    orig_fname);
180        } else {
181            mr_msg(2, "%s was sorted OK.", orig_fname);
182        }
183    }
184    mr_free(tmp_fname);
185    mr_msg(1, "Finished sorting file %s", orig_fname);
186    return (retval);
187}
188
189
190/**
191 * Chop the filelist into sets.
192 * Each fileset is a list of files whose total (uncompressed) size is usually
193 * about X KB. Files bigger than 8X KB are placed in a "biggielist"; they will
194 * be sliced and compressed separately from the regular files.
195 *
196 * @param filelist The big filelist (filelist.full) to chop up.
197 * @param outdir The directory to place the files (filelist.N where N is
198 * an integer, biggielist.txt, and LAST-FILELIST-NUMBER) created
199 * @param maxsetsizeK Optimal size of a fileset (X above).
200 * @return number of errors encountered (0 for success).
201 */
202int chop_filelist(char *filelist, char *outdir, long maxsetsizeK)
203{
204/*@ long ****************************************/
205    long lino = 0;
206    long max_sane_size_for_a_file;
207    long curr_set_size;
208    long noof_lines;
209    long siz;
210
211    /*@ int **************************************** */
212    int i;
213    size_t n = 0;
214    long curr_set_no;
215
216    /*@ buffers ************************************* */
217    char *outfname;
218    char *biggie_fname;
219    char *incoming = NULL;
220    char *tmp;
221
222    /*@ pointers *********************************** */
223    FILE *fin;
224    FILE *fout;
225    FILE *fbig;
226
227    /*@ structures ********************************* */
228    struct stat buf;
229    int err = 0;
230
231
232    assert_string_is_neither_NULL_nor_zerolength(filelist);
233    assert_string_is_neither_NULL_nor_zerolength(outdir);
234    assert(maxsetsizeK > 0);
235
236    max_sane_size_for_a_file = 32L * 1024L;
237// max_sane_size_for_a_file = maxsetsizeK*2;
238//  if (max_sane_size_for_a_file > 32*1024)
239//    { max_sane_size_for_a_file = 32*1024; }
240
241    log_it("filelist=%s;", filelist);
242    open_evalcall_form("Dividing filelist into sets");
243    noof_lines = count_lines_in_file(filelist);
244    if (!(fin = fopen(filelist, "r"))) {
245        log_OS_error("Cannot openin filelist");
246        return (0);
247    }
248    curr_set_no = 0;
249    curr_set_size = 0;
250    mr_asprintf(&outfname, "%s/filelist.%ld", outdir, curr_set_no);
251    mr_asprintf(&biggie_fname, "%s/biggielist.txt", outdir);
252    log_it("outfname=%s; biggie_fname=%s", outfname, biggie_fname);
253    if (!(fbig = fopen(biggie_fname, "w"))) {
254        log_OS_error("Cannot openout biggie_fname");
255        err++;
256        goto end_of_func;
257    }
258    if (!(fout = fopen(outfname, "w"))) {
259        log_OS_error("Cannot openout outfname");
260        err++;
261        goto end_of_func;
262    }
263    mr_getline(&incoming, &n, fin);
264    while (!feof(fin)) {
265        lino++;
266        i = strlen(incoming) - 1;
267        if (i < 0) {
268            i = 0;
269        }
270        if (incoming[i] < 32) {
271            incoming[i] = '\0';
272        }
273        if (!strncmp(incoming, "/dev/", 5)) {
274            siz = 1;
275        } else if (lstat(incoming, &buf) != 0) {
276            siz = 0;
277        } else {
278            siz = (long) (buf.st_size >> 10);
279        }
280        if (siz > max_sane_size_for_a_file)
281// && strcmp(incoming+strlen(incoming)-4, ".bz2") && strcmp(incoming+strlen(incoming)-4, ".tbz"))
282        {
283            fprintf(fbig, "%s\n", incoming);
284        } else {
285            curr_set_size += siz;
286            fprintf(fout, "%s\n", incoming);
287            if (curr_set_size > maxsetsizeK) {
288                paranoid_fclose(fout);
289                sort_file(outfname);
290                mr_free(outfname);
291
292                curr_set_no++;
293                curr_set_size = 0;
294                mr_asprintf(&outfname, "%s/filelist.%ld", outdir,
295                         curr_set_no);
296                if (!(fout = fopen(outfname, "w"))) {
297                    log_OS_error("Unable to openout outfname");
298                    err++;
299                    goto end_of_func;
300                }
301                update_evalcall_form((int) (lino * 100 / noof_lines));
302            }
303        }
304        mr_getline(&incoming, &n, fin);
305    }
306    paranoid_fclose(fin);
307    paranoid_fclose(fout);
308    paranoid_fclose(fbig);
309
310    if (length_of_file(outfname) <= 2) {
311        unlink(outfname);
312        g_noof_sets--;
313    }
314    g_noof_sets = curr_set_no;
315    sort_file(outfname);
316    sort_file(biggie_fname);
317    mr_free(biggie_fname);
318    mr_free(outfname);
319
320    mr_asprintf(&outfname, "%s/LAST-FILELIST-NUMBER", outdir);
321    mr_asprintf(&tmp, "%ld", curr_set_no);
322    if (write_one_liner_data_file(outfname, tmp)) {
323        log_OS_error
324            ("Unable to echo write one-liner to LAST-FILELIST-NUMBER");
325        err = 1;
326    }
327    mr_free(outfname);
328    mr_free(tmp);
329
330    if (curr_set_no == 0) {
331        mr_asprintf(&tmp, "Only one fileset. Fine.");
332    } else {
333        mr_asprintf(&tmp, "Filelist divided into %ld sets", curr_set_no + 1);
334    }
335    mr_msg(1, tmp);
336    mr_free(tmp);
337    close_evalcall_form();
338    /* This is to work around an obscure bug in Newt; open a form, close it,
339       carry on... I don't know why it works but it works. If you don't do this
340       then update_progress_form() won't show the "time taken / time remaining"
341       line. The bug only crops up AFTER the call to chop_filelist(). Weird. */
342#ifndef _XWIN
343    if (!g_text_mode) {
344        open_progress_form("", "", "", "", 100);
345        newtPopHelpLine();
346        newtFormDestroy(g_progressForm);
347        newtPopWindow();
348    }
349#endif
350  end_of_func:
351    return (err ? 0 : curr_set_no + 1);
352}
353
354
355/**
356 * Free all the memory used by a filelist structure.
357 * Since this may take a long time for large filelists, a progress bar will be displayed.
358 * @param filelist The filelist to free.
359 */
360void free_filelist(struct s_node *filelist)
361{
362    /*@ int's ******************************************************* */
363    static int depth = 0;
364    int percentage;
365
366    /*@ long's ****************************************************** */
367    static long i = 0;
368
369    /*@ end vars **************************************************** */
370
371    assert(filelist != NULL);
372    if (depth == 0) {
373        open_evalcall_form("Freeing memory");
374        log_to_screen(_("Freeing memory formerly occupied by filelist"));
375    }
376    depth++;
377
378    if (filelist->ch == '\0') {
379        if (!(i++ % 1111)) {
380            percentage =
381                (int) (i * 100 / g_original_noof_lines_in_filelist);
382            update_evalcall_form(percentage);
383
384        }
385    }
386
387    if (filelist->right) {
388        free_filelist(filelist->right);
389        filelist->right = NULL;
390    }
391    if (filelist->down) {
392/*      if (!(i++ %39999)) { update_evalcall_form(0); } */
393        free_filelist(filelist->down);
394        filelist->down = NULL;
395    }
396    filelist->ch = '\0';
397    mr_free(filelist);
398    depth--;
399    if (depth == 0) {
400        close_evalcall_form();
401        log_it("Finished freeing memory");
402    }
403}
404
405
406int call_exe_and_pipe_output_to_fd(char *sys_call, FILE * pout)
407{
408    FILE *pattr;
409    char *tmp = NULL;
410    size_t n = 0;
411
412    pattr = popen(sys_call, "r");
413    if (!pattr) {
414        mr_msg(1, "Failed to open fattr() %s", sys_call);
415        return (1);
416    }
417    if (feof(pattr)) {
418        mr_msg(1, "Failed to call fattr() %s", sys_call);
419        paranoid_pclose(pattr);
420        return (2);
421    }
422    for (mr_getline(&tmp, &n, pattr); !feof(pattr); mr_getline(&tmp, &n, pattr)) {
423        fputs(tmp, pout);
424    }
425    paranoid_pclose(pattr);
426    mr_free(tmp);
427    return (0);
428}
429
430
431int gen_aux_list(char *filelist, char *syscall_sprintf,
432                 char *auxlist_fname)
433{
434    FILE *fin;
435    FILE *pout;
436    char *pout_command;
437    char *sys_call;
438    char *file_to_analyze = NULL;
439    char *strtmp = NULL;
440    int i;
441    size_t n = 0;
442
443    if (!(fin = fopen(filelist, "r"))) {
444        mr_msg(1, "Cannot openin filelist %s", filelist);
445        return (1);
446    }
447    mr_asprintf(&pout_command, "gzip -c1 > %s", auxlist_fname);
448    if (!(pout = popen(pout_command, "w"))) {
449        mr_msg(1, "Cannot openout auxlist_fname %s", auxlist_fname);
450        fclose(fin);
451        mr_free(pout_command);
452        return (4);
453    }
454    mr_free(pout_command);
455
456    for (mr_getline(&file_to_analyze, &n, fin); !feof(fin);
457         mr_getline(&file_to_analyze, &n, fin)) {
458        i = strlen(file_to_analyze);
459        if (i > 0 && file_to_analyze[i - 1] < 32) {
460            file_to_analyze[i - 1] = '\0';
461        }
462        mr_msg(8, "Analyzing %s", file_to_analyze);
463        /* BERLIOS : to be checked */
464        mr_asprintf(&strtmp, syscall_sprintf, mr_stresc(file_to_analyze, "`$\\\"", '\\'));
465        mr_asprintf(&sys_call, "%s 2>> /dev/null", strtmp);
466        mr_free(strtmp);
467        call_exe_and_pipe_output_to_fd(sys_call, pout);
468        mr_free(sys_call);
469    }
470    paranoid_fclose(fin);
471    paranoid_pclose(pout);
472    mr_free(file_to_analyze);
473    return (0);
474}
475
476
477int get_acl_list(char *filelist, char *facl_fname)
478{
479    char *command = NULL;
480    int retval = 0;
481
482    if (g_getfacl != NULL) {
483        mr_asprintf(&command, "touch %s", facl_fname);
484        run_program_and_log_output(command, 8);
485        mr_free(command);
486
487        mr_asprintf(&command,
488                 "getfacl --all-effective -P %s 2>> %s | gzip -c1 > %s 2>> %s",
489                 filelist, MONDO_LOGFILE, facl_fname, MONDO_LOGFILE);
490        iamhere(command);
491        retval = system(command);
492        mr_free(command);
493    }
494    return (retval);
495}
496
497
498int get_fattr_list(char *filelist, char *fattr_fname)
499{
500    char *command = NULL;
501    int retval = 0;
502
503    if (g_getfattr != NULL) {
504        mr_asprintf(&command, "touch %s", fattr_fname);
505        run_program_and_log_output(command, 8);
506        mr_free(command);
507
508        retval =
509            gen_aux_list(filelist, "getfattr --en=hex -P -d \"%s\"",
510                         fattr_fname);
511    }
512    return (retval);
513}
514
515
516int set_EXAT_list(char *orig_msklist, char *original_exat_fname,
517                  char *executable)
518{
519    const int my_depth = 8;
520    char *command, *syscall_pin, *syscall_pout;
521    char *incoming = NULL;
522    char *current_subset_file = NULL;
523    char *current_master_file, *masklist;
524    int retval = 0;
525    int i;
526    size_t n = 0;
527    char *p, *q;
528    FILE *pin, *pout, *faclin;
529
530    mr_msg(1, "set_EXAT_list(%s, %s, %s)", orig_msklist,
531            original_exat_fname, executable);
532    if (!orig_msklist || !orig_msklist[0]
533        || !does_file_exist(orig_msklist)) {
534        mr_msg(1,
535                "No masklist provided. I shall therefore set ALL attributes.");
536        mr_asprintf(&command, "gzip -dc %s | %s --restore - 2>> %s",
537                 original_exat_fname, executable, MONDO_LOGFILE);
538        mr_msg(1, "command = %s", command);
539        retval = system(command);
540        mr_free(command);
541        mr_msg(1, "Returning w/ retval=%d", retval);
542        return (retval);
543    }
544    if (length_of_file(original_exat_fname) <= 0) {
545        mr_msg(1,
546                "original_exat_fname %s is empty or missing, so no need to set EXAT list",
547                original_exat_fname);
548        return (0);
549    }
550    mr_asprintf(&masklist, "/tmp/%d.%d.mask", (int) (random() % 32768),
551             (int) (random() % 32768));
552
553    mr_asprintf(&command, "cp -f %s %s", orig_msklist, masklist);
554    run_program_and_log_output(command, 1);
555    mr_free(command);
556
557    sort_file(masklist);
558    mr_asprintf(&syscall_pin, "gzip -dc %s", original_exat_fname);
559    mr_asprintf(&syscall_pout, "%s --restore - 2>> %s", executable,
560             MONDO_LOGFILE);
561
562    mr_msg(1, "syscall_pin = %s", syscall_pin);
563    mr_msg(1, "syscall_pout = %s", syscall_pout);
564    pout = popen(syscall_pout, "w");
565    if (!pout) {
566        iamhere("Unable to openout to syscall_pout");
567        return (1);
568    }
569    mr_free(syscall_pout);
570
571    pin = popen(syscall_pin, "r");
572    if (!pin) {
573        pclose(pout);
574        iamhere("Unable to openin from syscall");
575        return (1);
576    }
577    mr_free(syscall_pin);
578
579    faclin = fopen(masklist, "r");
580    if (!faclin) {
581        pclose(pin);
582        pclose(pout);
583        iamhere("Unable to openin masklist");
584        return (1);
585    }
586//  printf("Hi there. Starting the loop\n");
587
588    mr_getline(&current_subset_file, &n, faclin);
589    mr_getline(&incoming, &n, pin);
590    while (!feof(pin) && !feof(faclin)) {
591//      printf("incoming = %s", incoming);
592
593        mr_asprintf(&current_master_file, incoming + 8);
594
595        p = current_subset_file;
596        if (*p == '/') {
597            p++;
598        }
599        i = strlen(p);
600        if (i > 0 && p[i - 1] < 32) {
601            p[i - 1] = '\0';
602        }
603
604
605        q = current_master_file;
606        if (*q == '/') {
607            q++;
608        }
609        i = strlen(q);
610        if (i > 0 && q[i - 1] < 32) {
611            q[i - 1] = '\0';
612        }
613
614        i = strcmp(p, q);
615        mr_msg(my_depth, "'%s' v '%s' --> %d\n", p, q, i);
616
617//      printf("%s v %s --> %d\n", p, q, i);
618
619        if (i < 0) {            // read another subset file in.
620            mr_msg(my_depth, "Reading next subset line in\n\n");
621            mr_getline(&current_subset_file, &n, faclin);
622            continue;
623        }
624
625        if (!i) {
626            fputs(incoming, pout);
627        }
628        mr_getline(&incoming, &n, pin);
629        if (!i) {
630            mr_msg(my_depth, "Copying master %s", q);
631        }
632//      if (!i) { printf("Match --- %s\n", q); }
633
634        while (!feof(pin) && strncmp(incoming, "# file: ", 8)) {
635            if (!i) {
636
637//    printf("%s", incoming);
638
639                fputs(incoming, pout);
640            }
641            mr_getline(&incoming, &n, pin);
642        }
643        if (!i) {
644            mr_getline(&current_subset_file, &n, faclin);
645        }
646        mr_free(current_master_file);
647    }
648    mr_free(current_subset_file);
649
650    while (!feof(pin)) {
651        mr_getline(&incoming, &n, pin);
652    }
653    mr_free(incoming);
654    fclose(faclin);
655    pclose(pin);
656    pclose(pout);
657
658//  printf("OK, loop is done\n");
659
660    unlink(masklist);
661    mr_free(masklist);
662
663    return (retval);
664}
665
666
667int set_fattr_list(char *masklist, char *fattr_fname)
668{
669    if (find_home_of_exe("setfattr")) {
670        return (set_EXAT_list(masklist, fattr_fname, "setfattr"));
671    } else {
672        mr_msg(1, "ERROR: set_EXAT_list: setfattr doesn't exist");
673        return(0);
674    }
675}
676
677
678
679int set_acl_list(char *masklist, char *acl_fname)
680{
681    if (find_home_of_exe("setfacl")) {
682        return (set_EXAT_list(masklist, acl_fname, "setfacl"));
683    } else {
684        mr_msg(1, "ERROR: set_EXAT_list: setfacl doesn't exist");
685        return(0);
686    }
687}
688
689
690/**
691 * Get the number of the last fileset in the backup.
692 * @param bkpinfo The backup information structure. Only the @c bkpinfo->tmpdir field is used.
693 * @return The last filelist number.
694 * @note This function should only be called at restore-time.
695 */
696int get_last_filelist_number(struct s_bkpinfo *bkpinfo)
697{
698    /*@ buffers ***************************************************** */
699    char val_sz[MAX_STR_LEN];
700    char *cfg_fname = NULL;
701
702    /*@ long ******************************************************** */
703    int val_i = 0;
704
705    /*@ end vars **************************************************** */
706
707    assert(bkpinfo != NULL);
708
709    mr_asprintf(&cfg_fname, "%s/mondo-restore.cfg", bkpinfo->tmpdir);
710    read_cfg_var(cfg_fname, "last-filelist-number", val_sz);
711    val_i = atoi(val_sz);
712    if (val_i <= 0) {
713        val_i = 500;
714    }
715    mr_free(cfg_fname);
716    return (val_i);
717}
718
719
720/**
721 * Add a string at @p startnode.
722 * @param startnode The node to start at when searching for where to add the string.
723 * @param string_to_add The string to add.
724 * @return 0 for success, 1 for failure.
725 * @bug I don't understand this function. Would someone care to explain it?
726 */
727
728int add_string_at_node(struct s_node *startnode, char *string_to_add)
729{
730
731    int noof_chars;
732    int i;
733    int res;
734
735    struct s_node *node, *newnode;
736
737    char char_to_add;
738
739    const bool sosodef = FALSE;
740
741    static int depth = 0;
742    static char original_string[MAX_STR_LEN];
743
744    assert(startnode != NULL);
745    assert(string_to_add != NULL);
746
747    if (!depth) {
748        strcpy(original_string, string_to_add);
749    }
750
751    noof_chars = strlen(string_to_add) + 1; // we include the '\0'
752
753    // walk across tree if necessary
754    node = startnode;
755    char_to_add = string_to_add[0];
756    if (node->right != NULL && node->ch < char_to_add) {
757        mr_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth,
758                char_to_add, node->ch, (node->right)->ch);
759        return (add_string_at_node(node->right, string_to_add));
760    }
761
762    // walk down tree if appropriate
763    if (node->down != NULL && node->ch == char_to_add) {
764        mr_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_add);
765        depth++;
766        res = add_string_at_node(node->down, string_to_add + 1);
767        depth--;
768        return (res);
769    }
770
771    if (char_to_add == '\0' && node->ch == '\0') {
772        mr_msg(6, "%s already in tree", original_string);
773        return (1);
774    }
775
776    // add here
777    newnode = (struct s_node *) mr_malloc(sizeof(struct s_node));
778    if (char_to_add < node->ch) // add to the left of node
779    {
780        mr_msg(7, "depth=%d char=%c --- adding (left)", depth,
781                char_to_add);
782        memcpy((void *) newnode, (void *) node, sizeof(struct s_node));
783        node->right = newnode;
784    } else if (char_to_add > node->ch)  // add to the right of node
785    {
786        mr_msg(7, "depth=%d char=%c --- adding (right)", depth,
787                char_to_add);
788        newnode->right = node->right;   // newnode is to the RIGHT of node
789        node->right = newnode;
790        node = newnode;
791    }
792    // from now on, we're working on 'node'
793    node->down = NULL;
794    node->ch = char_to_add;
795    node->expanded = node->selected = FALSE;
796    if (char_to_add == '\0') {
797        mr_msg(6, "Added %s OK", original_string);
798        return (0);
799    }
800    // add the rest
801    mr_msg(6, "Adding remaining chars ('%s')", string_to_add + 1);
802    for (i = 1; i < noof_chars; i++) {
803        node->down = (struct s_node *) mr_malloc(sizeof(struct s_node));
804        node = node->down;
805        char_to_add = string_to_add[i];
806        mr_msg(6, "Adding '%c'", char_to_add);
807        node->ch = char_to_add;
808        node->right = node->down = NULL;
809        node->expanded = node->selected = FALSE;
810        if (!node->ch) {
811            node->selected = sosodef;
812        }
813    }
814    mr_msg(6, "Finally - added %s OK", original_string);
815    return (0);
816}
817
818
819/**
820 * Load a filelist into a <tt>struct s_node</tt>.
821 * When you are done with the filelist, call free_filelist().
822 * @param filelist_fname The file to load the filelist from.
823 * @return A filelist tree structure.
824 */
825struct s_node *load_filelist(char *filelist_fname)
826{
827
828    struct s_node *filelist = NULL;
829    FILE *pin = NULL;
830    char *command_to_open_fname = NULL;
831    char *fname = NULL;
832    char *tmp = NULL;
833    char *tmp1 = NULL;
834    int pos_in_fname = 0;
835    size_t n = 0;
836    int percentage = 0;
837
838    long lines_in_filelist = 0L;
839    long lino = 0L;
840
841    assert_string_is_neither_NULL_nor_zerolength(filelist_fname);
842
843    if (!does_file_exist(filelist_fname)) {
844        fatal_error("filelist does not exist -- cannot load it");
845    }
846    log_to_screen(_("Loading filelist"));
847    mr_asprintf(&command_to_open_fname, "gzip -dc %s", filelist_fname);
848    mr_asprintf(&tmp, "zcat %s | wc -l", filelist_fname);
849    mr_msg(6, "tmp = %s", tmp);
850    lines_in_filelist =
851        atol(call_program_and_get_last_line_of_output(tmp));
852    if (lines_in_filelist < 3) {
853        log_to_screen(_("Warning - surprisingly short filelist."));
854    }
855    g_original_noof_lines_in_filelist = lines_in_filelist;
856    filelist = (struct s_node *) mr_malloc(sizeof(struct s_node));
857    filelist->ch = '/';
858    filelist->right = NULL;
859    filelist->down = mr_malloc(sizeof(struct s_node));
860    filelist->expanded = filelist->selected = FALSE;
861    (filelist->down)->ch = '\0';
862    (filelist->down)->right = (filelist->down)->down = FALSE;
863    (filelist->down)->expanded = (filelist->down)->selected = FALSE;
864    if (!(pin = popen(command_to_open_fname, "r"))) {
865        log_OS_error("Unable to openin filelist_fname");
866        return (NULL);
867    }
868    mr_free(command_to_open_fname);
869
870    open_evalcall_form(_("Loading filelist from disk"));
871    for (mr_getline(&fname, &n, pin); !feof(pin); mr_getline(&fname, &n, pin)) {
872        if ((fname[strlen(fname) - 1] == 13
873             || fname[strlen(fname) - 1] == 10) && strlen(fname) > 0) {
874            fname[strlen(fname) - 1] = '\0';
875        }
876        if (!strlen(fname)) {
877            continue;
878        }
879        for (pos_in_fname = 0; fname[pos_in_fname] != '\0'; pos_in_fname++) {
880            if (fname[pos_in_fname] != '/') {
881                continue;
882            }
883            mr_asprintf(&tmp, fname);
884            tmp[pos_in_fname] = '\0';
885            if (strlen(tmp)) {
886                add_string_at_node(filelist, tmp);
887            }
888            mr_free(tmp);
889        }
890        add_string_at_node(filelist, fname);
891        if (!(++lino % 1111)) {
892            percentage = (int) (lino * 100 / lines_in_filelist);
893            update_evalcall_form(percentage);
894        }
895    }
896    mr_free(fname);
897    paranoid_pclose(pin);
898    close_evalcall_form();
899    log_it("Finished loading filelist");
900    return (filelist);
901}
902
903
904/**
905 * Log a list of files in @p node.
906 * @param node The toplevel node to use.
907 */
908void show_filelist(struct s_node *node)
909{
910    static int depth = 0;
911    static char current_string[200];
912
913    if (depth == 0) {
914        mr_msg(0, "----------------show filelist--------------");
915    }
916    current_string[depth] = node->ch;
917
918    mr_msg(3, "depth=%d", depth);
919    if (node->down) {
920        mr_msg(3, "moving down");
921        depth++;
922        show_filelist(node->down);
923        depth--;
924    }
925
926    if (!node->ch) {
927        mr_msg(0, "%s\n", current_string);
928    }
929
930    if (node->right) {
931        mr_msg(3, "moving right");
932        show_filelist(node->right);
933    }
934    if (depth == 0) {
935        mr_msg(0, "----------------show filelist--------------");
936    }
937    return;
938}
939
940
941/**
942 * Reset the filelist to the state it was when it was loaded. This does not
943 * touch the file on disk.
944 * @param filelist The filelist tree structure.
945 */
946void reload_filelist(struct s_node *filelist)
947{
948    assert(filelist != NULL);
949    toggle_node_selection(filelist, FALSE);
950    toggle_path_expandability(filelist, "/", FALSE);
951    toggle_all_root_dirs_on(filelist);
952}
953
954
955/**
956 * Save a filelist tree structure to disk.
957 * @param filelist The filelist tree structure to save.
958 * @param outfname Where to save it.
959 */
960void save_filelist(struct s_node *filelist, char *outfname)
961{
962    /*@ int ********************************************************* */
963    static int percentage;
964    static int depth = 0;
965
966    /*@ buffers ***************************************************** */
967    static char str[MAX_STR_LEN];
968
969    /*@ structures ************************************************** */
970    struct s_node *node;
971
972    /*@ pointers **************************************************** */
973    static FILE *fout = NULL;
974
975    /*@ long ******************************************************** */
976    static long lines_in_filelist = 0;
977    static long lino = 0;
978
979    /*@ end vars *************************************************** */
980
981    assert(filelist != NULL);
982    assert(outfname != NULL);   // will be zerolength if save_filelist() is called by itself
983    if (depth == 0) {
984        log_to_screen(_("Saving filelist"));
985        if (!(fout = fopen(outfname, "w"))) {
986            fatal_error("Cannot openout/save filelist");
987        }
988        lines_in_filelist = g_original_noof_lines_in_filelist;  /* set by load_filelist() */
989        open_evalcall_form(_("Saving selection to disk"));
990    }
991    for (node = filelist; node != NULL; node = node->right) {
992        str[depth] = node->ch;
993        mr_msg(5, "depth=%d ch=%c", depth, node->ch);
994        if (!node->ch) {
995//    if (node->selected)
996//      {
997            fprintf(fout, "%s\n", str);
998//      }
999            if (!(++lino % 1111)) {
1000                percentage = (int) (lino * 100 / lines_in_filelist);
1001                update_evalcall_form(percentage);
1002            }
1003        }
1004        if (node->down) {
1005            depth++;
1006            save_filelist(node->down, "");
1007            depth--;
1008        }
1009    }
1010    if (depth == 0) {
1011        paranoid_fclose(fout);
1012        close_evalcall_form();
1013        log_it("Finished saving filelist");
1014    }
1015}
1016
1017
1018/**
1019 * Toggle all root dirs on.
1020 * @param filelist The filelist tree structure to operate on.
1021 * @bug I don't understand this function. Would someone care to explain it?
1022 */
1023void toggle_all_root_dirs_on(struct s_node *filelist)
1024{
1025    /*@ structures ************************************************** */
1026    struct s_node *node;
1027
1028    /*@ int ********************************************************* */
1029    static int depth = 0;
1030    static int root_dirs_expanded;
1031
1032    /*@ buffers ***************************************************** */
1033    static char filename[MAX_STR_LEN];
1034
1035    /*@ end vars *************************************************** */
1036
1037    assert(filelist != NULL);
1038    if (depth == 0) {
1039        log_it("Toggling all root dirs ON");
1040        root_dirs_expanded = 0;
1041    }
1042    for (node = filelist; node != NULL; node = node->right) {
1043        filename[depth] = node->ch;
1044        if (node->ch == '\0' && strlen(filename) > 1
1045            && (!strchr(filename + 1, '/'))) {
1046            node->selected = FALSE;
1047            node->expanded = TRUE;
1048//    log_it (filename);
1049            root_dirs_expanded++;
1050        }
1051        if (node->down) {
1052            depth++;
1053            toggle_all_root_dirs_on(node->down);
1054            depth--;
1055        }
1056    }
1057    if (depth == 0) {
1058        log_it("Finished toggling all root dirs ON");
1059    }
1060}
1061
1062
1063/**
1064 * Toggle the expandability of a path.
1065 * @param filelist The filelist tree to operate on.
1066 * @param pathname The path to toggle expandability of.
1067 * @param on_or_off Whether to toggle it on or off.
1068 * @bug I don't understand this function. Would someone care to explain it?
1069 */
1070void
1071toggle_path_expandability(struct s_node *filelist, char *pathname,
1072                          bool on_or_off)
1073{
1074
1075    /*@ int ******************************************************** */
1076    static int depth = 0;
1077    static int total_expanded;
1078    static int root_depth;
1079    int j;
1080    /*@ structures ************************************************* */
1081    struct s_node *node;
1082
1083    /*@ buffers **************************************************** */
1084    static char current_filename[MAX_STR_LEN];
1085
1086    /*@ end vars *************************************************** */
1087
1088    assert(filelist != NULL);
1089    assert_string_is_neither_NULL_nor_zerolength(pathname);
1090    if (depth == 0) {
1091        total_expanded = 0;
1092//      log_it ("Toggling path's expandability");
1093        for (root_depth = (int) strlen(pathname);
1094             root_depth > 0 && pathname[root_depth - 1] != '/';
1095             root_depth--);
1096        if (root_depth < 2) {
1097            root_depth = (int) strlen(pathname);
1098        }
1099    }
1100    for (node = filelist; node != NULL; node = node->right) {
1101        current_filename[depth] = node->ch;
1102        if (node->down) {
1103            depth++;
1104            toggle_path_expandability(node->down, pathname, on_or_off);
1105            depth--;
1106        }
1107        if (node->ch == '\0') {
1108            if (!strncmp(pathname, current_filename, strlen(pathname))) {
1109                for (j = root_depth;
1110                     current_filename[j] != '/'
1111                     && current_filename[j] != '\0'; j++);
1112                if (current_filename[j] != '\0') {
1113                    for (j++;
1114                         current_filename[j] != '/'
1115                         && current_filename[j] != '\0'; j++);
1116                }
1117                if (current_filename[j] == '\0') {
1118                    node->expanded =
1119                        (!strcmp(pathname, current_filename) ? TRUE :
1120                         on_or_off);
1121                }
1122            }
1123        }
1124        if (node->expanded) {
1125            if (total_expanded < ARBITRARY_MAXIMUM - 32
1126                || !strrchr(current_filename + strlen(pathname), '/')) {
1127                total_expanded++;
1128            } else {
1129                node->expanded = FALSE;
1130            }
1131        }
1132    }
1133    if (depth == 0) {
1134//      log_it ("Finished toggling expandability");
1135    }
1136}
1137
1138
1139/**
1140 * Toggle whether a path is selected.
1141 * @param filelist The filelist tree to operate on.
1142 * @param pathname The path to toggle selection of.
1143 * @param on_or_off Whether to toggle it on or off.
1144 * @bug I don't understand this function. Would someone care to explain it?
1145 */
1146void
1147toggle_path_selection(struct s_node *filelist, char *pathname,
1148                      bool on_or_off)
1149{
1150    /*@ int ********************************************************* */
1151    static int depth = 0;
1152    int j;
1153
1154    /*@ structures ************************************************** */
1155    struct s_node *node;
1156
1157    /*@ buffers ***************************************************** */
1158    static char current_filename[MAX_STR_LEN];
1159
1160    /*@ end vars *************************************************** */
1161    assert(filelist != NULL);
1162    assert_string_is_neither_NULL_nor_zerolength(pathname);
1163    if (depth == 0) {
1164        log_it("Toggling path's selection");
1165    }
1166    for (node = filelist; node != NULL; node = node->right) {
1167        current_filename[depth] = node->ch;
1168        if (node->down) {
1169            depth++;
1170            toggle_path_selection(node->down, pathname, on_or_off);
1171            depth--;
1172        }
1173        if (node->ch == '\0') {
1174            if (!strncmp(pathname, current_filename, strlen(pathname))) {
1175                for (j = 0;
1176                     pathname[j] != '\0'
1177                     && pathname[j] == current_filename[j]; j++);
1178                if (current_filename[j] == '/'
1179                    || current_filename[j] == '\0') {
1180                    node->selected = on_or_off;
1181                }
1182            }
1183        }
1184    }
1185    if (depth == 0) {
1186        log_it("Finished toggling selection");
1187    }
1188}
1189
1190
1191/**
1192 * Toggle node selection of a filelist tree.
1193 * @param filelist The filelist tree to operate on.
1194 * @param on_or_off Whether to toggle selection on or off.
1195 * @bug I don't understand this function. Would someone care to explain it?
1196 */
1197void toggle_node_selection(struct s_node *filelist, bool on_or_off)
1198{
1199    /*@ structure ************************************************** */
1200    struct s_node *node;
1201
1202    /*@ end vars *************************************************** */
1203    assert(filelist != NULL);
1204    for (node = filelist; node != NULL; node = node->right) {
1205        if (node->ch == '/') {
1206            continue;
1207        }                       /* don't go deep */
1208        if (node->ch == '\0') {
1209            node->selected = on_or_off;
1210        }
1211        if (node->down) {
1212            toggle_node_selection(node->down, on_or_off);
1213        }
1214    }
1215}
1216
1217
1218/**
1219 * The pathname to the skeleton filelist, used to give better progress reporting for mondo_makefilelist().
1220 */
1221char *g_skeleton_filelist = NULL;
1222
1223/**
1224 * Number of entries in the skeleton filelist.
1225 */
1226long g_skeleton_entries = 0;
1227
1228/**
1229 * Wrapper around mondo_makefilelist().
1230 * @param bkpinfo The backup information structure. Fields used:
1231 * - @c bkpinfo->differential
1232 * - @c bkpinfo->exclude_paths
1233 * - @c bkpinfo->include_paths
1234 * - @c bkpinfo->make_filelist
1235 * - @c bkpinfo->scratchdir
1236 * - @c bkpinfo->tmpdir
1237 * @return 0 for success, nonzero for failure.
1238 * @see mondo_makefilelist
1239 */
1240int prepare_filelist(struct s_bkpinfo *bkpinfo)
1241{
1242
1243    /*@ int **************************************************** */
1244    int res = 0;
1245
1246    assert(bkpinfo != NULL);
1247    log_it("tmpdir=%s; scratchdir=%s", bkpinfo->tmpdir,
1248           bkpinfo->scratchdir);
1249    if (bkpinfo->make_filelist) {
1250        mvaddstr_and_log_it(g_currentY, 0,
1251                            _("Making catalog of files to be backed up"));
1252    } else {
1253        mvaddstr_and_log_it(g_currentY, 0,
1254                            _("Using supplied catalog of files to be backed up"));
1255    }
1256
1257    if (bkpinfo->make_filelist) {
1258        res =
1259            mondo_makefilelist(MONDO_LOGFILE, bkpinfo->tmpdir,
1260                               bkpinfo->scratchdir, bkpinfo->include_paths,
1261                               bkpinfo->exclude_paths,
1262                               bkpinfo->differential, NULL);
1263    } else {
1264        res =
1265            mondo_makefilelist(MONDO_LOGFILE, bkpinfo->tmpdir,
1266                               bkpinfo->scratchdir, NULL,
1267                               bkpinfo->exclude_paths,
1268                               bkpinfo->differential,
1269                               bkpinfo->include_paths);
1270    }
1271
1272    if (res) {
1273        log_OS_error("Call to mondo_makefilelist failed");
1274        mvaddstr_and_log_it(g_currentY++, 74, _("Failed."));
1275    } else {
1276        mvaddstr_and_log_it(g_currentY++, 74, _("Done."));
1277    }
1278    return (res);
1279}
1280
1281
1282/**
1283 * Recursively list all files in @p dir newer than @p time_of_last_full_backup to @p fout.
1284 * @param dir The directory to list to @p fout.
1285 * @param sth The directories to skip (exclude).
1286 * @param fout The file to write everything to.
1287 * @param time_of_last_full_backup Only backup files newer than this (0 to disable).
1288 * @return 0, always.
1289 * @bug Return value should be @c void.
1290 */
1291int open_and_list_dir(char *dir, char *sth, FILE * fout,
1292                      time_t time_of_last_full_backup)
1293{
1294    const char delims[] = " ";
1295    DIR *dip = NULL;
1296    struct dirent *dit = NULL;
1297
1298    struct stat statbuf;
1299    char new[MAX_STR_LEN];
1300    char *tmp = NULL;
1301    char *sth_B = NULL;
1302    static int percentage = 0;
1303    char *ith_B = NULL;
1304    char *skip_these = NULL;
1305    char *new_with_spaces = NULL;
1306    static char *name_of_evalcall_form = NULL;
1307    int i = 0;
1308    char *strtmp = NULL;
1309    char *token = NULL;
1310    char *find_excludes = NULL;
1311    int lastpos = 0;
1312    static int depth = 0;
1313    char *p = NULL;
1314    static int counter = 0;
1315    static int uberctr = 0;
1316    static char *find_skeleton_marker = NULL;
1317    static long skeleton_lino = 0;
1318    static time_t last_time = 0;
1319    time_t this_time;
1320
1321    malloc_string(tmp);
1322    malloc_string(sth_B);
1323    malloc_string(ith_B);
1324    malloc_string(new_with_spaces);
1325    p = strrchr(dir, '/');
1326    if (p) {
1327        if (!strcmp(p, "/.") || !strcmp(p, "/..")) {
1328            return (0);
1329        }
1330    }
1331
1332    if (!depth) {
1333        malloc_string(name_of_evalcall_form);
1334        malloc_string(find_skeleton_marker);
1335        asprintf(&find_excludes, " ");
1336        while((token = mr_strtok (sth, delims, &lastpos))) {
1337          asprintf(&strtmp,"%s", find_excludes);
1338          mr_free(find_excludes);
1339          asprintf(&find_excludes,"%s -path %s -prune -o", strtmp, token);
1340          mr_free(strtmp);
1341          mr_free(token);
1342        }
1343#if linux
1344        // 2.6 has /sys as a proc-type thing -- must be excluded
1345        asprintf(&strtmp,
1346             "find %s -maxdepth %d -fstype mvfs -prune -o -path /dev/shm -prune -o %s -type d -print > %s 2> /dev/null",
1347             dir, MAX_SKEL_DEPTH, find_excludes, g_skeleton_filelist);
1348#else
1349        // On BSD, for example, /sys is the kernel sources -- don't exclude
1350        asprintf(&strtmp,
1351             "find %s -maxdepth %d -fstype mvfs -prune -o -path /proc -prune -o %s -type d -print > %s 2> /dev/null",
1352                dir, MAX_SKEL_DEPTH, find_excludes, g_skeleton_filelist);
1353#endif
1354        mr_free(find_excludes);
1355        mr_msg(5, "find command = %s", strtmp);
1356        system(strtmp);
1357        mr_free(strtmp);
1358        sprintf(tmp, "wc -l %s | awk '{print $1;}'", g_skeleton_filelist);
1359        g_skeleton_entries =
1360            1 + atol(call_program_and_get_last_line_of_output(tmp));
1361        sprintf(name_of_evalcall_form, "Making catalog of %s", dir);
1362        open_evalcall_form(name_of_evalcall_form);
1363        find_skeleton_marker[0] = '\0';
1364        skeleton_lino = 1;
1365        mr_msg(5, "entries = %ld", g_skeleton_entries);
1366        percentage = 0;
1367    } else if (depth <= MAX_SKEL_DEPTH) // update evalcall form if appropriate
1368    {
1369        sprintf(find_skeleton_marker,
1370                "grep -Fv \"%s\" %s > %s.new 2> /dev/null", dir,
1371                g_skeleton_filelist, g_skeleton_filelist);
1372//    mr_msg(0, "fsm = %s", find_skeleton_marker);
1373        if (!system(find_skeleton_marker)) {
1374            percentage = (int) (skeleton_lino * 100 / g_skeleton_entries);
1375            skeleton_lino++;
1376//        mr_msg(5, "Found %s", dir);
1377//        mr_msg(2, "Incrementing skeleton_lino; now %ld/%ld (%d%%)", skeleton_lino, g_skeleton_entries, percentage);
1378            sprintf(find_skeleton_marker, "mv -f %s.new %s",
1379                    g_skeleton_filelist, g_skeleton_filelist);
1380//        mr_msg(6, "fsm = %s", find_skeleton_marker);
1381            run_program_and_log_output(find_skeleton_marker, 8);
1382            time(&this_time);
1383            if (this_time != last_time) {
1384                last_time = this_time;
1385#ifndef _XWIN
1386                if (!g_text_mode) {
1387                    sprintf(tmp, "Reading %-68s", dir);
1388                    newtDrawRootText(0, g_noof_rows - 3, tmp);
1389                }
1390#endif
1391                update_evalcall_form(percentage);
1392            }
1393        }
1394    }
1395
1396    depth++;
1397
1398//  mr_msg(0, "Cataloguing %s", dir);
1399    if (sth[0] == ' ') {
1400        skip_these = sth;
1401    } else {
1402        skip_these = sth_B;
1403        sprintf(skip_these, " %s ", sth);
1404    }
1405    sprintf(new_with_spaces, " %s ", dir);
1406    if ((dip = opendir(dir)) == NULL) {
1407        log_OS_error("opendir");
1408    } else if (strstr(skip_these, new_with_spaces)) {
1409        fprintf(fout, "%s\n", dir); // if excluded dir then print dir ONLY
1410    } else {
1411        fprintf(fout, "%s\n", dir);
1412        while ((dit = readdir(dip)) != NULL) {
1413            i++;
1414            strcpy(new, dir);
1415            if (strcmp(dir, "/")) {
1416                strcat(new, "/");
1417            }
1418            strcat(new, dit->d_name);
1419            new_with_spaces[0] = ' ';
1420            strcpy(new_with_spaces + 1, new);
1421            strcat(new_with_spaces, " ");
1422            if (strstr(skip_these, new_with_spaces)) {
1423                fprintf(fout, "%s\n", new);
1424            } else {
1425                if (!lstat(new, &statbuf)) {
1426                    if (!S_ISLNK(statbuf.st_mode)
1427                        && S_ISDIR(statbuf.st_mode)) {
1428                        open_and_list_dir(new, skip_these, fout,
1429                                          time_of_last_full_backup);
1430                    } else {
1431                        if (time_of_last_full_backup == 0
1432                            || time_of_last_full_backup <
1433                            statbuf.st_ctime) {
1434                            fprintf(fout, "%s\n", new);
1435                            if ((counter++) > 128) {
1436                                counter = 0;
1437                                uberctr++;
1438                                sprintf(tmp, " %c ",
1439                                        special_dot_char(uberctr));
1440#ifndef _XWIN
1441                                if (!g_text_mode) {
1442                                    newtDrawRootText(77, g_noof_rows - 3,
1443                                                     tmp);
1444                                    newtRefresh();
1445                                }
1446#endif
1447                            }
1448                        }
1449                    }
1450                }
1451            }
1452        }
1453    }
1454    if (dip) {
1455        if (closedir(dip) == -1) {
1456            log_OS_error("closedir");
1457        }
1458    }
1459    depth--;
1460    if (!depth) {
1461        close_evalcall_form();
1462        mr_free(name_of_evalcall_form);
1463        mr_free(find_skeleton_marker);
1464        unlink(g_skeleton_filelist);
1465        mr_msg(5, "g_skeleton_entries = %ld", g_skeleton_entries);
1466    }
1467    mr_free(tmp);
1468    mr_free(sth_B);
1469    mr_free(ith_B);
1470    mr_free(new_with_spaces);
1471    return (0);
1472}
1473
1474
1475/**
1476 * Get the next entry in the space-separated list in @p incoming.
1477 * So if @p incoming was '"one and two" three four', we would
1478 * return "one and two".
1479 * @param incoming The list to get the next entry from.
1480 * @return The first item in the list (respecting double quotes).
1481 * @note The returned string points to static data that will be overwritten with each call.
1482 */
1483char *next_entry(char *incoming)
1484{
1485    static char sz_res[MAX_STR_LEN];
1486    char *p = NULL;
1487    bool in_quotes = FALSE;
1488
1489    strcpy(sz_res, incoming);
1490    p = sz_res;
1491    while ((*p != ' ' || in_quotes) && *p != '\0') {
1492        if (*p == '\"') {
1493            in_quotes = !in_quotes;
1494        }
1495        p++;
1496    }
1497    *p = '\0';
1498    return (sz_res);
1499}
1500
1501
1502/**
1503 * Create the filelist for the backup. It will be stored in [scratchdir]/archives/filelist.full.
1504 * @param logfile Unused.
1505 * @param tmpdir The tmpdir of the backup.
1506 * @param scratchdir The scratchdir of the backup.
1507 * @param include_paths The paths to back up, or NULL if you're using a user-defined filelist.
1508 * @param excp The paths to NOT back up.
1509 * @param differential The differential level (currently only 0 and 1 are supported).
1510 * @param userdef_filelist The user-defined filelist, or NULL if you're using @p include_paths.
1511 * @return 0, always.
1512 * @bug @p logfile is unused.
1513 * @bug Return value is meaningless.
1514 */
1515int mondo_makefilelist(char *logfile, char *tmpdir, char *scratchdir,
1516                       char *include_paths, char *excp, int differential,
1517                       char *userdef_filelist)
1518{
1519    char sz_datefile_wildcard[] = "/var/cache/mondo-archive/difflevel.%d";
1520    char *p = NULL;
1521    char *q = NULL;
1522
1523    char *tmp1 = NULL;
1524    char *tmp2 = NULL;
1525    char *sz_datefile = NULL;
1526    char *sz_filelist = NULL;
1527    char *exclude_paths = NULL;
1528    int i = 0;
1529    FILE *fout = NULL;
1530    char *command = NULL;
1531    time_t time_of_last_full_backup = 0;
1532    struct stat statbuf;
1533
1534    malloc_string(sz_datefile);
1535    malloc_string(exclude_paths);
1536    malloc_string(g_skeleton_filelist);
1537    // The pathname to the skeleton filelist, used to give better progress reporting for mondo_makefilelist().
1538    mr_asprintf(&sz_datefile, sz_datefile_wildcard, 0);
1539    if (!include_paths && !userdef_filelist) {
1540        fatal_error
1541            ("Please supply either include_paths or userdef_filelist");
1542    }
1543// make hole for filelist
1544    mr_asprintf(&command, "mkdir -p %s/archives", scratchdir);
1545    paranoid_system(command);
1546    mr_free(command);
1547
1548    mr_asprintf(&sz_filelist, "%s/tmpfs/filelist.full", tmpdir);
1549    make_hole_for_file(sz_filelist);
1550
1551    if (differential == 0) {
1552        // restore last good datefile if it exists
1553        mr_asprintf(&command, "cp -f %s.aborted %s", sz_datefile,
1554                 sz_datefile);
1555        run_program_and_log_output(command, 3);
1556        mr_free(command);
1557
1558        // backup last known good datefile just in case :)
1559        if (does_file_exist(sz_datefile)) {
1560            mr_asprintf(&command, "mv -f %s %s.aborted", sz_datefile,
1561                     sz_datefile);
1562            paranoid_system(command);
1563            mr_free(command);
1564        }
1565        make_hole_for_file(sz_datefile);
1566        write_one_liner_data_file(sz_datefile,
1567                                  call_program_and_get_last_line_of_output
1568                                  ("date +%s"));
1569    } else if (lstat(sz_datefile, &statbuf)) {
1570        mr_msg(2,
1571                "Warning - unable to find date of previous backup. Full backup instead.");
1572        differential = 0;
1573        time_of_last_full_backup = 0;
1574    } else {
1575        time_of_last_full_backup = statbuf.st_mtime;
1576        mr_msg(2, "Differential backup. Yay.");
1577    }
1578    mr_free(sz_datefile);
1579
1580// use user-specified filelist (if specified)
1581    if (userdef_filelist) {
1582        mr_msg(1,
1583                "Using the user-specified filelist - %s - instead of calculating one",
1584                userdef_filelist);
1585        mr_asprintf(&command, "cp -f %s %s", userdef_filelist, sz_filelist);
1586        if (run_program_and_log_output(command, 3)) {
1587            fatal_error("Failed to copy user-specified filelist");
1588        }
1589        mr_free(command);
1590    } else {
1591        mr_msg(2, "include_paths = '%s'", include_paths);
1592        mr_msg(1, "Calculating filelist");
1593        sprintf(exclude_paths, " %s %s %s %s %s %s . .. \
1594" MNT_CDROM " " MNT_FLOPPY " /media \
1595/proc /sys /tmp /root/images/mondo /var/cache/mindi ", excp, call_program_and_get_last_line_of_output("locate /win386.swp 2> /dev/null"), call_program_and_get_last_line_of_output("locate /hiberfil.sys 2> /dev/null"), call_program_and_get_last_line_of_output("locate /pagefile.sys 2> /dev/null"), (tmpdir[0] == '/' && tmpdir[1] == '/') ? (tmpdir + 1) : tmpdir, (scratchdir[0] == '/' && scratchdir[1] == '/') ? (scratchdir + 1) : scratchdir);
1596
1597        mr_msg(2, "Excluding paths = '%s'", exclude_paths);
1598        mr_msg(2,
1599                "Generating skeleton filelist so that we can track our progress");
1600        sprintf(g_skeleton_filelist, "%s/tmpfs/skeleton.txt", tmpdir);
1601        make_hole_for_file(g_skeleton_filelist);
1602        mr_msg(4, "g_skeleton_entries = %ld", g_skeleton_entries);
1603        mr_msg(2, "Opening out filelist to %s", sz_filelist);
1604        if (!(fout = fopen(sz_filelist, "w"))) {
1605            fatal_error("Cannot openout to sz_filelist");
1606        }
1607        i = 0;
1608        if (strlen(include_paths) == 0) {
1609            mr_msg(1, "Including only '/' in %s", sz_filelist);
1610            open_and_list_dir("/", exclude_paths, fout,
1611                              time_of_last_full_backup);
1612        } else {
1613            p = include_paths;
1614            while (*p) {
1615                q = next_entry(p);
1616                mr_msg(1, "Including %s in filelist %s", q, sz_filelist);
1617                open_and_list_dir(q, exclude_paths, fout,
1618                                  time_of_last_full_backup);
1619                p += strlen(q);
1620                while (*p == ' ') {
1621                    p++;
1622                }
1623            }
1624        }
1625        paranoid_fclose(fout);
1626        mr_free(exclude_paths);
1627    }
1628    mr_msg(2, "Copying new filelist to scratchdir");
1629    mr_asprintf(&command, "mkdir -p %s/archives", scratchdir);
1630    paranoid_system(command);
1631    mr_free(command);
1632
1633    mr_asprintf(&command, "cp -f %s %s/archives/", sz_filelist, scratchdir);
1634    paranoid_system(command);
1635    mr_free(command);
1636
1637    mr_asprintf(&command, "mv -f %s %s", sz_filelist, tmpdir);
1638    paranoid_system(command);
1639    mr_free(command);
1640    mr_free(sz_filelist);
1641    mr_free(g_skeleton_filelist);
1642    mr_msg(2, "Exiting");
1643    return (0);
1644}
1645
1646
1647/**
1648 * Locate the string @p string_to_find in the tree rooted at @p startnode.
1649 * @param startnode The node containing the root of the directory tree.
1650 * @param string_to_find The string to look for at @p startnode.
1651 * @return The node containing the last element of @p string_to_find, or NULL if
1652 * it was not found.
1653 */
1654struct s_node *find_string_at_node(struct s_node *startnode,
1655                                   char *string_to_find)
1656{
1657    /*@ int ******************************************************** */
1658    int noof_chars;
1659    static int depth = 0;
1660    static char original_string[MAX_STR_LEN];
1661
1662    /*@ sturctures ************************************************* */
1663    struct s_node *node;
1664
1665    /*@ char  ****************************************************** */
1666    char char_to_find;
1667
1668    /*@ bools ****************************************************** */
1669
1670    if (!depth) {
1671        strcpy(original_string, string_to_find);
1672    }
1673
1674    assert(startnode != NULL);
1675    assert(string_to_find != NULL);
1676
1677    noof_chars = strlen(string_to_find) + 1;    /* we include the '\0' */
1678
1679    mr_msg(7, "starting --- str=%s", string_to_find);
1680
1681/* walk across tree if necessary */
1682    node = startnode;
1683    char_to_find = string_to_find[0];
1684    if (node->right != NULL && node->ch < char_to_find) {
1685        mr_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth,
1686                char_to_find, node->ch, (node->right)->ch);
1687        return (find_string_at_node(node->right, string_to_find));
1688    }
1689
1690/* walk down tree if appropriate */
1691    if (node->down != NULL && node->ch == char_to_find) {
1692        mr_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_find);
1693        depth++;
1694        node = find_string_at_node(node->down, string_to_find + 1);
1695        depth--;
1696        return (node);
1697    }
1698
1699    if (char_to_find == '\0' && node->ch == '\0') {
1700        mr_msg(7, "%s is in tree", original_string);
1701        return (node);
1702    } else {
1703        mr_msg(7, "%s is NOT in tree", original_string);
1704        return (NULL);
1705    }
1706}
1707
1708
1709/**
1710 * Write all entries in @p needles_list_fname which are also in
1711 * @p filelist to @p matches_list_fname.
1712 * @param needles_list_fname A file containing strings to look for, 1 per line.
1713 * @param filelist The node for the root of the directory structure to search in.
1714 * @param matches_list_fname The filename where we should put the matches.
1715 * @return The number of matches found.
1716 */
1717long save_filelist_entries_in_common(char *needles_list_fname,
1718                                     struct s_node *filelist,
1719                                     char *matches_list_fname,
1720                                     bool use_star)
1721{
1722    int retval = 0;
1723    struct s_node *found_node;
1724    FILE *fin;
1725    FILE *fout;
1726    char *fname = NULL;
1727    char *tmp;
1728    size_t len = 0;             // Scrub's patch doesn't work without that
1729
1730    mr_msg(5, "starting");
1731    mr_msg(5, "needles_list_fname = %s", needles_list_fname);
1732    mr_msg(5, "matches_list_fname = %s", matches_list_fname);
1733    if (!(fin = fopen(needles_list_fname, "r"))) {
1734        fatal_error("Cannot openin needles_list_fname");
1735    }
1736    if (!(fout = fopen(matches_list_fname, "w"))) {
1737        fatal_error("Cannot openout matches_list_fname");
1738    }
1739    while (!feof(fin)) {
1740        mr_getline(&fname, &len, fin);
1741        if (!use_star) {
1742            if (fname[0] == '/') {
1743                mr_asprintf(&tmp, fname);
1744            } else {
1745                mr_asprintf(&tmp, "/%s", fname);
1746            }
1747            mr_free(fname);
1748            fname = tmp;
1749        }
1750        while (strlen(fname) > 0 && fname[strlen(fname) - 1] < 32) {
1751            fname[strlen(fname) - 1] = '\0';
1752        }
1753
1754        mr_msg(5, "Looking for '%s'", fname);
1755        found_node = find_string_at_node(filelist, fname);
1756        if (found_node) {
1757            if (found_node->selected) {
1758                if (fname[0] == '/') {
1759                    mr_asprintf(&tmp, fname + 1);
1760                    mr_free(fname);
1761                    fname = tmp;
1762                }
1763                mr_msg(5, "Found '%s'", fname);
1764                turn_wildcard_chars_into_literal_chars(tmp, fname);
1765                fprintf(fout, "%s\n", tmp);
1766                mr_free(tmp);
1767                retval++;
1768            }
1769        }
1770        mr_free(fname);
1771    }
1772    paranoid_fclose(fout);
1773    paranoid_fclose(fin);
1774    return (retval);
1775}
1776
1777
1778/**
1779 * Add all files listed in @p list_of_files_fname to the directory structure rooted at
1780 * @p filelist.
1781 * @param filelist The top node of the directory structure to add the files to.
1782 * @param list_of_files_fname The file containing the files to add, 1 per line.
1783 * @param flag_em If TRUE, then flag the added files for restoration.
1784 * @return 0 for success, nonzero for failure.
1785 */
1786int add_list_of_files_to_filelist(struct s_node *filelist,
1787                                  char *list_of_files_fname, bool flag_em)
1788{
1789    FILE *fin;
1790    char *tmp = NULL;
1791    size_t n = 0;
1792    struct s_node *nod;
1793
1794    mr_msg(3, "Adding %s to filelist", list_of_files_fname);
1795    if (!(fin = fopen(list_of_files_fname, "r"))) {
1796        iamhere(list_of_files_fname);
1797        return (1);
1798    }
1799    for (mr_getline(&tmp, &n, fin); !feof(fin); mr_getline(&tmp, &n, fin)) {
1800        if (!tmp[0]) {
1801            continue;
1802        }
1803        if ((tmp[strlen(tmp) - 1] == 13 || tmp[strlen(tmp) - 1] == 10)
1804            && strlen(tmp) > 0) {
1805            tmp[strlen(tmp) - 1] = '\0';
1806        }
1807        mr_msg(2, "tmp = '%s'", tmp);
1808        if (!tmp[0]) {
1809            continue;
1810        }
1811        if ((nod = find_string_at_node(filelist, tmp))) {
1812            mr_msg(5, "Found '%s' in filelist already. Cool.", tmp);
1813        } else {
1814            add_string_at_node(filelist, tmp);
1815            nod = find_string_at_node(filelist, tmp);
1816        }
1817
1818        if (nod && flag_em) {
1819            toggle_path_selection(filelist, tmp, TRUE);
1820            mr_msg(5, "Flagged '%s'", tmp);
1821        }
1822    }
1823    paranoid_fclose(fin);
1824    mr_free(tmp);
1825    return (0);
1826}
1827
1828/* @} - end of filelistGroup */
Note: See TracBrowser for help on using the repository browser.