/* * Signfs version 0.6, Copyright Benoit DOLEZ, 2002 */ /* command line is: * ./flx check [options] input1 input2 * ./flx check [options] [inputs1] , [inputs2] * options are : * -h,--help : this help * --ignore-mode : ignore file mode * --ignore-owner : ignore owner (user, group) * --ignore-dev : ignore device type * --ignore-size : ignore file size * --ignore-sum : ignore checksum (md5) * --ignore-date : ignore date * --ignore-link : ignore link * --ignore-ldate : ignore links date * --show-new : show only new files * --show-old : show only removed files * --show-all : show all files (sames and differs) */ #include #include #include "flx.h" #include "check.h" #include "input.h" #include "output.h" #include "output_file.h" #include "input_file.h" #include "input_fs.h" static POOL_INIT(t_file_diff); #define O_HELP 1 #define O_SH_HR 5 #define O_SH_ALL 6 #define O_SH_NEW 7 #define O_SH_OLD 8 #define O_IGN_MODE 9 #define O_IGN_OWNER 10 #define O_IGN_SUM 11 #define O_IGN_SIZE 12 #define O_IGN_DATE 13 #define O_IGN_LINK 14 #define O_IGN_LDATE 15 #define O_REWRITE_SRC1 16 #define O_REWRITE_SRC2 17 #define O_OUTPUT 18 #define O_IGN_DOT 19 t_param flxcheck_poptions[] = { { 'h', "help", O_HELP, 0, "-h,--help show this help"}, { 0, "ignore-mode", O_IGN_MODE, 0, "--ignore-mode ignore mode" }, { 0, "ignore-owner", O_IGN_OWNER, 0, "--ignore-owner ignore uid/gid" }, { 0, "ignore-sum", O_IGN_SUM, 0, "--ignore-sum ignore checksum" }, { 0, "ignore-size", O_IGN_SIZE, 0, "--ignore-size ignore size" }, { 'd', "ignore-date", O_IGN_DATE, 0, "--ignore-date ignore date" }, { 0, "ignore-link", O_IGN_LINK, 0, "-d,--ignore-link ignore link" }, { 0, "ignore-ldate", O_IGN_LDATE, 0, "--ignore-ldate ignore date on links" }, { 0, "ignore-dot", O_IGN_DOT, 0, "--ignore-dot do not compare '.' and '..'" }, { 0, "show-all", O_SH_ALL, 0, "--show-all show all files (same and differs)" }, { 0, "only-new", O_SH_NEW, 0, "--only-new show only new files"}, { 0, "only-old", O_SH_OLD, 0, "--only-old show only old files"}, { 0, "human-readable", O_SH_HR, 0, "--human-readable show long format"}, { 0, "rw1", O_REWRITE_SRC1, 1, "--rw1 string before first source path"}, { 0, "rw2", O_REWRITE_SRC2, 1, "--rw2 string before second source path"}, { 0, "op", O_OUTPUT, 1, "--op all output prefix"}, { 0, NULL, 0 } } ; static int output_diff_fcntl(t_file_status *env, int cmd); static int output_diff_write(t_file_status *spec, t_ft *tree, int number); static t_source_type InputTypes[] = { { "fs", (void*)input_fs_open, (void*)input_fs_read, NULL, (void*)input_fs_close, (void*)input_fs_fcntl }, { "file", (void*)input_file_open, (void*)input_file_read, NULL, (void*)input_file_close, (void*)input_file_fcntl }, { "stdin", (void*)input_stdin_open, (void*)input_file_read, NULL, (void*)input_file_close, (void*)input_file_fcntl }, { NULL } }; static t_source_type OutputTypes[] = { { "stdout", (void*)output_stdout_open, NULL, (void*)output_diff_write, (void*)output_file_close, (void*)output_diff_fcntl}, { NULL } }; static void *InputSrc1, *InputSrc2, *Output; static char *Rewrite_Src1 = NULL, *Rewrite_Src2 = NULL, *Output_Str = NULL; /* cette fonction permet de repérer toutes les données communes à deux sources et de * les inserer dans un autre arbre * cette fonction retourne le nombre d'éléments sortis des arbres sources */ int ft_find_diff(t_ft *src1, t_ft *src2, t_ft *dst) { int count = 0, ret, diff; t_file_diff *desc; t_ft *next1, *next2; /* on démarre du fils de chaque arbre en créant au besoin les éléments manquant dans l'arbre destination */ if (!src1 || !src2 || !src1->subtree || !src2->subtree) return (0); if (dst && !dst->subtree) { dst->subtree = NEWDIR(dst); } src1 = src1->subtree->next; src2 = src2->subtree->next; dst = dst->subtree->next; while (!IS(src1->status, BASE) && !IS(src2->status, BASE)) { /* look for next same data */ while ((ret = strcmp(src1->filename, src2->filename))) { if (ret < 0) src1 = src1->next; if (ret > 0) src2 = src2->next; /* no element found return */ if (IS(src1->status, BASE) || IS(src2->status, BASE)) return (count); } /* backup next pointers */ next1 = src1->next; next2 = src2->next; /* place dst to best previous position */ while (!IS(dst->status, BASE) && strcmp(src1->filename, dst->filename) <= 0) dst = dst->next; dst = dst->prev; /* only if they are filled */ if (IS(src1->status, FILLED) && IS(src2->status, FILLED)) { /* pass '.' and '..' when IGNORE_DOT option */ //REM if ((!IS(Options, GOPT_IGNORE_DOT) || !IS_DOTF(src1->filename)) && if ((diff = files_are_the_same(src1->desc, src2->desc, Diff, NULL)) || IS(Options, SHOW_ALL)) { /* should be place before current dst, but at the good place */ dst = ft_add(dst, src1->filename, NULL, NULL); /* remove usage of c1->filename, in src1 if used by dst */ if (src1->filename == dst->filename) dst->filename = strdup(src1->filename); /* else already exist */ if (dst->desc) { /* a test can be made with dst->desc ..., probably an * error in source data */ warning("duplicate entry in sources"); } else { /* create difference description structure */ dst->desc = desc = POOL_ALLOC(t_file_diff); desc->diff = diff; desc->src1 = src1->desc; src1->desc = NULL; desc->src2 = src2->desc; src2->desc = NULL; /* mark changed */ SET(dst->status, FILLED); SET_PARENT(dst, CHANGED); /* update counter */ count++; } } /* mark already seen */ UNSET(src1->status, FILLED); UNSET(src2->status, FILLED); // printf("%s treated\n", src2->filename); // if (!src1->subtree) ft_del(src1, NULL, NULL); // if (!src2->subtree) ft_del(src2, NULL, NULL); } /* if directory, run into */ if (src1->subtree && src2->subtree && (IS(src1->status, CHANGED) || IS(src2->status, CHANGED)) && !IS_PDOTF(src1) && !IS_PDOTF(src2)) { /* get directory in dst or create it */ dst = ft_add(dst, src1->filename, NULL, NULL); if (src1->filename == dst->filename) dst->filename = strdup(dst->filename); count += ft_find_diff(src1, src2, dst); } /* go to next */ src1 = next1; src2 = next2; } return (count); } /* cette fonction permet de déplacer le contenu d'un premier arbre dans un * deuxième arbre */ void ft_move(t_ft *src, t_ft *dst) { t_ft *next; int ret; if (!src || !dst || !src->subtree) return; /* move full subtree */ if (!dst->subtree && src->subtree) { t_ft *cur; /* change parent */ src->subtree->parent = dst; for (cur = src->subtree->next; !IS(cur->status, BASE); cur = cur->next) { if (IS_PDOTDOT(cur)) cur->subtree = dst->parent->subtree; cur->parent = dst; } /* change from src to dst */ dst->subtree = src->subtree; src->subtree = NULL; return ; } src = src->subtree->next; dst = dst->subtree->next; while (!IS(src->status, BASE)) { /* backup next position */ next = src->next; ret = 1; /* search for it in dst */ while (!IS(dst->status, BASE) && (ret = strcmp(dst->filename, src->filename)) < 0) dst = dst->next; if (ret == 0) { /* element exist */ /* need to move description */ if (src->desc && IS(src->status, FILLED) && !dst->desc) { dst->desc = src->desc; src->desc = NULL; } /* need to run into */ if (src->subtree) { if (IS_PDOT(src)) { dst->subtree = dst->parent->subtree; src->subtree = NULL; } else if (IS_PDOTDOT(src)) { dst->subtree = dst->parent->parent->subtree; src->subtree = NULL; } else { ft_move(src, dst); } } ft_del(src, NULL, NULL); } else { dst = dst->prev; /* go back to the previous one */ /* unchain from source tree */ LIST_UNCHAIN(src); /* chain to destination tree */ LIST_CHAIN(dst, src, dst->next); /* change parent hierarchy */ if (IS_PDOT(src)) src->subtree = dst->parent->subtree; else if (IS_PDOTDOT(src)) src->subtree = dst->parent->parent->subtree; else if (src->subtree) { t_ft *cur; for (cur = src->subtree->next; !IS(cur->status, BASE); cur = cur->next) { if (IS_PDOTDOT(cur)) cur->subtree = dst->parent->subtree; } } src->parent = dst->parent; /* change destination current location */ dst = src; } src = next; } } /* cette fonction permet de déplacer le contenu de deux arbres dans un troisième */ void ft_diff_move(t_ft *src, t_ft *dst, int opt) { t_ft *next; t_file_diff *new = NULL; if (!src || !dst || !src->subtree) return; if (!dst->subtree) dst->subtree = NEWDIR(dst); src = src->subtree->next; dst = dst->subtree->next; while (!IS(src->status, BASE)) { /* backup next position */ next = src->next; if (IS(src->status, FILLED) || src->subtree) { /* should find or create a new element */ dst = ft_add(dst, src->filename, NULL, NULL); if (src->filename == dst->filename) src->filename = NULL; } if (IS(src->status, FILLED) && src->desc) { /* need to move description */ if (!(new = dst->desc)) { dst->desc = new = MALLOC(sizeof(*new)); bzero(new, sizeof(*new)); new->diff = 0xFFFF; } if (opt == ONLY_SRC1) new->src1 = src->desc; if (opt == ONLY_SRC2) new->src2 = src->desc; src->desc = NULL; SET(dst->status, FILLED); SET_PARENT(dst, CHANGED); UNSET(src->status, FILLED); } /* need to run into */ if (src->subtree) { if (IS_PDOT(src)) dst->subtree = dst->parent->subtree; else if (IS_PDOTDOT(src)) dst->subtree = dst->parent->parent->subtree; else ft_diff_move(src, dst, opt); } ft_del(src, NULL, NULL); src = next; } } /* cette fonction permet d'initialiser la structure donnée en paramêtre */ void ft_init(t_ft *d) { bzero(d, sizeof(*d)); d->prev = d->next = d; d->parent = d; SET(d->status, BASE); } t_file_diff *fct_free_diff_desc(void *data, t_file_diff *desc) { if (desc->src1) fct_free_file_desc(data, desc->src1); if (desc->src2) fct_free_file_desc(data, desc->src2); free(desc); return (NULL); } char *build_diff_line(char *path, char *filename, t_file_diff *info) { static char tmp[BUFFER_LENGTH]; static char opath[BUFFER_LENGTH]; char *s = tmp; char *ppath; *s = 0; if (info->diff == 0) { ppath = opath; if (Output_Str) ppath += sprintf(ppath, "%s/", Output_Str); if (Rewrite_Src1) ppath += sprintf(ppath, "%s/", Rewrite_Src1); sprintf(ppath, "%s", path); s += sprintf(s, "= %s", build_line(opath, filename, info->src1)); return (tmp); } if (info->src1 && IS(Options, SHOW_OLD)) { ppath = opath; if (Output_Str) ppath += sprintf(ppath, "%s/", Output_Str); if (Rewrite_Src1) ppath += sprintf(ppath, "%s/", Rewrite_Src1); sprintf(ppath, "%s", path); if (!info->src2) s += sprintf(s, "- %s", build_line(opath, filename, info->src1)); else s += sprintf(s, IS(Options, SHOW_NEW)?"< %s\n":"< %s", build_line(opath, filename, info->src1)); } if (info->src2 && IS(Options, SHOW_NEW)) { ppath = opath; if (Output_Str) ppath += sprintf(ppath, "%s/", Output_Str); if (Rewrite_Src2) ppath += sprintf(ppath, "%s/", Rewrite_Src2); sprintf(ppath, "%s", path); if (!info->src1) s += sprintf(s, "+ %s", build_line(opath, filename, info->src2)); else s += sprintf(s, "> %s", build_line(opath, filename, info->src2)); } if (*tmp) return (tmp); return (NULL); } int output_diff_write(t_file_status *desc, t_ft *tree, int number) { return (output_file_write_(desc, tree, number, build_diff_line)); } /* traduct a string options descriptions to flags */ int output_diff_fcntl(t_file_status *env, int cmd) { return (0); } /* cette fonction se base sur les différents paramêtres pour * déterminer les différences entre les signatures de deux types de sources. * Aux types de sources sont associées des fonctions spécialisées. Cette * définition est valable de la même manière pour la destination */ int flxcheck_main(int argc, char **argv) { /* tree for each source */ t_ft src1 = { &src1, &src1, &src1, NULL, NULL, NULL, BASE }; t_ft src2 = { &src2, &src2, &src2, NULL, NULL, NULL, BASE }; /* tree for destination */ t_ft dst = { &dst, &dst, &dst, NULL, NULL, NULL, BASE|CHANGED }; t_ft tmp1, tmp2; /* les bases temporaires */ int nb1 = 0, nb2 = 0; /* il faut initialiser les structures de manière à définir les * fonctions associées aux sources */ /* on initialise un premier type de source à partir d'un premier pointeur * sur arbre et vice-versa */ if (!InputSrc1 || !InputSrc2) { arg_usage(flxcheck_poptions, "{source1 source2 | source1 [...] , source2 [...]}"); } if (!Output) Output = output_alloc(); output_add(Output, "stdout:", OutputTypes); /* il faut initialiser les bases temporaires à partir des bases originales */ ft_init(&tmp1); ft_init(&tmp2); while (!input_eof(InputSrc1) || !input_eof(InputSrc2)) { /* la comparaison s'effectue sur les deux sources simultanément */ /* on lit des données dans chacune des entrées vers un tampon */ nb1 += input_read(InputSrc1, &src1); nb2 += input_read(InputSrc2, &src2); /* on compare les données deux à deux afin de compléter 'dst' */ ft_find_diff(&src1, &src2, &dst); ft_find_diff(&src1, &tmp2, &dst); ft_find_diff(&tmp1, &src2, &dst); /* toutes les données identiques ont été détectée pour la partie * déterminée, on déplace le contenu de la source vers les tampons */ ft_move(&src1, &tmp1); ft_move(&src2, &tmp2); /* visualiser les différences si possible */ // ft_diff_move(&tmp1, &dst, ONLY_SRC1); // ft_diff_move(&tmp2, &dst, ONLY_SRC2); // output_write(output, &dst); // ft_free(&dst, (void*)fct_free_diff_desc, NULL); /* les sources sont vidées, il est possible de recommencer l'opération */ } /* il n'y a plus de différences détectables * le reste sont des différences uniques */ ft_diff_move(&tmp1, &dst, ONLY_SRC1); ft_diff_move(&tmp2, &dst, ONLY_SRC2); /* visualiser les différences */ output_write(Output, &dst, 0); ft_free(&dst, (void*)fct_free_diff_desc, NULL); /* close all interface */ input_free(InputSrc1); input_free(InputSrc2); output_free(Output); return (0); } int flxcheck_pfct(int opt, t_param *param, char **flag, char **argv) { static int status = 1; /* source 1 or source 2 */ if (opt == O_HELP) arg_usage(flxcheck_poptions, NULL); else if (opt == O_SH_ALL) SET(Options, SHOW_SAME|SHOW_OLD|SHOW_NEW); else if (opt == O_SH_OLD) UNSET(Options, SHOW_SAME|SHOW_NEW); else if (opt == O_SH_NEW) UNSET(Options, SHOW_SAME|SHOW_OLD); else if (opt == O_IGN_MODE) UNSET(Diff, DIFF_MODE); else if (opt == O_IGN_OWNER) UNSET(Diff, DIFF_OWNER); else if (opt == O_IGN_SUM) UNSET(Diff, DIFF_CHECKSUM); else if (opt == O_IGN_SIZE) UNSET(Diff, DIFF_SIZE); else if (opt == O_IGN_DATE) UNSET(Diff, DIFF_TIME); else if (opt == O_IGN_LINK) UNSET(Diff, DIFF_LINK); else if (opt == O_IGN_LDATE) UNSET(Diff, DIFF_LDATE); else if (opt == O_SH_HR) SET(Options, GOPT_HUMAN_READABLE); else if (opt == O_REWRITE_SRC1) Rewrite_Src1 = strdup(*(argv+1)); else if (opt == O_REWRITE_SRC2) Rewrite_Src2 = strdup(*(argv+1)); else if (opt == O_OUTPUT) Output_Str = strdup(*(argv+1)); else if (opt == O_IGN_DOT) SET(Options, GOPT_IGNORE_DOT); else if (opt == -1) { if (!strcmp(*argv, ",")) status = 3; else if (status == 1 || status == 2) { /* add new source to source 1 */ if (!InputSrc1) InputSrc1 = input_alloc(); input_add(InputSrc1, *argv, InputTypes); if (status == 1) { /* search alone ',' in others parameters */ char **ptr = argv; while (*ptr && strcmp(*ptr, ",")) ptr++; if (*ptr) status = 2; else status = 3; } } else { /* status == 3 */ /* add new source to source 2 */ if (!InputSrc2) InputSrc2 = input_alloc(); input_add(InputSrc2, *argv, InputTypes); } } else return (-1); return (param ? param->minarg : 0); }