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