00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 #include <fcntl.h>
00010 #include <search.h>
00011 
00012 #include "global.h"
00013 #include "cp_mv.h"
00014 #include "status.h"
00015 #include "est_ops.h"
00016 #include "url.h"
00017 #include "checksum.h"
00018 #include "options.h"
00019 #include "props.h"
00020 #include "cache.h"
00021 #include "helper.h"
00022 #include "waa.h"
00023 
00024 
00229 
00230 
00231 
00232 
00233 
00234 
00235 
00236 
00237 
00238 
00239 
00240 
00241 
00242 
00243 
00244 
00245 
00246 
00247 
00248 
00249 
00250 
00251 
00252 
00253 
00254 
00255 
00256 
00257 
00258 
00259 
00260 
00261 
00262 
00263 
00264 
00265 
00266 
00267 
00268 
00269 
00270 
00271 
00272 
00273 
00274 
00275 
00276 
00277 
00278 
00279 
00280 
00281 
00282 
00283 
00284 
00285 
00286 
00287 
00288 
00289 
00290 
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298 
00299 
00302 #define MAX_DUPL_ENTRIES (HASH__LIST_MAX -1)
00303 
00304 #if 0
00305 
00307 #define MIN_FILE_SIZE (32)
00308 #endif
00309 
00310 
00312 int copydetect_count;
00313 
00314 
00316 struct cm___candidate_t {
00318     struct estat *sts;
00320     int matches_where;
00322     int match_count;
00323 };
00324 
00325 
00328 typedef datum (cm___to_datum_t)(const struct estat *sts);
00329 cm___to_datum_t cm___md5_datum;
00330 cm___to_datum_t cm___inode_datum;
00331 cm___to_datum_t cm___name_datum;
00338 struct cm___match_t;
00339 
00341 typedef int (cm___register_fn)(struct estat *, struct cm___match_t *);
00346 typedef int (cm___get_list_fn)(struct estat *, struct cm___match_t *,
00347         struct cm___candidate_t **, int *count);
00351 typedef char* (cm___format_fn)(struct cm___match_t * match,
00352         struct cm___candidate_t *cand);
00353 
00354 
00356 cm___register_fn cm___hash_register;
00358 cm___get_list_fn cm___hash_list;
00360 cm___get_list_fn cm___match_children;
00362 cm___format_fn cm___output_pct;
00363 
00364 
00366 struct cm___match_t {
00368     char name[8];
00370     mode_t entry_type;
00372     int is_expensive:1;
00374     int is_enabled:1;
00375 
00377     cm___register_fn *insert;
00379     cm___get_list_fn *get_list;
00381     cm___format_fn *format;
00382 
00385     cm___to_datum_t *to_key;
00387     char filename[8];
00389     hash_t db;
00392     datum key;
00393 };
00394 
00396 enum cm___match_e {
00397     CM___NAME_F=0,
00398     CM___NAME_D,
00399     CM___DIRLIST,
00400 };
00401 
00409 struct cm___match_t cm___match_array[]=
00410 {
00411     [CM___NAME_F] = { .name="name", .to_key=cm___name_datum, 
00412         .insert=cm___hash_register, .get_list=cm___hash_list,
00413         .entry_type=S_IFREG,  .filename=WAA__FILE_NAME_EXT},
00414     [CM___NAME_D] = { .name="name", .to_key=cm___name_datum, 
00415         .insert=cm___hash_register, .get_list=cm___hash_list,
00416         .entry_type=S_IFDIR,  .filename=WAA__DIR_NAME_EXT},
00417 
00418     [CM___DIRLIST] = { .name="dirlist", 
00419         .get_list=cm___match_children, .format=cm___output_pct,
00420         .entry_type=S_IFDIR, },
00421 
00422     { .name="md5", .to_key=cm___md5_datum, .is_expensive=1,
00423         .insert=cm___hash_register, .get_list=cm___hash_list,
00424         .entry_type=S_IFREG, .filename=WAA__FILE_MD5s_EXT},
00425 
00426     { .name="inode", .to_key=cm___inode_datum, 
00427         .insert=cm___hash_register, .get_list=cm___hash_list,
00428         .entry_type=S_IFDIR, .filename=WAA__FILE_INODE_EXT},
00429     { .name="inode", .to_key=cm___inode_datum, 
00430         .insert=cm___hash_register, .get_list=cm___hash_list,
00431         .entry_type=S_IFREG, .filename=WAA__DIR_INODE_EXT},
00432 };
00433 #define CM___MATCH_NUM (sizeof(cm___match_array)/sizeof(cm___match_array[0]))
00434 
00435 
00437 datum cm___md5_datum(const struct estat *sts)
00438 {
00439     datum d;
00440 
00441     d.dsize=APR_MD5_DIGESTSIZE*2+1;
00442     d.dptr=cs__md5tohex_buffered(sts->md5);
00443     return d;
00444 }
00445 
00446 
00449 datum cm___name_datum(const struct estat *sts)
00450 {
00451     datum d;
00452 
00453     d.dptr=sts->name;
00454     
00455     d.dsize=strlen(d.dptr)+1;
00456     return d;
00457 }
00458 
00459 
00461 datum cm___inode_datum(const struct estat *sts)
00462 {
00463     static struct { ino_t ino; dev_t dev; } tmp;
00464     datum d;
00465 
00466     tmp.ino=sts->st.ino;
00467     tmp.dev=sts->st.dev;
00468     d.dptr=(char*)&tmp;
00469     d.dsize=sizeof(tmp);
00470     return d;
00471 }
00472 
00473 
00475 static int cm___cand_compare(const void *_a, const void *_b)
00476 {
00477     const struct cm___candidate_t *a=_a;
00478     const struct cm___candidate_t *b=_b;
00479     return a->sts - b->sts;
00480 }
00481 
00482 
00484 static int cm___cand_comp_count(const void *_a, const void *_b)
00485 {
00486     const struct cm___candidate_t *a=_a;
00487     const struct cm___candidate_t *b=_b;
00488     return a->match_count - b->match_count;
00489 }
00490 
00491 
00493 int cm___hash_register(struct estat *sts, struct cm___match_t *match)
00494 {
00495     int status;
00496 
00497 
00498     status=hsh__insert_pointer( match->db, 
00499             (match->to_key)(sts), sts);
00500 
00501     
00502     if (status == EFBIG)
00503         status=0;
00504     return status;
00505 }
00506 
00507 
00513 int cm___match_children(struct estat *sts, struct cm___match_t *match,
00514         struct cm___candidate_t **list, int *found)
00515 {
00516     int status;
00517     
00518 
00519     static struct cm___candidate_t similar_dirs[MAX_DUPL_ENTRIES*4];
00520     struct cm___candidate_t *cur, tmp_cand={0};
00521     size_t simil_dir_count;
00522     int common;
00523     struct estat **children, *curr;
00524     struct estat **others, *other_dir;
00525     int other_count, i;
00526     datum key;
00527     struct cm___match_t *name_match;
00528 
00529 
00530     status=0;
00531     DEBUGP("child matching for %s", sts->name);
00532 
00533     
00534     if (!sts->entry_count) goto ex;
00535 
00536     simil_dir_count=0;
00537 
00538     children=sts->by_inode;
00539     while (*children)
00540     {
00541         curr=*children;
00542         
00543 
00544         if (S_ISDIR(curr->st.mode))
00545             name_match=cm___match_array+CM___NAME_D;
00546         else if (S_ISREG(curr->st.mode))
00547             name_match=cm___match_array+CM___NAME_F;
00548         else goto next_child;
00549 
00550 
00551         key=(name_match->to_key)(curr);
00552         status=hsh__list_get(name_match->db, key, &key, &others, &other_count);
00553 
00554 
00555         
00556 
00557         if (status != ENOENT && other_count && 
00558                 other_count<MAX_DUPL_ENTRIES)
00559         {
00560             for(i=0; i<other_count; i++)
00561             {
00562             
00563 
00564 
00565 
00566 
00567                 tmp_cand.sts=others[i]->parent;
00568 
00569                 cur=lsearch(&tmp_cand, similar_dirs, &simil_dir_count, 
00570                         sizeof(similar_dirs[0]), cm___cand_compare);
00571                 cur->match_count++;
00572                 DEBUGP("dir %s has count %d", cur->sts->name, cur->match_count);
00573 
00574                 BUG_ON(simil_dir_count > sizeof(similar_dirs)/sizeof(similar_dirs[0]));
00575             }
00576         }
00577 
00578 next_child:
00579         children++;
00580     }
00581 
00582     
00583     for(i=0; i<simil_dir_count; i++)
00584     {
00585         common=0;
00586         other_dir=similar_dirs[i].sts;
00587 
00588         int both(struct estat *a, struct estat*b) { common++; return 0; }
00589         STOPIF( ops__correlate_dirs(sts, other_dir,
00590                     NULL, both, NULL, NULL), NULL);
00591 
00592         
00593 
00594 
00595         similar_dirs[i].match_count = 
00596             1000*common/(sts->entry_count + other_dir->entry_count - common);
00597     }
00598 
00599     
00600     qsort( similar_dirs, simil_dir_count, sizeof(similar_dirs[0]), 
00601             cm___cand_comp_count);
00602 
00603     *found=simil_dir_count > HASH__LIST_MAX ? HASH__LIST_MAX : simil_dir_count;
00604     *list=similar_dirs;
00605 
00606 ex:
00607     return status;
00608 }
00609 
00610 
00613 int cm___hash_list(struct estat *sts, struct cm___match_t *match,
00614         struct cm___candidate_t **output, int *found)
00615 {
00616     int status;
00617     static struct cm___candidate_t arr[MAX_DUPL_ENTRIES];
00618     struct estat **list;
00619     int i;
00620 
00621     match->key=(match->to_key)(sts);
00622     status=hsh__list_get(match->db, match->key, &match->key, &list, found);
00623 
00624     if (status == 0)
00625     {
00626         for(i=0; i<*found; i++)
00627         {
00630             memset(arr+i, 0, sizeof(*arr));
00631             arr[i].sts=list[i];
00632         }
00633         *output=arr;
00634     }
00635 
00636     return status;
00637 }
00638 
00639 
00641 char* cm___output_pct(struct cm___match_t *match, 
00642         struct cm___candidate_t *cand)
00643 {
00644     static char buffer[8];
00645 
00646     BUG_ON(cand->match_count > 1000 || cand->match_count < 0);
00647 
00648     sprintf(buffer, "=%d.%1d%%",
00649             cand->match_count/10, cand->match_count % 10);
00650 
00651     return buffer;
00652 }
00653 
00654 
00656 int cm___register_entry(struct estat *sts)
00657 {
00658     int status;
00659     int i;
00660     struct cm___match_t *match;
00661 
00662 
00663     status=0;
00664     if (!(sts->entry_status & FS_NEW))
00665     {
00666         for(i=0; i<CM___MATCH_NUM; i++)
00667         {
00668             match=cm___match_array+i;
00669             
00670 
00671             if (match->is_enabled && match->insert &&
00672                     (sts->st.mode & S_IFMT) == match->entry_type )
00673             {
00674                 STOPIF( (match->insert)(sts, match), NULL);
00675                 DEBUGP("inserted %s for %s", sts->name, match->name);
00676             }
00677         }
00678     }
00679 
00680 ex:
00681     return status;
00682 }
00683 
00684 
00687 static int cm___match(struct estat *entry)
00688 {   
00689     int status;
00690     char *path, *formatted;
00691     int i, count, have_match, j, overflows;
00692     struct estat *sts;
00693     struct cm___match_t *match;
00694     struct cm___candidate_t candidates[HASH__LIST_MAX*CM___MATCH_NUM];
00695     struct cm___candidate_t *cur, *list;
00696     size_t candidate_count;
00697     FILE *output=stdout;
00698 
00699 
00700     
00701 
00702     BUG_ON(sizeof(candidates[0].matches_where) *4 < CM___MATCH_NUM,
00703             "Wrong datatype chosen for matches_where.");
00704 
00705 
00706     formatted=NULL;
00707     status=0;
00708     candidate_count=0;
00709     overflows=0;
00710     path=NULL;
00711 
00712     
00713 
00714 
00715     for(i=0; i<CM___MATCH_NUM; i++)
00716     {
00717         match=cm___match_array+i;
00718 
00719         
00720         if ((entry->st.mode & S_IFMT) != match->entry_type)
00721             continue;
00722 
00723         
00724         status=match->get_list(entry, match, &list, &count);
00725 
00726         
00727         if (status == ENOENT) continue;
00728 
00729         STOPIF(status, NULL);
00730 
00731         if (count > MAX_DUPL_ENTRIES)
00732         {
00733             
00734 
00735             overflows++;
00736             count=MAX_DUPL_ENTRIES;
00737         }
00738 
00739         for(j=0; j<count; j++)
00740         {
00741             
00742 
00743 
00744 
00745 
00746             cur=lsearch(list+j, candidates, &candidate_count, 
00747                     sizeof(candidates[0]), cm___cand_compare);
00748 
00749             BUG_ON(candidate_count > sizeof(candidates)/sizeof(candidates[0]));
00750 
00751             cur->matches_where |= 1 << i;
00752 
00753             
00754             if (i == CM___DIRLIST)
00755                 cur->match_count=list[j].match_count;
00756 
00757             DEBUGP("got %s for %s => 0x%X",
00758                     cur->sts->name, match->name, cur->matches_where);
00759         }
00760     }
00761 
00762     status=0;
00763 
00764     if (candidate_count)
00765     {
00766         copydetect_count++;
00767 
00768         STOPIF( ops__build_path(&path, entry), NULL);
00769         STOPIF( hlp__format_path(entry, path, &formatted), NULL);
00770 
00771         
00772         STOPIF_CODE_EPIPE( fprintf(output, "%s\n", formatted), NULL);
00773 
00774         
00775         for(j=0; j<candidate_count; j++)
00776         {
00777             sts=candidates[j].sts;
00778             have_match=0;
00779 
00780             STOPIF_CODE_EPIPE( fputs("  ", output), NULL);
00781 
00782             for(i=0; i<CM___MATCH_NUM; i++)
00783             {
00784                 if (candidates[j].matches_where & (1 << i))
00785                 {
00786                     match=cm___match_array+i;
00787 
00788                     if (have_match)
00789                         STOPIF_CODE_EPIPE( fputs(",", output), NULL);
00790                     have_match=1;
00791 
00792                     STOPIF_CODE_EPIPE( fputs(match->name, output), NULL);
00793                     if (opt__is_verbose()>0 && match->format)
00794                         STOPIF_CODE_EPIPE( 
00795                                 fputs( match->format(match, candidates+j), output), 
00796                                 NULL);
00797                 }
00798             }
00799 
00800             STOPIF( ops__build_path(&path, sts), NULL);
00801             STOPIF( hlp__format_path(sts, path, &formatted), NULL);
00802             STOPIF_CODE_EPIPE( fprintf(output, ":%s\n", formatted), NULL);
00803         }
00804 
00805 
00806         if (overflows)
00807             STOPIF_CODE_EPIPE( fputs("  ...\n", output), NULL);
00808     }
00809     else
00810     {
00811         
00812         STOPIF( ops__build_path(&path, entry), NULL);
00813 
00814         if (opt__is_verbose() > 0)
00815         {
00816             STOPIF( hlp__format_path(entry, path, &formatted), NULL);
00817             STOPIF_CODE_EPIPE( fprintf(output, 
00818                         "- No copyfrom relation found for %s\n", 
00819                         formatted), NULL);
00820         }
00821         else
00822             DEBUGP("No sources found for %s", path);
00823     }
00824 
00825     STOPIF_CODE_EPIPE( fflush(output), NULL);
00826 
00827 ex:
00828     return status;
00829 }
00830 
00831 
00832 int cm__find_dir_source(struct estat *dir)
00833 {
00834     int status;
00835     status=0;
00836 
00837     STOPIF( cm___match( dir ), NULL);
00838 
00839 ex:
00840     return status;
00841 }
00842 
00843 
00844 int cm__find_file_source(struct estat *file)
00845 {
00846     int status;
00847     char *path;
00848 
00849 
00850     status=0;
00851     STOPIF( ops__build_path(&path, file), NULL);
00852     DEBUGP("finding source of %s", file->name);
00853 
00854     STOPIF( cs__compare_file(file, path, NULL), NULL);
00855     
00856 
00857     STOPIF( cm___match( file ), NULL);
00858 
00859 ex:
00860     return status;
00861 }
00862 
00863 
00864 
00867 int cm__find_copied(struct estat *root)
00868 {
00869     int status;
00870     struct estat *sts, **child;
00871 
00872 
00873     status=0;
00874     child=root->by_inode;
00875     if (!child) goto ex;
00876 
00877     while (*child)
00878     {
00879         sts=*child;
00880         
00881 
00882 
00883 
00884 
00885 
00886         if (sts->entry_status & FS_NEW)
00887         {
00888             switch (sts->st.mode & S_IFMT)
00889             {
00890                 case S_IFDIR:
00891                     STOPIF( cm__find_dir_source(sts), NULL);
00892                     break;
00893                 case S_IFLNK:
00894                 case S_IFREG:
00895                     STOPIF( cm__find_file_source(sts), NULL);
00896                     break;
00897                 default:
00898                     DEBUGP("Don't handle entry %s", sts->name);
00899             }
00900         }
00901 
00902         if (S_ISDIR(sts->st.mode) && 
00903                 (sts->entry_status & (FS_CHILD_CHANGED | FS_CHANGED)) )
00904             STOPIF( cm__find_copied(sts), NULL);
00905 
00906         child++;
00907     }
00908 
00909 ex:
00910     return status;
00911 }
00912 
00913 
00916 int cm__detect(struct estat *root, int argc, char *argv[])
00917 {
00918     int status, st2;
00919     char **normalized;
00920     int i;
00921     struct cm___match_t *match;
00922     hash_t hash;
00923 
00924 
00925     
00926     opt_recursive++;
00927     
00928 
00929     opt__set_int(OPT__CHANGECHECK, PRIO_MUSTHAVE, CHCHECK_NONE);
00930 
00931     STOPIF( waa__find_common_base(argc, argv, &normalized), NULL);
00932 
00939     STOPIF( url__load_list(NULL, 0), NULL);
00940 
00941 
00942     for(i=0; i<CM___MATCH_NUM; i++)
00943     {
00944         match=cm___match_array+i;
00945 
00946         match->is_enabled= !match->is_expensive || 
00947             opt__get_int(OPT__COPYFROM_EXP);
00948 
00949         if (!match->filename[0]) continue;
00950 
00951         DEBUGP("open hash for %s as %s", match->name, match->filename);
00952 
00953         
00954         STOPIF( hsh__new(wc_path, match->filename, 
00955                     HASH_TEMPORARY, & match->db), NULL);
00956     }
00957 
00958 
00959     
00960     status=waa__read_or_build_tree(root, argc, normalized, argv, 
00961             cm___register_entry, 1);
00962     if (status == -ENOENT)
00963         STOPIF(status, "!No committed working copy found.");
00964     STOPIF(status, NULL);
00965 
00966 
00967     copydetect_count=0;
00968 
00969     STOPIF( cm__find_copied(root), NULL);
00970 
00971     if (!copydetect_count)
00972         STOPIF_CODE_EPIPE( printf("No copyfrom relations found.\n"), NULL);
00973     else if (opt__is_verbose() > 0)
00974         STOPIF_CODE_EPIPE( printf("%d copyfrom relation%s found.\n",
00975                     copydetect_count, copydetect_count == 1 ? "" : "s"), NULL);
00976 
00977 ex:
00978     for(i=0; i<CM___MATCH_NUM; i++)
00979     {
00980         hash=cm___match_array[i].db;
00981         cm___match_array[i].db=NULL;
00982         if (hash)
00983         {
00984             st2=hsh__close(hash, status);
00985             STOPIF_CODE_ERR( st2 && !status, st2, NULL);
00986         }
00987     }
00988 
00989     return status;
00990 }
00991 
00992 
00996 int cm___string_to_rev_path(char *string, char **out_url, svn_revnum_t *orev)
00997 {
00998     char *path;
00999     svn_revnum_t rev;
01000 
01001     
01002     if (string[0] != '0' || 
01003             string[1] != ' ') goto inval;
01004     string+=2;
01005 
01006     rev=strtol(string, &path, 10);
01007     if (orev) *orev=rev;
01008     if (string == path) goto inval;
01009 
01010     if (!isspace(*path)) goto inval;
01011 
01012     path=hlp__skip_ws(path);
01013     *out_url=path;
01014 
01015     DEBUGP("string parsed to r%llu of %s", (t_ull)rev, path);
01016     return 0;
01017 
01018 inval:
01019     DEBUGP("cannot parse %s", string);
01020     return EINVAL;
01021 }
01022 
01023 
01027 int cm___rev_path_to_string(char *url, svn_revnum_t revision, char **string)
01028 {
01029     int status;
01030     static struct cache_entry_t *c=NULL;
01031     int buflen, used;
01032     char *buffer;
01033 
01034 
01035     
01036 
01037 
01038 
01039     buflen=10 + 1 + strlen(url) + 1;
01040     STOPIF( cch__entry_set( &c, 0, NULL, buflen, 0, &buffer), NULL);
01041 
01042     used=snprintf(buffer, buflen, "0 %llu %s", (t_ull)revision, url);
01043     BUG_ON(used >= buflen);
01044 
01045     *string=buffer;
01046 
01047 ex:
01048     return status;
01049 }
01050 
01051 
01054 int cm___absolute_path(char *path, char **output)
01055 {
01056     static struct cache_t *cache;
01057     int status, len;
01058     char *cp;
01059 
01060     STOPIF( cch__new_cache(&cache, 8), NULL);
01061     STOPIF( cch__add(cache, 0, NULL, 
01062                 
01063         start_path_len + 1 + strlen(path) + 1, &cp), NULL);
01064     DEBUGP("norm from: %s", path);
01065     hlp__pathcopy(cp, &len, path, NULL);
01066     DEBUGP("norm to: %s", cp);
01067 
01068     BUG_ON(len > cache->entries[cache->lru]->len);
01069 
01070     *output=cp;
01071 
01072 ex:
01073     return status;
01074 }
01075 
01076 
01083 inline int cm___not_below_wcpath(char *path, char **out)
01084 {
01085     if (strncmp(path, wc_path, wc_path_len) != 0 ||
01086             path[wc_path_len] != PATH_SEPARATOR)
01087         return EINVAL;
01088 
01089     *out=path+wc_path_len+1;
01090     return 0;
01091 }
01092 
01093 
01094 
01095 
01100 int cm___dump_list(FILE *output, int argc, char *normalized[])
01101 {
01102     int status;
01103     hash_t db;
01104     datum key, value;
01105     int have;
01106     char *path;
01107     svn_revnum_t rev;
01108 
01109 
01110     
01111     db=NULL;
01112 
01113     
01114     status=hsh__new(wc_path, WAA__COPYFROM_EXT, GDBM_READER, &db);
01115     if (status==ENOENT)
01116     {
01117         status=0;
01118         goto no_copyfrom;
01119     }
01120 
01121     have=0;
01122     status=hsh__first(db, &key);
01123     while (status == 0)
01124     {
01125         STOPIF( hsh__fetch(db, key, &value), NULL);
01126 
01127         
01128 
01129         if (have)
01130             status=fputs(".\n", output);
01131 
01132         STOPIF( cm___string_to_rev_path( value.dptr, &path, &rev), NULL);
01133 
01134         status |= fprintf(output, "%s\n%s\n", path, key.dptr);
01135         IF_FREE(value.dptr);
01136 
01137         STOPIF_CODE_ERR( status < 0, -EPIPE, "output error");
01138 
01139         status=hsh__next(db, &key, &key);
01140         have++;
01141     }
01142 
01143     if (!have)
01144     {
01145 no_copyfrom:
01146         fprintf(output, "No copyfrom information was written.\n");
01147     }
01148     else
01149         if (opt__is_verbose() > 0)
01150             fprintf(output, "%d copyfrom relation%s.\n", 
01151                     have, have == 1 ? "" : "s");
01152 
01153 ex:
01154     if (db)
01155         STOPIF( hsh__close(db, status), NULL);
01156 
01157     return status;
01158 }
01159 
01160 
01176 int cm___make_copy(struct estat *root, 
01177         char *cp_src, svn_revnum_t revision,
01178         char *cp_dest, 
01179         int paths_are_wc_relative)
01180 {
01181     int status;
01182     static const char err[]="!The %s path \"%s\" is not below the wc base.";
01183     struct estat *src, *dest;
01184     static hash_t db=NULL;
01185     char *abs_src, *abs_dest;
01186     char *wc_src, *wc_dest;
01187     char *buffer, *url;
01188 
01189 
01190     if (!root)
01191     {
01192         STOPIF( hsh__close(db, 0), NULL);
01193         goto ex;
01194     }
01195 
01196     
01197 
01198 
01199 
01200 
01201     if (paths_are_wc_relative)
01202     {
01203         wc_dest=cp_dest;
01204         wc_src=cp_src;
01205     }
01206     else
01207     {
01208         STOPIF( cm___absolute_path(cp_dest, &abs_dest), NULL);
01209         STOPIF( cm___absolute_path(cp_src, &abs_src), NULL);
01210 
01211         STOPIF( cm___not_below_wcpath(abs_dest, &wc_dest),
01212                 err, "destination", abs_dest);
01213         STOPIF( cm___not_below_wcpath(abs_src, &wc_src),
01214                 err, "source", abs_src);
01215     }
01216 
01217 
01218     STOPIF( ops__traverse(root, cp_src, 0, 0, &src), NULL);
01219 
01220     
01221 
01222 
01223     STOPIF_CODE_ERR( src->flags & RF___IS_COPY, EINVAL,
01224             "!Copied entries must be committed before using them as copyfrom source.");
01225 
01226     
01227 
01228 
01229     STOPIF( ops__traverse(root, cp_dest, 
01230                 OPS__CREATE, RF_ADD, 
01231                 &dest), NULL);
01232 
01233     STOPIF_CODE_ERR( !(dest->flags & RF_ISNEW), EINVAL,
01234             "!The destination is already known - must be a new entry.");
01235 
01236     if (!db)
01237         STOPIF( hsh__new(wc_path, WAA__COPYFROM_EXT, GDBM_WRCREAT, &db), NULL);
01238 
01239     if (revision)
01240     {
01241         BUG_ON(1, "fetch list of entries from the repository");
01242     }
01243     else
01244     {
01245         STOPIF( waa__copy_entries(src, dest), NULL);
01246         revision=src->url->current_rev;
01247     }
01248 
01249     
01250 
01251     dest->flags |= RF_COPY_BASE;
01252     dest->flags &= ~RF_COPY_SUB;
01253 
01254 
01255     STOPIF( url__full_url( src, &url), NULL);
01256     STOPIF( cm___rev_path_to_string(url, revision, &buffer), NULL);
01257     STOPIF( hsh__store_charp(db, wc_dest, buffer), NULL);
01258 
01259 ex:
01260     return status;
01261 }
01262 
01263 
01271 int cm___ignore_impl_copied(struct estat *cur)
01272 {
01273     struct estat **sts;
01274     int all_ign;
01275 
01276 
01277     all_ign=1;
01278     cur->flags &= ~RF_COPY_SUB;
01279 
01280     if (cur->flags & (RF_ADD | RF_PUSHPROPS))
01281         all_ign=0;
01282 
01283     if (ops__has_children(cur))
01284     {
01285         sts=cur->by_inode;
01286         while (*sts)
01287         {
01288             all_ign &= cm___ignore_impl_copied(*sts);
01289             sts++;
01290         }
01291     }
01292 
01293     if (all_ign)
01294         cur->to_be_ignored=1;
01295     else
01296         
01297         cur->flags |= RF_ADD | RF_CHECK;
01298     DEBUGP("%s: all_ignore=%d", cur->name, all_ign);
01299 
01300     return all_ign;
01301 }
01302 
01303 
01306 int cm__uncopy(struct estat *root, int argc, char *argv[])
01307 {
01308     int status;
01309     char **normalized;
01310     struct estat *dest;
01311 
01312 
01313     
01314     opt_recursive=-1;
01315 
01316     if (!argc)
01317         ac__Usage_this();
01318 
01319     STOPIF( waa__find_common_base(argc, argv, &normalized), NULL);
01320 
01321     STOPIF( url__load_nonempty_list(NULL, 0), NULL);
01322 
01323 
01324     
01325     status=waa__input_tree(root, NULL, NULL);
01326     if (status == ENOENT)
01327         STOPIF( EINVAL, "!No working copy could be found.");
01328     else
01329         STOPIF( status, NULL);
01330 
01331     while (*normalized)
01332     {
01333         DEBUGP("uncopy %s %s", *normalized, normalized[1]);
01334 
01335         STOPIF( ops__traverse(root, *normalized, 
01336                     OPS__FAIL_NOT_LIST, 0, 
01337                     &dest), 
01338                 "!The entry \"%s\" is not known.", *normalized);
01339         STOPIF_CODE_ERR( !(dest->flags & RF_COPY_BASE), EINVAL,
01340                 "!The entry \"%s\" is not a copy base.", *normalized);
01341 
01342         
01343 
01344 
01345         STOPIF( cm__get_source(dest, NULL, NULL, NULL, 1), NULL);
01346 
01347         dest->flags &= ~RF_COPY_BASE;
01348 
01349         
01350         cm___ignore_impl_copied(dest);
01351 
01352         normalized++;
01353     }
01354 
01355     STOPIF( waa__output_tree(root), NULL);
01356     
01357     STOPIF( cm__get_source(NULL, NULL, NULL, NULL, 0), NULL);
01358 
01359 ex:
01360     return status;
01361 }
01362 
01363 
01366 int cm__work(struct estat *root, int argc, char *argv[])
01367 {
01368     int status;
01369     char **normalized;
01370     int count;
01371     FILE *input=stdin;
01372     char *src, *dest, *cp;
01373     int is_dump, is_load;
01374     svn_revnum_t revision;
01375 
01376 
01377     status=0;
01378     is_load=is_dump=0;
01379 
01380     
01381 
01382 
01383 
01384     
01385     if (argc==0) 
01386         is_dump=1;
01387     else if (strcmp(argv[0], parm_dump) == 0)
01388     {
01389         is_dump=1;
01390         argv++;
01391         argc--;
01392     }
01393     else if (strcmp(argv[0], parm_load) == 0)
01394     {
01395         is_load=1;
01396         argv++;
01397         argc--;
01398     }
01399 
01400     STOPIF( waa__find_common_base(argc, argv, &normalized), NULL);
01401 
01402     if (is_dump)
01403     {
01404         STOPIF( cm___dump_list(stdout, argc, normalized), NULL);
01405         
01406         goto ex;
01407     }
01408 
01409 
01410     switch (opt_target_revisions_given)
01411     {
01412         case 0:
01413             
01414             revision=0;
01415             break;
01416         case 1:
01417             revision=opt_target_revision;
01418         default:
01419             STOPIF( EINVAL, "!Only a single revision number may be given.");
01420     }
01421 
01422 
01423     STOPIF( url__load_nonempty_list(NULL, 0), NULL);
01424 
01425     
01426 
01427     status=waa__input_tree(root, NULL, NULL);
01428     if (status == -ENOENT)
01429         STOPIF(status, "!No entries are currently known, "
01430                 "so you can't define copy or move relations yet.\n");
01431     STOPIF(status, NULL);
01432 
01433     hlp__string_from_filep(NULL, NULL, NULL, SFF_RESET_LINENUM);
01434 
01435     if (is_load)
01436     {
01437         
01438         count=0;
01439 
01440         while (1)
01441         {
01442             status=hlp__string_from_filep(input, &cp, NULL, 0);
01443             if (status == EOF) 
01444             {
01445                 status=0;
01446                 break;
01447             }
01448             STOPIF( status, "Failed to read copyfrom source");
01449 
01450             STOPIF_CODE_ERR( !*cp, EINVAL, 
01451                     "!Copyfrom source must not be empty.");
01452             STOPIF( hlp__strdup( &src, cp), NULL);
01453 
01454 
01455             status=hlp__string_from_filep(input, &cp, NULL, 0);
01456             STOPIF_CODE_ERR( status == EOF, EINVAL,
01457                     "!Expected a target specification, got EOF!");
01458             STOPIF( status, "Failed to read copyfrom destination");
01459 
01460             STOPIF( hlp__strdup( &dest, cp), NULL);
01461 
01462 
01463             
01464             status=hlp__string_from_filep(input, &cp, NULL, SFF_WHITESPACE);
01465             if (status == EOF)
01466                 DEBUGP("delimiter line missing - EOF");
01467             else if (status == 0 && 
01468                     cp[0] == '.' && cp[1] == 0)
01469                 DEBUGP("delimiter line ok");
01470             else
01471             {
01472                 STOPIF(status, "Cannot read delimiter line");
01473                 
01474                 STOPIF(EINVAL, "Expected delimiter line - got %s", cp);
01475             }
01476 
01477             DEBUGP("read %s => %s", src, dest);
01478             
01479 
01480 
01481             STOPIF( cm___make_copy(root, src, revision, dest, 0), NULL);
01482             count++;
01483 
01484             free(dest);
01485             free(src);
01486         }
01487 
01488         if (opt__is_verbose() >= 0)
01489             printf("%d copyfrom relation%s loaded.\n", count, count==1 ? "" : "s");
01490     }
01491     else
01492     {
01493         STOPIF_CODE_ERR(argc != 2, EINVAL, 
01494                 "!At least source and destination, "
01495                 "or \"dump\" resp. \"load\" must be given.");
01496 
01497         
01498         STOPIF( cm___make_copy(root, 
01499                     normalized[0], revision, 
01500                     normalized[1], 1), 
01501                 "Storing \"%s\" as source of \"%s\" failed.",
01502                 normalized[0], normalized[1]);
01503     }
01504 
01505     STOPIF( cm___make_copy(NULL, NULL, 0, NULL, 0), NULL);
01506     STOPIF( waa__output_tree(root), NULL);
01507 
01508 ex:
01509     return status;
01510 }
01511 
01512 
01513 
01517 int cm___get_base_source(struct estat *sts, char *name,
01518         char **src_url, svn_revnum_t *src_rev,
01519         int alloc_extra,
01520         int register_for_cleanup)
01521 {
01522     int status;
01523     datum key, value;
01524     static hash_t hash;
01525     static int init=0;
01526     char *url;
01527 
01528 
01529     value.dptr=NULL;
01530     status=0;
01531     if (src_url) *src_url=NULL;
01532     if (src_rev) *src_rev=SVN_INVALID_REVNUM;
01533 
01534     if (!sts)
01535     {
01536         
01537         STOPIF( hsh__close(hash, register_for_cleanup), NULL);
01538         hash=NULL;
01539         init=0;
01540         goto ex;
01541     }
01542 
01543     if (!init)
01544     {
01545         
01546 
01547 
01548 
01549         init=1;
01550 
01551         
01552         status=hsh__new(wc_path, WAA__COPYFROM_EXT, 
01553                 GDBM_WRITER | HASH_REMEMBER_FILENAME, &hash);
01554         
01555 
01556         if (status != ENOENT)
01557             STOPIF( status, NULL);
01558     }
01559 
01560     
01561     if (!name)
01562         STOPIF( ops__build_path( &name, sts), NULL);
01563 
01564     if (name[0]=='.' && name[1]==PATH_SEPARATOR)
01565         name+=2;
01566 
01567     key.dptr=name;
01568     key.dsize=strlen(name)+1;
01569     status=hsh__fetch(hash, key, &value);
01570     if (status) 
01571     {
01572         DEBUGP("no source for %s found",
01573                 name);
01574         goto ex;
01575     }
01576 
01577     if (register_for_cleanup)
01578         STOPIF( hsh__register_delete(hash, key), NULL);
01579 
01580     
01581     STOPIF( cm___string_to_rev_path( value.dptr, 
01582                 &url, src_rev), NULL);
01583 
01584     if (src_url)
01585     {
01586         BUG_ON(!url);
01587 
01588         status=strlen(url);
01589         
01590 
01591 
01592 
01593         STOPIF( hlp__strnalloc( status + 1 +alloc_extra + 4, 
01594                     src_url, url), NULL);
01595         status=0;
01596     }
01597 
01598 ex:
01599     IF_FREE(value.dptr);
01600     return status;
01601 }
01602 
01603 
01610 int cm___get_sub_source_rek(struct estat *cur, int length_to_add,
01611         char **dest_buffer, svn_revnum_t *src_rev,
01612         char **eobuffer)
01613 {
01614     int status;
01615     struct estat *copied;
01616     int len;
01617 
01618 
01619     
01620 
01621 
01622     copied=cur->parent;
01623     BUG_ON(!copied, "Copy-sub but no base?");
01624     len=strlen(cur->name);
01625 
01626     length_to_add+=len+1;
01627 
01628     if (copied->flags & RF_COPY_BASE)
01629     {
01630         
01631         status=cm___get_base_source(copied, NULL, 
01632                 dest_buffer, src_rev, 
01633                 length_to_add, 0);
01634         if (status) goto ex;
01635 
01636         *eobuffer=*dest_buffer+strlen(*dest_buffer);
01637         DEBUGP("after base eob-5=%s", *eobuffer-5);
01638     }
01639     else
01640     {
01641         
01642 
01643         status=cm___get_sub_source_rek(copied, length_to_add,
01644                 dest_buffer, src_rev, eobuffer);
01645         if (status) goto ex;
01646     }
01647 
01648     
01649     
01650     **eobuffer = '/';
01651     strcpy( *eobuffer +1, cur->name );
01652     *eobuffer += len+1;
01653 
01654     DEBUGP("sub source of %s is %s", cur->name, *dest_buffer);
01655 
01656 ex:
01657     return status;
01658 }
01659 
01660 
01666 int cm___get_sub_source(struct estat *sts, char *name,
01667         char **src_url, svn_revnum_t *src_rev)
01668 {
01669     int status;
01670     char *eob;
01671 
01672 
01673     
01674 
01675 
01676 
01677 
01678 
01679 
01680 
01681 
01682 
01683 
01684 
01685 
01686     STOPIF( cm___get_sub_source_rek(sts, 0, src_url, src_rev, &eob), NULL);
01687 
01688 ex:
01689     return status;
01690 }
01691 
01692 
01710 int cm__get_source(struct estat *sts, char *name,
01711         char **src_url, svn_revnum_t *src_rev,
01712         int register_for_cleanup)
01713 {
01714     int status;
01715 
01716 
01717     if (!sts)
01718     {
01719         status=cm___get_base_source(NULL, NULL, NULL, NULL, 0, 0);
01720         goto ex;
01721     }
01722 
01723     if (sts->flags & RF_COPY_BASE)
01724     {
01725         status= cm___get_base_source(sts, name, 
01726                 src_url, src_rev, 0,
01727                 register_for_cleanup);
01728     }
01729     else if (sts->flags & RF_COPY_SUB)
01730     {
01731         status= cm___get_sub_source(sts, name, 
01732                 src_url, src_rev);
01733     }
01734     else
01735     {
01736         status=ENOENT;
01737         goto ex;
01738     }
01739 
01740     if (src_url)
01741         DEBUGP("source of %s is %s", sts->name, *src_url);
01742 
01743     if (status)
01744     {
01745         
01746 
01747 
01748         DEBUGP("bit set, no source!");
01749         
01750         goto ex;
01751     }
01752 
01753 ex:
01754     return status;
01755 }
01756