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