source: trunk/mondo/src/common/libmondo-filelist.c @ 900

Last change on this file since 900 was 900, checked in by Bruno Cornec, 14 years ago

Huge patch to introduce low level functions that will bw used everywhere (mr_free, mr_asprintf, ...)
Nearly linking now due to that.

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