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

Last change on this file since 1478 was 1478, checked in by Bruno Cornec, 13 years ago
  • Adds dmesg result to init log
  • Less verbosity in the sort function (reduces log file and helps reading it)

(merge -r 1473:1477 of 2.2.4)

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