/* libmondo-filelist.c $Id: libmondo-filelist.c,v 1.6 2004/06/10 15:29:12 hugo Exp $ - for subroutines which manipulate the filelist 01/11/2005 - change time_of_last_full_backup = to st_mtime to allow manipulation of - /var/cache/mondo-archive/difflevel.0 - Conor Daly 10/01 - don't try to sort non-existent file; return 0 instead 09/14 - dropped 'sort -o' (busybox doesn't support); use 'sort > ' instead 08/02 - add \ in front of [, ], ? or * when generating filelist subset 07/27 - sort filelists after creating them 07/14 - disabled ACL, xattr stuff; moved it to libmondo-archive.c - max sane size for a file is now 32MB 07/10 - added ACL and xattr support for afio users 07/05 - exclude /dev/shm from backup (Roberto Alsina) 06/09 - exclude /media/floppy or SuSE 9.1 freezes :( 01/18/2004 - exclude /sys as well as /proc 10/18/2003 - load_filelist() --- make sure you add the paths leading to the file, e.g. if you add /usr/lib/thingy.so then add /usr, /usr/lib first - rewritten load_filelist(), save_filelist(), - add_string_at_node(), find_string_at_node(), - save_filelist_entries_in_common() 09/27 - line 1269 had one too many '%s's 09/26 - fix newt* to be enclosed in #ifndef _XWIN - added superior progress display to mondo_makefilelist() 09/18 - call locate w/ 2> /dev/null at end of str 09/16 - replaced mondo-makefilelist with C subroutine 06/06 - fixed silly bug in load_filelist() which stopped funny German filenames from being handled properly 05/19 - misc clean-up (Steve Hindle) 05/04 - added Herman Kuster's multi-level bkp patch 04/26 - maximum max_set_size_for_a_file = maxsetsizeK*2 - maximum is also 32MB 04/24 - added lots of assert()'s and log_OS_error()'s 04/22 - added lots of asserts() and log_OS_errors()'s 04/07 - cleaned up code a bit 04/04 - max_sane_size_for_a_file = maxsetsizeK*2 (was *8) 01/02/2003 - tell mondo-makefilelist that it's a custom catalog (if it is) 10/01 - 10/31/2002 - don't make large .bz2 or .tbz files into biggiefiles - max_sane_size_for_a_file = maxsetsizeK*8, or 64MB ...whichever is smaller 08/01 - 08/31 - if last filelist is <=2 bytes long then delete it - max_sane_size_for_a_file = maxsetsizeK (was SLICESIZE*4 or something) - cleaned up some log_it() calls 07/26 - started */ /** * @file * Functions which create, chop, and edit the filelist. */ #include "my-stuff.h" #include "mondostructures.h" #include "lib-common-externs.h" #include "libmondo-filelist.h" #include "libmondo-string-EXT.h" #include "libmondo-files-EXT.h" #include "libmondo-fork-EXT.h" #include "libmondo-gui-EXT.h" #include "libmondo-tools-EXT.h" #include #include #include #include #include #include #include /** * The maximum depth of directories to put in the skeleton filelist. * This is a balance between performance and a good progress indicator. */ #define MAX_SKEL_DEPTH 3 extern ssize_t getline(char **lineptr, size_t *n, FILE *stream); int mondo_makefilelist(char*logfile, char*tmpdir, char*scratchdir, char*include_paths, char*excp, int differential, char *userdef_filelist); /*@unused@*/ //static char cvsid[] = "$Id: libmondo-filelist.c,v 1.6 2004/06/10 15:29:12 hugo Exp $"; /** * Number of lines in the filelist last loaded. * @warning This implies that two filesets cannot be loaded at once. * @ingroup globalGroup */ long g_original_noof_lines_in_filelist = 0; /** * Number of filesets in the current backup. * @ingroup globalGroup */ long g_noof_sets=0; extern bool g_text_mode; extern newtComponent g_progressForm; extern int g_currentY; extern int g_noof_rows; /** * @addtogroup filelistGroup * @{ */ /** * Call chop_filelist() to chop the filelist into sets. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->image_devs * - @c bkpinfo->optimal_set_size * - @c bkpinfo->scratchdir * - @c bkpinfo->tmpdir * @see chop_filelist */ int call_filelist_chopper (struct s_bkpinfo *bkpinfo) { /*@ buffers ************************/ char *dev; char *filelist; char *tempfile; char *cksumlist; char *tmp; long noof_sets; /*@ pointers ***********************/ char *ptr; FILE *fout; /*@ int ****************************/ int i, retval=0; malloc_string(dev); malloc_string(filelist); malloc_string(tempfile); malloc_string(cksumlist); malloc_string(tmp); mvaddstr_and_log_it (g_currentY, 0, "Dividing filelist into sets"); log_to_screen ("Dividing filelist into sets. Please wait."); i=0; /* if (find_home_of_exe("getfattr")) { i++; log_to_screen ("NEW! Recording extended attributes."); } if (find_home_of_exe("getfacl")) { i++; log_to_screen ("NEW! Recording ACL information."); } if (i) { i=0; log_to_screen ("This will take more time. Please be patient."); } */ sprintf (filelist, "%s/archives/filelist.full", bkpinfo->scratchdir); sprintf (cksumlist, "%s/cklist.full", bkpinfo->tmpdir); if (!does_file_exist(filelist)) { log_it("filelist %s not found", filelist); fatal_error("call_filelist_chopper() -- filelist not found!"); } noof_sets = chop_filelist (filelist, bkpinfo->tmpdir, bkpinfo->optimal_set_size); estimate_noof_media_required (bkpinfo, noof_sets); // for cosmetic purposes sprintf (tempfile, "%s/biggielist.txt", bkpinfo->tmpdir); if (!(fout = fopen (tempfile, "a"))) { log_OS_error("Cannot append to biggielist"); retval++; goto end_of_func; } log_it (bkpinfo->image_devs); ptr = bkpinfo->image_devs; while (ptr && *ptr) { strcpy (dev, ptr); log_it ("Examining imagedev %s", dev); for (i = 0; i < (int) strlen (dev) && dev[i] != ' '; i++); dev[i] = '\0'; if (!strlen (dev)) { continue; } fprintf (fout, "%s\n", dev); log_it ("Adding '%s' to biggielist", dev); if ((ptr = strchr (ptr, ' '))) { ptr++; } } paranoid_fclose(fout); mvaddstr_and_log_it (g_currentY++, 74, "Done."); end_of_func: paranoid_free(filelist); paranoid_free(tempfile); paranoid_free(cksumlist); paranoid_free(dev); paranoid_free(tmp); return(retval); } int sort_file(char*orig_fname) { char *tmp_fname; char *command; int retval=0; log_msg(1, "Sorting file %s", orig_fname); malloc_string(tmp_fname); malloc_string(command); sprintf(tmp_fname, "/tmp/sort.%d.%d.%d", (int)(random()%32768), (int)(random()%32768), (int)(random()%32768)); if (!does_file_exist(orig_fname)) { return(0); } // no sense in trying to sort an empty file sprintf(command, "sort %s > %s 2>> %s", orig_fname, tmp_fname, MONDO_LOGFILE); retval = system(command); if (retval) { log_msg(2, "Failed to sort %s - oh dear", orig_fname); } else { log_msg(2, "Sorted %s --> %s OK. Copying it back to %s now", orig_fname, tmp_fname, orig_fname); sprintf(command, "mv -f %s %s", tmp_fname, orig_fname); retval += run_program_and_log_output(command, 2); if (retval) { log_msg(2, "Failed to copy %s back to %s - oh dear", tmp_fname, orig_fname); } else { log_msg(2, "%s was sorted OK.", orig_fname); } } paranoid_free(tmp_fname); paranoid_free(command); log_msg(1, "Finished sorting file %s", orig_fname); return(retval); } /** * Chop the filelist into sets. * Each fileset is a list of files whose total (uncompressed) size is usually * about X KB. Files bigger than 8X KB are placed in a "biggielist"; they will * be sliced and compressed separately from the regular files. * * @param filelist The big filelist (filelist.full) to chop up. * @param outdir The directory to place the files (filelist.N where N is * an integer, biggielist.txt, and LAST-FILELIST-NUMBER) created * @param maxsetsizeK Optimal size of a fileset (X above). * @return number of errors encountered (0 for success). */ int chop_filelist (char *filelist, char *outdir, long maxsetsizeK) { /*@ long ****************************************/ long lino = 0; long max_sane_size_for_a_file; long curr_set_size; long noof_lines; long siz; /*@ int *****************************************/ int i; long curr_set_no; /*@ buffers **************************************/ char *outfname; char *biggie_fname; char *incoming; char *tmp; char *acl_fname; char *xattr_fname; /*@ pointers ************************************/ FILE *fin; FILE *fout; FILE *fbig; /*@ structures **********************************/ struct stat buf; int err = 0; malloc_string(outfname); malloc_string(biggie_fname); incoming = malloc(MAX_STR_LEN*2); malloc_string(tmp); malloc_string(acl_fname); malloc_string(xattr_fname); assert_string_is_neither_NULL_nor_zerolength(filelist); assert_string_is_neither_NULL_nor_zerolength(outdir); assert( maxsetsizeK > 0); max_sane_size_for_a_file = 32L*1024L; // max_sane_size_for_a_file = maxsetsizeK*2; // if (max_sane_size_for_a_file > 32*1024) // { max_sane_size_for_a_file = 32*1024; } log_it("filelist=%s;", filelist); open_evalcall_form ("Dividing filelist into sets"); noof_lines = count_lines_in_file (filelist); if (!(fin = fopen (filelist, "r"))) { log_OS_error("Cannot openin filelist"); return(0); } curr_set_no = 0; curr_set_size = 0; sprintf (outfname, "%s/filelist.%ld", outdir, curr_set_no); sprintf (biggie_fname, "%s/biggielist.txt", outdir); log_it("outfname=%s; biggie_fname=%s", outfname, biggie_fname); if (!(fbig = fopen (biggie_fname, "w"))) { log_OS_error("Cannot openout biggie_fname"); err++; goto end_of_func; } if (!(fout = fopen (outfname, "w"))) { log_OS_error("Cannot openout outfname"); err++; goto end_of_func; } (void) fgets (incoming, MAX_STR_LEN*2-1, fin); while (!feof (fin)) { lino++; i = strlen (incoming) - 1; if (i < 0) { i = 0; } if (i > MAX_STR_LEN-1) { incoming[MAX_STR_LEN-30] = '\0'; log_msg(1, "Warning - truncating file %s's name", incoming); err++; } if (incoming[i] < 32) { incoming[i] = '\0'; } if (!strncmp (incoming, "/dev/", 5)) { siz = 1; } else if (lstat (incoming, &buf) != 0) { siz = 0; } else { siz = (long) (buf.st_size >> 10); } if (siz > max_sane_size_for_a_file) // && strcmp(incoming+strlen(incoming)-4, ".bz2") && strcmp(incoming+strlen(incoming)-4, ".tbz")) { fprintf (fbig, "%s\n", incoming); } else { curr_set_size += siz; fprintf (fout, "%s\n", incoming); if (curr_set_size > maxsetsizeK) { paranoid_fclose(fout); sort_file(outfname); curr_set_no++; curr_set_size = 0; sprintf (outfname, "%s/filelist.%ld", outdir, curr_set_no); if (!(fout = fopen (outfname, "w"))) { log_OS_error("Unable to openout outfname"); err++; goto end_of_func; } sprintf (tmp, "Fileset #%ld chopped ", curr_set_no - 1); update_evalcall_form ((int) (lino * 100 / noof_lines)); /* if (!g_text_mode) {newtDrawRootText(0,22,tmp);newtRefresh();} else {log_it(tmp);} */ } } (void) fgets (incoming, MAX_STR_LEN*2-1, fin); } paranoid_fclose (fin); paranoid_fclose (fout); paranoid_fclose (fbig); if (length_of_file(outfname) <= 2) { unlink(outfname); g_noof_sets --; } g_noof_sets = curr_set_no; sort_file(outfname); sort_file(biggie_fname); sprintf (outfname, "%s/LAST-FILELIST-NUMBER", outdir); sprintf (tmp, "%ld", curr_set_no); if (write_one_liner_data_file (outfname, tmp)) { log_OS_error("Unable to echo write one-liner to LAST-FILELIST-NUMBER"); err = 1;} if (curr_set_no == 0) { sprintf (tmp, "Only one fileset. Fine."); } else { sprintf (tmp, "Filelist divided into %ld sets", curr_set_no + 1); } log_msg(1, tmp); close_evalcall_form (); /* This is to work around an obscure bug in Newt; open a form, close it, carry on... I don't know why it works but it works. If you don't do this then update_progress_form() won't show the "time taken / time remaining" line. The bug only crops up AFTER the call to chop_filelist(). Weird. */ #ifndef _XWIN if (!g_text_mode) { open_progress_form ("", "", "", "", 100); newtPopHelpLine (); newtFormDestroy (g_progressForm); newtPopWindow (); } #endif end_of_func: paranoid_free(outfname); paranoid_free(biggie_fname); paranoid_free(incoming); paranoid_free(tmp); paranoid_free(acl_fname); paranoid_free(xattr_fname); return (err ? 0 : curr_set_no + 1); } /** * Free all the memory used by a filelist structure. * Since this may take a long time for large filelists, a progress bar will be displayed. * @param filelist The filelist to free. */ void free_filelist (struct s_node *filelist) { /*@ int's ********************************************************/ static int depth = 0; int percentage; /*@ long's *******************************************************/ static long i = 0; /*@ end vars *****************************************************/ assert(filelist!=NULL); if (depth == 0) { open_evalcall_form ("Freeing memory"); log_to_screen ("Freeing memory formerly occupied by filelist"); } depth++; if (filelist->ch == '\0') { if (!(i++ % 1111)) { percentage = (int) (i * 100 / g_original_noof_lines_in_filelist); update_evalcall_form (percentage); } } if (filelist->right) { free_filelist (filelist->right); filelist->right = NULL; } if (filelist->down) { /* if (!(i++ %39999)) { update_evalcall_form(0); } */ free_filelist (filelist->down); filelist->down = NULL; } filelist->ch = '\0'; paranoid_free (filelist); depth--; if (depth == 0) { close_evalcall_form (); log_it ("Finished freeing memory"); } } int call_exe_and_pipe_output_to_fd(char*syscall, FILE*pout) { FILE *pattr; char *tmp; pattr = popen(syscall, "r"); if (!pattr) { log_msg(1, "Failed to open fattr() %s", syscall); return(1); } if (feof(pattr)) { log_msg(1, "Failed to call fattr() %s", syscall); paranoid_pclose(pattr); return(2); } malloc_string(tmp); for (fgets (tmp, MAX_STR_LEN, pattr); !feof (pattr); fgets (tmp, MAX_STR_LEN, pattr)) { fputs(tmp, pout); } paranoid_pclose(pattr); paranoid_free(tmp); return(0); } int gen_aux_list(char*filelist, char*syscall_sprintf, char*auxlist_fname) { FILE*fin; FILE*pout; char*pout_command; char*syscall; char*file_to_analyze; int i; if (!(fin=fopen(filelist, "r"))) { log_msg(1, "Cannot openin filelist %s", filelist); return(1); } malloc_string(pout_command); sprintf(pout_command, "gzip -c1 > %s", auxlist_fname); if (!(pout=popen(pout_command, "w"))) { log_msg(1, "Cannot openout auxlist_fname %s", auxlist_fname); fclose(fin); paranoid_free(pout_command); return(4); } malloc_string(syscall); malloc_string(file_to_analyze); for (fgets (file_to_analyze, MAX_STR_LEN, fin); !feof (fin); fgets (file_to_analyze, MAX_STR_LEN, fin)) { i = strlen(file_to_analyze); if (i>0 && file_to_analyze[i-1]<32) { file_to_analyze[i-1]='\0'; } log_msg(8, "Analyzing %s", file_to_analyze); sprintf(syscall, syscall_sprintf, file_to_analyze); strcat(syscall, " 2>> /dev/null"); // " MONDO_LOGFILE); call_exe_and_pipe_output_to_fd(syscall, pout); } paranoid_fclose(fin); paranoid_pclose(pout); paranoid_free(file_to_analyze); paranoid_free(syscall); paranoid_free(pout_command); return(0); } int get_acl_list(char*filelist, char*facl_fname) { char*command; int retval=0; malloc_string(command); sprintf(command, "touch %s", facl_fname); run_program_and_log_output(command, 8); if (find_home_of_exe("getfacl")) { // sort_file(filelist); // FIXME - filelist chopper sorts, so this isn't necessary sprintf(command, "cat %s | getfacl --all-effective -P - 2>> %s | gzip -c1 > %s 2>> %s", filelist, MONDO_LOGFILE, facl_fname, MONDO_LOGFILE); iamhere(command); retval = system(command); } paranoid_free(command); return(retval); } int get_fattr_list(char*filelist, char*fattr_fname) { char *command; int retval=0; malloc_string(command); sprintf(command, "touch %s", fattr_fname); run_program_and_log_output(command, 8); if (find_home_of_exe("getfattr")) { // sort_file(filelist); // FIXME - filelist chopper sorts, so this isn't necessary retval = gen_aux_list(filelist, "getfattr --en=hex -P -d \"%s\"", fattr_fname); } paranoid_free(command); return(retval); } /* int set_acl_list(char*masklist, char*acl_fname) { char*command; int retval=0; if (length_of_file(acl_fname) <= 0) { return(0); } log_msg(1, "FIXME - not using masklist"); malloc_string(command); if (find_home_of_exe("setfacl")) { sprintf(command, "cat %s | gzip -dc | setfacl --restore - 2>> %s", acl_fname, MONDO_LOGFILE); log_msg(1, "command = %s", command); retval = system(command); } paranoid_free(command); return(retval); } */ int set_EXAT_list(char*orig_msklist, char*original_exat_fname, char*executable) { const int my_depth=8; char*command, *syscall_pin, *syscall_pout, *incoming; char*current_subset_file, *current_master_file, *masklist; int retval=0; int i; char*p,*q; FILE*pin, *pout, *faclin; malloc_string(command); log_msg(1, "set_EXAT_list(%s, %s, %s)", orig_msklist, original_exat_fname, executable); if (!orig_msklist || !orig_msklist[0] || !does_file_exist(orig_msklist)) { log_msg(1, "No masklist provided. I shall therefore set ALL attributes."); sprintf(command, "cat %s | gzip -dc | %s --restore - 2>> %s", original_exat_fname, executable, MONDO_LOGFILE); log_msg(1, "command = %s", command); retval = system(command); paranoid_free(command); log_msg(1, "Returning w/ retval=%d", retval); return(retval); } if (length_of_file(original_exat_fname) <= 0) { log_msg(1, "original_exat_fname %s is empty or missing, so no need to set EXAT list", original_exat_fname); paranoid_free(command); return(0); } malloc_string(incoming); malloc_string(masklist); malloc_string(current_subset_file); malloc_string(current_master_file); malloc_string(syscall_pin); malloc_string(syscall_pout); sprintf(masklist, "/tmp/%d.%d.mask", (int)(random()%32768), (int)(random()%32768)); sprintf(command, "cp -f %s %s", orig_msklist, masklist); run_program_and_log_output(command, 1); sort_file(masklist); current_subset_file[0] = current_master_file[0] = '\0'; sprintf(syscall_pin, "cat %s | gzip -dc", original_exat_fname); sprintf(syscall_pout, "%s --restore - 2>> %s", executable, MONDO_LOGFILE); log_msg(1, "syscall_pin = %s", syscall_pin); log_msg(1, "syscall_pout = %s", syscall_pout); pout = popen(syscall_pout, "w"); if (!pout) { iamhere("Unable to openout to syscall_pout"); return(1); } pin = popen(syscall_pin, "r"); if (!pin) { pclose(pout); iamhere("Unable to openin from syscall"); return(1); } faclin = fopen(masklist, "r"); if (!faclin) { pclose(pin); pclose(pout); iamhere("Unable to openin masklist"); return(1); } // printf("Hi there. Starting the loop\n"); fgets(current_subset_file, MAX_STR_LEN, faclin); fgets(incoming, MAX_STR_LEN, pin); while(!feof(pin) && !feof(faclin)) { // printf("incoming = %s", incoming); strcpy(current_master_file, incoming+8); p = current_subset_file; if (*p=='/') {p++;} i = strlen(p); if (i>0 && p[i-1]<32) { p[i-1] = '\0'; } q = current_master_file; if (*q=='/') {q++;} i = strlen(q); if (i>0 && q[i-1]<32) { q[i-1] = '\0'; } i = strcmp(p,q); log_msg(my_depth, "'%s' v '%s' --> %d\n", p, q, i); // printf("%s v %s --> %d\n", p, q, i); if (i<0) { // read another subset file in. log_msg(my_depth, "Reading next subset line in\n\n"); fgets(current_subset_file, MAX_STR_LEN, faclin); continue; } if (!i) { fputs(incoming, pout); } fgets(incoming, MAX_STR_LEN, pin); if (!i) { log_msg(my_depth, "Copying master %s", q); } // if (!i) { printf("Match --- %s\n", q); } while (!feof(pin) && strncmp(incoming, "# file: ", 8)) { if (!i) { // printf("%s", incoming); fputs(incoming, pout); } fgets(incoming, MAX_STR_LEN, pin); } if (!i) { fgets(current_subset_file, MAX_STR_LEN, faclin); } } while(!feof(pin)) { fgets (incoming, MAX_STR_LEN, pin); } fclose(faclin); pclose(pin); pclose(pout); // printf("OK, loop is done\n"); unlink(masklist); paranoid_free(current_subset_file); paranoid_free(current_master_file); paranoid_free(syscall_pout); paranoid_free(syscall_pin); paranoid_free(masklist); paranoid_free(incoming); paranoid_free(command); return(retval); } int set_fattr_list(char*masklist, char*fattr_fname) { return(set_EXAT_list(masklist, fattr_fname, "setfattr")); } int set_acl_list(char*masklist, char*acl_fname) { return(set_EXAT_list(masklist, acl_fname, "setfacl")); } /* if (find_home_of_exe("setfattr")) { sprintf(command, "cat %s | gzip -dc | setfattr --restore - 2>> %s", acl_fname, MONDO_LOGFILE); log_msg(1, "command = %s", command); retval = system(command); } paranoid_free(acl_subset_fname); paranoid_free(syscall_pin); paranoid_free(command); return(retval); */ /** * Get the number of the last fileset in the backup. * @param bkpinfo The backup information structure. Only the @c bkpinfo->tmpdir field is used. * @return The last filelist number. * @note This function should only be called at restore-time. */ int get_last_filelist_number (struct s_bkpinfo *bkpinfo) { /*@ buffers ******************************************************/ char val_sz[MAX_STR_LEN]; char cfg_fname[MAX_STR_LEN]; /* char tmp[MAX_STR_LEN]; remove stan benoit apr 2002 */ /*@ long *********************************************************/ int val_i; /*@ end vars *****************************************************/ assert(bkpinfo!=NULL); sprintf (cfg_fname, "%s/mondo-restore.cfg", bkpinfo->tmpdir); read_cfg_var (cfg_fname, "last-filelist-number", val_sz); val_i = atoi (val_sz); if (val_i <= 0) { val_i = 500; } return (val_i); } /** * Add a string at @p startnode. * @param startnode The node to start at when searching for where to add the string. * @param string_to_add The string to add. * @return 0 for success, 1 for failure. * @bug I don't understand this function. Would someone care to explain it? */ int add_string_at_node (struct s_node *startnode, char *string_to_add) { /*@ int *********************************************************/ int noof_chars; int i; int res; /*@ sturctures **************************************************/ struct s_node *node, *newnode; /*@ char *******************************************************/ char char_to_add; /*@ bools *******************************************************/ const bool sosodef = FALSE; static int depth=0; static char original_string[MAX_STR_LEN]; assert(startnode!=NULL); assert(string_to_add!=NULL); if (!depth) { strcpy(original_string, string_to_add); } noof_chars = strlen (string_to_add) + 1; /* we include the '\0' */ /* walk across tree if necessary */ node = startnode; char_to_add = string_to_add[0]; if (node->right != NULL && node->ch < char_to_add) { log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth, char_to_add, node->ch, (node->right)->ch); return (add_string_at_node (node->right, string_to_add)); } /* walk down tree if appropriate */ if (node->down != NULL && node->ch == char_to_add) { log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_add); depth++; res = add_string_at_node (node->down, string_to_add + 1); depth--; return (res); } if (char_to_add == '\0' && node->ch =='\0') { log_msg(6, "%s already in tree", original_string); return(1); } /* add here */ if (!(newnode = (struct s_node*)malloc (sizeof (struct s_node)))) { log_to_screen ("failed to malloc"); depth--; return (1); } if (char_to_add < node->ch) // add to the left of node { log_msg(7, "depth=%d char=%c --- adding (left)", depth, char_to_add); memcpy((void*)newnode, (void*)node, sizeof(struct s_node)); node->right = newnode; } else if (char_to_add > node->ch) // add to the right of node { log_msg(7, "depth=%d char=%c --- adding (right)", depth, char_to_add); newnode->right = node->right; // newnode is to the RIGHT of node node->right = newnode; node = newnode; } // from now on, we're working on 'node' node->down = NULL; node->ch = char_to_add; node->expanded = node->selected = FALSE; if (char_to_add == '\0') { log_msg (6, "Added %s OK", original_string); return (0); } // add the rest log_msg(6, "Adding remaining chars ('%s')", string_to_add+1); for (i = 1; i < noof_chars; i++) { if (!(node->down = (struct s_node*)malloc (sizeof (struct s_node)))) { log_to_screen ("%s - failed to malloc", string_to_add); return (1); } node = node->down; char_to_add = string_to_add[i]; log_msg(6, "Adding '%c'", char_to_add); node->ch = char_to_add; node->right = node->down = NULL; node->expanded = node->selected = FALSE; if (!node->ch) { node->selected = sosodef; } } log_msg (6, "Finally - added %s OK", original_string); return (0); } /** * Load a filelist into a struct s_node. * When you are done with the filelist, call free_filelist(). * @param filelist_fname The file to load the filelist from. * @return A filelist tree structure. */ struct s_node * load_filelist (char *filelist_fname) { /*@ structures **************************************************/ struct s_node *filelist; /*@ pointers ****************************************************/ FILE *pin; /*@ buffers *****************************************************/ char command_to_open_fname[MAX_STR_LEN]; char fname[MAX_STR_LEN]; char tmp[MAX_STR_LEN]; int pos_in_fname; /*@ int *********************************************************/ int percentage; /*@ long ********************************************************/ long lines_in_filelist; long lino = 0; /*@ end vars ****************************************************/ assert_string_is_neither_NULL_nor_zerolength(filelist_fname); if (!does_file_exist(filelist_fname)) { fatal_error("filelist does not exist -- cannot load it"); } log_to_screen ("Loading filelist"); sprintf(command_to_open_fname, "gzip -dc %s", filelist_fname); sprintf(tmp, "zcat %s | wc -l", filelist_fname); log_msg(6, "tmp = %s", tmp); lines_in_filelist = atol(call_program_and_get_last_line_of_output(tmp)); if (lines_in_filelist < 3) { log_to_screen("Warning - surprisingly short filelist."); } g_original_noof_lines_in_filelist = lines_in_filelist; if (!(filelist = (struct s_node*)malloc (sizeof (struct s_node)))) { return (NULL); } filelist->ch = '/'; filelist->right = NULL; filelist->down = malloc(sizeof(struct s_node)); filelist->expanded = filelist->selected = FALSE; (filelist->down)->ch = '\0'; (filelist->down)->right = (filelist->down)->down = FALSE; (filelist->down)->expanded = (filelist->down)->selected = FALSE; if (!(pin = popen (command_to_open_fname, "r"))) { log_OS_error("Unable to openin filelist_fname"); return (NULL); } open_evalcall_form ("Loading filelist from disk"); for (fgets (fname, MAX_STR_LEN, pin); !feof (pin); fgets (fname, MAX_STR_LEN, pin)) { if ((fname[strlen(fname)-1]==13 || fname[strlen(fname)-1]==10) && strlen(fname)>0) { fname[strlen(fname)-1] = '\0'; } // strip_spaces (fname); if (!strlen (fname)) { continue; } for(pos_in_fname=0; fname[pos_in_fname]!='\0'; pos_in_fname++) { if (fname[pos_in_fname]!='/') { continue; } strcpy(tmp, fname); tmp[pos_in_fname] = '\0'; if (strlen(tmp)) { add_string_at_node (filelist, tmp); } } add_string_at_node (filelist, fname); if (!(++lino % 1111)) { percentage = (int) (lino * 100 / lines_in_filelist); update_evalcall_form (percentage); } } paranoid_pclose(pin); close_evalcall_form (); log_it ("Finished loading filelist"); return (filelist); } /** * Log a list of files in @p node. * @param node The toplevel node to use. */ void show_filelist (struct s_node *node) { static int depth=0; static char current_string[200]; if (depth==0) { log_msg(0, "----------------show filelist--------------"); } current_string[depth] = node->ch; log_msg(3, "depth=%d", depth); if (node->down) { log_msg(3, "moving down"); depth++; show_filelist(node->down); depth--; } if (!node->ch) { log_msg(0, "%s\n", current_string); } if (node->right) { log_msg(3, "moving right"); show_filelist(node->right); } if (depth==0) { log_msg(0, "----------------show filelist--------------"); } return; } /** * Reset the filelist to the state it was when it was loaded. This does not * touch the file on disk. * @param filelist The filelist tree structure. */ void reload_filelist (struct s_node *filelist) { assert(filelist!=NULL); toggle_node_selection (filelist, FALSE); toggle_path_expandability (filelist, "/", FALSE); toggle_all_root_dirs_on (filelist); } /** * Save a filelist tree structure to disk. * @param filelist The filelist tree structure to save. * @param outfname Where to save it. */ void save_filelist (struct s_node *filelist, char *outfname) { /*@ int **********************************************************/ static int percentage; static int depth = 0; /*@ buffers ******************************************************/ static char str[MAX_STR_LEN]; /*@ structures ***************************************************/ struct s_node *node; /*@ pointers *****************************************************/ static FILE *fout = NULL; /*@ long *********************************************************/ static long lines_in_filelist = 0; static long lino = 0; /*@ end vars ****************************************************/ assert(filelist!=NULL); assert(outfname!=NULL); // will be zerolength if save_filelist() is called by itself if (depth == 0) { log_to_screen ("Saving filelist"); if (!(fout = fopen (outfname, "w"))) { fatal_error("Cannot openout/save filelist"); } lines_in_filelist = g_original_noof_lines_in_filelist; /* set by load_filelist() */ open_evalcall_form ("Saving selection to disk"); } for (node = filelist; node != NULL; node = node->right) { str[depth] = node->ch; log_msg(5, "depth=%d ch=%c", depth, node->ch); if (!node->ch) { // if (node->selected) // { fprintf (fout, "%s\n", str); // } if (!(++lino % 1111)) { percentage = (int) (lino * 100 / lines_in_filelist); update_evalcall_form (percentage); } } if (node->down) { depth++; save_filelist (node->down, ""); depth--; } } if (depth == 0) { paranoid_fclose (fout); close_evalcall_form (); log_it ("Finished saving filelist"); } } /** * Toggle all root dirs on. * @param filelist The filelist tree structure to operate on. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_all_root_dirs_on (struct s_node *filelist) { /*@ structures ***************************************************/ struct s_node *node; /*@ int **********************************************************/ static int depth = 0; static int root_dirs_expanded; /*@ buffers ******************************************************/ static char filename[MAX_STR_LEN]; /*@ end vars ****************************************************/ assert(filelist!=NULL); if (depth == 0) { log_it ("Toggling all root dirs ON"); root_dirs_expanded = 0; } for (node = filelist; node != NULL; node = node->right) { filename[depth] = node->ch; if (node->ch == '\0' && strlen (filename) > 1 && (!strchr (filename + 1, '/'))) { node->selected = FALSE; node->expanded = TRUE; // log_it (filename); root_dirs_expanded++; } if (node->down) { depth++; toggle_all_root_dirs_on (node->down); depth--; } } if (depth == 0) { log_it ("Finished toggling all root dirs ON"); } } /** * Toggle the expandability of a path. * @param filelist The filelist tree to operate on. * @param pathname The path to toggle expandability of. * @param on_or_off Whether to toggle it on or off. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_path_expandability (struct s_node *filelist, char *pathname, bool on_or_off) { /*@ int *********************************************************/ static int depth = 0; static int total_expanded; static int root_depth; int j; /*@ structures **************************************************/ struct s_node *node; /*@ buffers *****************************************************/ static char current_filename[MAX_STR_LEN]; /* char tmp[MAX_STR_LEN+2]; */ /*@ end vars ****************************************************/ assert(filelist!=NULL); assert_string_is_neither_NULL_nor_zerolength(pathname); if (depth == 0) { total_expanded = 0; // log_it ("Toggling path's expandability"); for (root_depth = (int) strlen (pathname); root_depth > 0 && pathname[root_depth - 1] != '/'; root_depth--); if (root_depth < 2) { root_depth = (int) strlen (pathname); } } for (node = filelist; node != NULL; node = node->right) { current_filename[depth] = node->ch; if (node->down) { depth++; toggle_path_expandability (node->down, pathname, on_or_off); depth--; } if (node->ch == '\0') { if (!strncmp (pathname, current_filename, strlen (pathname))) { for (j = root_depth; current_filename[j] != '/' && current_filename[j] != '\0'; j++); if (current_filename[j] != '\0') { for (j++; current_filename[j] != '/' && current_filename[j] != '\0'; j++); } if (current_filename[j] == '\0') { node->expanded = (!strcmp (pathname, current_filename) ? TRUE : on_or_off); } } } if (node->expanded) { if (total_expanded < ARBITRARY_MAXIMUM - 32 || !strrchr (current_filename + strlen (pathname), '/')) { total_expanded++; } else { node->expanded = FALSE; } } } if (depth == 0) { // log_it ("Finished toggling expandability"); } } /** * Toggle whether a path is selected. * @param filelist The filelist tree to operate on. * @param pathname The path to toggle selection of. * @param on_or_off Whether to toggle it on or off. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_path_selection (struct s_node *filelist, char *pathname, bool on_or_off) { /*@ int **********************************************************/ static int depth = 0; int j; /*@ structures ***************************************************/ struct s_node *node; /*@ buffers ******************************************************/ static char current_filename[MAX_STR_LEN]; char tmp[MAX_STR_LEN + 2]; /*@ end vars ****************************************************/ assert(filelist!=NULL); assert_string_is_neither_NULL_nor_zerolength(pathname); if (depth == 0) { log_it ("Toggling path's selection"); } for (node = filelist; node != NULL; node = node->right) { current_filename[depth] = node->ch; if (node->down) { depth++; toggle_path_selection (node->down, pathname, on_or_off); depth--; } if (node->ch == '\0') { if (!strncmp (pathname, current_filename, strlen (pathname))) { for (j = 0; pathname[j] != '\0' && pathname[j] == current_filename[j]; j++); if (current_filename[j] == '/' || current_filename[j] == '\0') { node->selected = on_or_off; sprintf (tmp, "%s is now %s\n", current_filename, (on_or_off ? "ON" : "OFF")); } } } } if (depth == 0) { log_it ("Finished toggling selection"); } } /** * Toggle node selection of a filelist tree. * @param filelist The filelist tree to operate on. * @param on_or_off Whether to toggle selection on or off. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_node_selection (struct s_node *filelist, bool on_or_off) { /*@ structure ***************************************************/ struct s_node *node; /*@ end vars ****************************************************/ assert(filelist!=NULL); for (node = filelist; node != NULL; node = node->right) { if (node->ch == '/') { continue; } /* don't go deep */ if (node->ch == '\0') { node->selected = on_or_off; } if (node->down) { toggle_node_selection (node->down, on_or_off); } } } /** * The pathname to the skeleton filelist, used to give better progress reporting for mondo_makefilelist(). */ char *g_skeleton_filelist=NULL; /** * Number of entries in the skeleton filelist. */ long g_skeleton_entries=0; /** * Wrapper around mondo_makefilelist(). * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->differential * - @c bkpinfo->exclude_paths * - @c bkpinfo->include_paths * - @c bkpinfo->make_filelist * - @c bkpinfo->scratchdir * - @c bkpinfo->tmpdir * @return 0 for success, nonzero for failure. * @see mondo_makefilelist */ int prepare_filelist (struct s_bkpinfo *bkpinfo) { /*@ int *****************************************************/ int res = 0; // patch by Herman Kuster // end patch int *p_res = &res; /*@ buffers *************************************************/ // char command[MAX_STR_LEN*2]; /*@ i don't have any idea ***********************************/ assert(bkpinfo!=NULL); log_it ("tmpdir=%s; scratchdir=%s", bkpinfo->tmpdir, bkpinfo->scratchdir); if (bkpinfo->make_filelist) { mvaddstr_and_log_it (g_currentY, 0, "Making catalog of files to be backed up"); } else { mvaddstr_and_log_it (g_currentY, 0, "Using supplied catalog of files to be backed up"); } if (bkpinfo->make_filelist) { res = mondo_makefilelist( MONDO_LOGFILE, bkpinfo->tmpdir, bkpinfo->scratchdir, bkpinfo->include_paths, bkpinfo->exclude_paths, bkpinfo->differential, NULL); } else { res = mondo_makefilelist( MONDO_LOGFILE, bkpinfo->tmpdir, bkpinfo->scratchdir, NULL, bkpinfo->exclude_paths, bkpinfo->differential, bkpinfo->include_paths); } if (res) { log_OS_error("Call to mondo-makefilelist failed"); *p_res ++; mvaddstr_and_log_it (g_currentY++, 74, "Failed."); } else { mvaddstr_and_log_it (g_currentY++, 74, "Done."); } return (res); } /** * Recursively list all files in @p dir newer than @p time_of_last_full_backup to @p fout. * @param dir The directory to list to @p fout. * @param sth The directories to skip (exclude). * @param fout The file to write everything to. * @param time_of_last_full_backup Only backup files newer than this (0 to disable). * @return 0, always. * @bug Return value should be @c void. */ int open_and_list_dir(char*dir, char*sth, FILE*fout, time_t time_of_last_full_backup) { DIR *dip; struct dirent *dit; struct stat statbuf; char new[MAX_STR_LEN]; char *tmp; char *sth_B; static int percentage=0; char *ith_B; char *skip_these; char *new_with_spaces; static char *name_of_evalcall_form; int i; static int depth=0; char *p; static int counter=0; static int uberctr=0; static char *find_skeleton_marker; static long skeleton_lino=0; static time_t last_time=0; time_t this_time; malloc_string(tmp); malloc_string(sth_B); malloc_string(ith_B); malloc_string(new_with_spaces); p = strrchr(dir, '/'); if (p) { if (!strcmp(p, "/.") || !strcmp(p, "/..")) { return(0); } } if (!depth) { malloc_string(name_of_evalcall_form); malloc_string(find_skeleton_marker); #if linux // 2.6 has /sys as a proc-type thing -- must be excluded sprintf(tmp, "find %s -maxdepth %d -path /proc -prune -o -path /tmp -prune -o -path /sys -prune -o -path /dev/shm -prune -o -path /media/floppy -prune -o -type d -a -print > %s 2> /dev/null", dir, MAX_SKEL_DEPTH, g_skeleton_filelist); #else // On BSD, for example, /sys is the kernel sources -- don't exclude sprintf(tmp, "find %s -maxdepth %d -path /proc -prune -o -path /tmp -prune -o -type d -a -print > %s 2> /dev/null", dir, MAX_SKEL_DEPTH, g_skeleton_filelist); #endif system(tmp); sprintf(tmp, "wc -l %s | awk '{print $1;}'", g_skeleton_filelist); g_skeleton_entries = 1 + atol(call_program_and_get_last_line_of_output(tmp)); sprintf(name_of_evalcall_form, "Making catalog of %s", dir); open_evalcall_form (name_of_evalcall_form); find_skeleton_marker[0] = '\0'; skeleton_lino=1; log_msg(5, "entries = %ld", g_skeleton_entries); percentage=0; } else if (depth<=MAX_SKEL_DEPTH) // update evalcall form if appropriate { sprintf(find_skeleton_marker, "fgrep -v \"%s\" %s > %s.new 2> /dev/null", dir, g_skeleton_filelist, g_skeleton_filelist); // log_msg(0, "fsm = %s", find_skeleton_marker); if (!system(find_skeleton_marker)) { percentage = (int)(skeleton_lino*100/g_skeleton_entries); skeleton_lino++; // log_msg(5, "Found %s", dir); // log_msg(2, "Incrementing skeleton_lino; now %ld/%ld (%d%%)", skeleton_lino, g_skeleton_entries, percentage); sprintf(find_skeleton_marker, "mv -f %s.new %s", g_skeleton_filelist, g_skeleton_filelist); // log_msg(6, "fsm = %s", find_skeleton_marker); run_program_and_log_output(find_skeleton_marker, 8); time(&this_time); if (this_time != last_time) { last_time = this_time; #ifndef _XWIN if (!g_text_mode) { sprintf(tmp, "Reading %-68s", dir); newtDrawRootText (0, g_noof_rows - 3, tmp); } #endif update_evalcall_form(percentage); } } } depth++; // log_msg(0, "Cataloguing %s", dir); if (sth[0]==' ') { skip_these = sth; } else { skip_these = sth_B; sprintf(skip_these, " %s ", sth); } sprintf(new_with_spaces, " %s ", dir); if ((dip = opendir(dir)) == NULL) { log_OS_error("opendir"); } else if (strstr(skip_these, new_with_spaces)) { fprintf(fout, "%s\n", dir); // if excluded dir then print dir ONLY } else { fprintf(fout, "%s\n", dir); while ((dit = readdir(dip)) != NULL) { i++; strcpy(new, dir); if (strcmp(dir, "/")) { strcat(new, "/"); } strcat(new, dit->d_name); new_with_spaces[0] = ' '; strcpy(new_with_spaces+1, new); strcat(new_with_spaces, " "); if (strstr(skip_these, new_with_spaces)) { fprintf(fout, "%s\n", new); } else { if (!lstat(new, &statbuf)) { if (!S_ISLNK(statbuf.st_mode) && S_ISDIR(statbuf.st_mode)) { open_and_list_dir(new, skip_these, fout, time_of_last_full_backup); } else { if (time_of_last_full_backup==0 || time_of_last_full_backup128) { counter=0; uberctr++; sprintf(tmp, " %c ", special_dot_char(uberctr)); #ifndef _XWIN if (!g_text_mode) { newtDrawRootText (77, g_noof_rows - 3, tmp); newtRefresh(); } #endif } } } } } } } if (dip) { if (closedir(dip) == -1) { log_OS_error("closedir"); } } depth--; if (!depth) { close_evalcall_form (); paranoid_free (name_of_evalcall_form); paranoid_free(find_skeleton_marker); unlink(g_skeleton_filelist); log_msg(5, "g_skeleton_entries = %ld", g_skeleton_entries); } paranoid_free(tmp); paranoid_free(sth_B); paranoid_free(ith_B); paranoid_free(new_with_spaces); return(0); } /** * Get the next entry in the space-separated list in @p incoming. * So if @p incoming was '"one and two" three four', we would * return "one and two". * @param incoming The list to get the next entry from. * @return The first item in the list (respecting double quotes). * @note The returned string points to static data that will be overwritten with each call. */ char *next_entry(char*incoming) { static char sz_res[MAX_STR_LEN]; char *p; bool in_quotes=FALSE; strcpy(sz_res, incoming); p = sz_res; while((*p!=' ' || in_quotes) && *p!='\0') { if (*p=='\"') { in_quotes = !in_quotes; } p++; } *p = '\0'; return(sz_res); } /** * Create the filelist for the backup. It will be stored in [scratchdir]/archives/filelist.full. * @param logfile Unused. * @param tmpdir The tmpdir of the backup. * @param scratchdir The scratchdir of the backup. * @param include_paths The paths to back up, or NULL if you're using a user-defined filelist. * @param excp The paths to NOT back up. * @param differential The differential level (currently only 0 and 1 are supported). * @param userdef_filelist The user-defined filelist, or NULL if you're using @p include_paths. * @return 0, always. * @bug @p logfile is unused. * @bug Return value is meaningless. */ int mondo_makefilelist(char*logfile, char*tmpdir, char*scratchdir, char*include_paths, char*excp, int differential, char *userdef_filelist) { char sz_datefile_wildcard[] = "/var/cache/mondo-archive/difflevel.%d"; char *p, *q; char sz_datefile[80]; char *sz_filelist, *exclude_paths, *tmp; int i; FILE*fout; char *command; time_t time_of_last_full_backup=0; struct stat statbuf; malloc_string(command); malloc_string(tmp); malloc_string(sz_filelist); malloc_string(g_skeleton_filelist); if (!(exclude_paths = malloc(1000))) { fatal_error("Cannot malloc exclude_paths"); } log_msg(3, "Trying to write test string to exclude_paths"); strcpy(exclude_paths, "/blah /froo"); log_msg(3, "...Success!"); sprintf(sz_datefile, sz_datefile_wildcard, 0); if (!include_paths && !userdef_filelist) { fatal_error("Please supply either include_paths or userdef_filelist"); } // make hole for filelist sprintf(command, "mkdir -p %s/archives", scratchdir); paranoid_system(command); sprintf(sz_filelist, "%s/tmpfs/filelist.full", tmpdir); make_hole_for_file(sz_filelist); if (differential==0) { // restore last good datefile if it exists sprintf(command, "cp -f %s.aborted %s", sz_datefile, sz_datefile); run_program_and_log_output(command, 3); // backup last known good datefile just in case :) if (does_file_exist(sz_datefile)) { sprintf(command, "mv -f %s %s.aborted", sz_datefile, sz_datefile); paranoid_system(command); } make_hole_for_file(sz_datefile); write_one_liner_data_file(sz_datefile, call_program_and_get_last_line_of_output("date +%s")); } else if (lstat(sz_datefile, &statbuf)) { log_msg(2, "Warning - unable to find date of previous backup. Full backup instead."); differential = 0; time_of_last_full_backup = 0; } else { time_of_last_full_backup = statbuf.st_mtime; log_msg(2, "Differential backup. Yay."); } // use user-specified filelist (if specified) if (userdef_filelist) { log_msg(1, "Using the user-specified filelist - %s - instead of calculating one", userdef_filelist); sprintf(command, "cp -f %s %s", userdef_filelist, sz_filelist); if (run_program_and_log_output(command, 3)) { fatal_error("Failed to copy user-specified filelist"); } } else { log_msg(2, "include_paths = '%s'", include_paths); log_msg(1, "Calculating filelist"); sprintf(exclude_paths, " %s %s %s %s %s %s . .. \ "MNT_CDROM" "MNT_FLOPPY" /media/cdrom /media/cdrecorder \ /proc /sys /tmp /root/images/mondo /root/images/mindi ", excp, call_program_and_get_last_line_of_output("locate /win386.swp 2> /dev/null"), call_program_and_get_last_line_of_output("locate /hiberfil.sys 2> /dev/null"), call_program_and_get_last_line_of_output("locate /pagefile.sys 2> /dev/null"), (tmpdir[0]=='/' && tmpdir[1]=='/')?(tmpdir+1):tmpdir, (scratchdir[0]=='/' && scratchdir[1]=='/')?(scratchdir+1):scratchdir ); log_msg(2, "Excluding paths = '%s'", exclude_paths); log_msg(2, "Generating skeleton filelist so that we can track our progress"); sprintf(g_skeleton_filelist, "%s/tmpfs/skeleton.txt", tmpdir); make_hole_for_file(g_skeleton_filelist); log_msg(4, "g_skeleton_entries = %ld", g_skeleton_entries); log_msg(2, "Opening out filelist to %s", sz_filelist); if (!(fout = fopen(sz_filelist, "w"))) { fatal_error("Cannot openout to sz_filelist"); } i = 0; if (strlen(include_paths)==0) { log_msg(1, "Including only '/' in %s", sz_filelist); open_and_list_dir("/", exclude_paths, fout, time_of_last_full_backup); } else { p = include_paths; while(*p) { q = next_entry(p); log_msg(1, "Including %s in filelist %s", q, sz_filelist); open_and_list_dir(q, exclude_paths, fout, time_of_last_full_backup); p += strlen(q); while(*p==' ') { p++; } } } paranoid_fclose(fout); } log_msg(2, "Copying new filelist to scratchdir"); sprintf(command, "mkdir -p %s/archives", scratchdir); paranoid_system(command); sprintf(command, "cp -f %s %s/archives/", sz_filelist, scratchdir); paranoid_system(command); sprintf(command, "mv -f %s %s", sz_filelist, tmpdir); paranoid_system(command); log_msg(2, "Freeing variables"); paranoid_free(sz_filelist); paranoid_free(command); paranoid_free(exclude_paths); paranoid_free(tmp); paranoid_free(g_skeleton_filelist); log_msg(2, "Exiting"); return(0); } /** * Locate the string @p string_to_find in the tree rooted at @p startnode. * @param startnode The node containing the root of the directory tree. * @param string_to_find The string to look for at @p startnode. * @return The node containing the last element of @p string_to_find, or NULL if * it was not found. */ struct s_node *find_string_at_node (struct s_node *startnode, char *string_to_find) { /*@ int *********************************************************/ int noof_chars; static int depth=0; static char original_string[MAX_STR_LEN]; /*@ sturctures **************************************************/ struct s_node *node; /*@ char *******************************************************/ char char_to_find; /*@ bools *******************************************************/ if (!depth) { strcpy(original_string, string_to_find); } assert(startnode!=NULL); assert(string_to_find!=NULL); noof_chars = strlen (string_to_find) + 1; /* we include the '\0' */ log_msg(7, "starting --- str=%s", string_to_find); /* walk across tree if necessary */ node = startnode; char_to_find = string_to_find[0]; if (node->right != NULL && node->ch < char_to_find) { log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth, char_to_find, node->ch, (node->right)->ch); return (find_string_at_node (node->right, string_to_find)); } /* walk down tree if appropriate */ if (node->down != NULL && node->ch == char_to_find) { log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_find); depth++; node = find_string_at_node (node->down, string_to_find + 1); depth--; return (node); } if (char_to_find == '\0' && node->ch =='\0') { log_msg(7, "%s is in tree", original_string); return(node); } else { log_msg(7, "%s is NOT in tree", original_string); return(NULL); } } /** * Write all entries in @p needles_list_fname which are also in * @p filelist to @p matches_list_fname. * @param needles_list_fname A file containing strings to look for, 1 per line. * @param filelist The node for the root of the directory structure to search in. * @param matches_list_fname The filename where we should put the matches. * @return The number of matches found. */ long save_filelist_entries_in_common( char*needles_list_fname, struct s_node *filelist, char*matches_list_fname, bool use_star) { int retval=0; struct s_node *found_node; FILE*fin; FILE*fout; char *fname; char *tmp; size_t len = 0; // Scrub's patch doesn't work without that // log_msg(1, "use_star = %s", (use_star)?"TRUE":"FALSE"); malloc_string(fname); malloc_string(tmp); log_msg(5, "starting"); log_msg(5, "needles_list_fname = %s", needles_list_fname); log_msg(5, "matches_list_fname = %s", matches_list_fname); if (!(fin = fopen(needles_list_fname, "r"))) { fatal_error("Cannot openin needles_list_fname"); } if (!(fout= fopen(matches_list_fname, "w"))) { fatal_error("Cannot openout matches_list_fname"); } while(!feof(fin)) { // fscanf(fin, "%s\n", fname); len = MAX_STR_LEN-1; getline(& fname, & len, fin); // patch by Scrub if (!use_star) { if (fname[0]=='/') { strcpy(tmp, fname); } else { tmp[0]='/'; strcpy(tmp+1, fname); } strcpy(fname, tmp); } while (strlen(fname)>0 && fname[strlen(fname)-1]<32) { fname[strlen(fname)-1] = '\0'; } /* if (strlen(fname)>3 && fname[strlen(fname)-1]=='/') { fname[strlen(fname)-1] = '\0'; } if (strlen(fname)==0) { continue; } sprintf(temporary_string, "echo \"Looking for '%s'\" >> /tmp/looking.txt", fname); system(temporary_string); */ log_msg(5, "Looking for '%s'", fname); found_node = find_string_at_node(filelist, fname); if (found_node) { if (found_node->selected) { // if (use_star) if (fname[0]=='/') { strcpy(tmp, fname+1); strcpy(fname, tmp); } log_msg(5, "Found '%s'", fname); turn_wildcard_chars_into_literal_chars(tmp, fname); fprintf(fout, "%s\n", tmp); retval++; } } } paranoid_fclose(fout); paranoid_fclose(fin); paranoid_free(fname); paranoid_free(tmp); return(retval); } /** * Add all files listed in @p list_of_files_fname to the directory structure rooted at * @p filelist. * @param filelist The top node of the directory structure to add the files to. * @param list_of_files_fname The file containing the files to add, 1 per line. * @param flag_em If TRUE, then flag the added files for restoration. * @return 0 for success, nonzero for failure. */ int add_list_of_files_to_filelist(struct s_node *filelist, char*list_of_files_fname, bool flag_em) { FILE*fin; char*tmp; struct s_node *nod; malloc_string(tmp); log_msg(3, "Adding %s to filelist", list_of_files_fname); if (!(fin=fopen(list_of_files_fname,"r"))) { iamhere(list_of_files_fname); return(1); } for (fgets (tmp, MAX_STR_LEN, fin); !feof(fin); fgets (tmp, MAX_STR_LEN, fin)) { if (!tmp[0]) { continue; } if ((tmp[strlen(tmp)-1]==13 || tmp[strlen(tmp)-1]==10) && strlen(tmp)>0) { tmp[strlen(tmp)-1] = '\0'; } log_msg(2, "tmp = '%s'", tmp); if (!tmp[0]) { continue; } if ((nod = find_string_at_node (filelist, tmp))) { log_msg(5, "Found '%s' in filelist already. Cool.", tmp); } else { add_string_at_node (filelist, tmp); nod = find_string_at_node (filelist, tmp); } if (nod && flag_em) { toggle_path_selection(filelist, tmp, TRUE); log_msg (5, "Flagged '%s'", tmp); } } paranoid_fclose(fin); paranoid_free(tmp); return(0); } /* @} - end of filelistGroup */