COOPY » Guide
version 0.6.5
|
00001 #include <stdio.h> 00002 #include <getopt.h> 00003 00004 #include <coopy/CsvFile.h> 00005 #include <coopy/CsvTextBook.h> 00006 #include <coopy/MergeOutputIndex.h> 00007 #include <coopy/MergeOutputPool.h> 00008 #include <coopy/MergeOutputFilter.h> 00009 #include <coopy/BookCompare.h> 00010 #include <coopy/PolyBook.h> 00011 #include <coopy/SheetPatcher.h> 00012 #include <coopy/PatchParser.h> 00013 #include <coopy/PoolImpl.h> 00014 #include <coopy/Options.h> 00015 #include <coopy/Highlighter.h> 00016 #include <coopy/ShortTextBook.h> 00017 #include <coopy/FilteredTextBook.h> 00018 #include <coopy/IndexSniffer.h> 00019 00020 #include <coopy/Diff.h> 00021 00022 using namespace std; 00023 using namespace coopy::app; 00024 using namespace coopy::store; 00025 using namespace coopy::cmp; 00026 00027 static Patcher *createTool(string mode, string version="") { 00028 return Patcher::createByName(mode.c_str(),version.c_str()); 00029 } 00030 00031 static bool copyFile(const char *src, const char *dest) { 00032 return Patcher::copyFile(src,dest); 00033 } 00034 00035 00036 // cloned should, in fact, be always true 00037 static bool copyBook(PolyBook& src_book, const char *src, const char *dest, 00038 bool *cloned) { 00039 PolyBook check; 00040 Property p1 = check.getType(src); 00041 Property p2 = check.getType(dest); 00042 if (p1.get("type").asString()==p2.get("type").asString()) { 00043 *cloned = true; 00044 return Patcher::copyFile(src,dest); 00045 } 00046 *cloned = false; 00047 return src_book.write(dest); 00048 } 00049 00050 00051 int Diff::apply(const Options& opt) { 00052 dbg_printf("{} Diff::apply begins\n"); 00053 bool resolved = true; 00054 bool verbose = opt.checkBool("verbose"); 00055 bool equality = opt.checkBool("equals"); 00056 bool resolving = opt.isResolveLike(); //opt.checkBool("resolving") 00057 std::string output = opt.checkString("output","-"); 00058 std::string parent_file = opt.checkString("parent"); 00059 std::string patch_file = opt.checkString("patch"); 00060 std::string meta_file = opt.checkString("meta"); 00061 std::string pool_file = opt.checkString("pool"); 00062 bool have_cmd = opt.isStringList("cmd"); 00063 std::vector<std::string> cmds; 00064 if (have_cmd) cmds = opt.getStringList("cmd"); 00065 std::string version = opt.checkString("version"); 00066 std::string tmp = opt.checkString("tmp",""); 00067 std::string resolve = opt.checkString("resolve",""); 00068 std::string defMode = "default"; 00069 bool apply = opt.checkBool("apply",opt.isResolveLike()?true:false); 00070 if (opt.isMergeLike()) defMode = "merge"; 00071 if (opt.isPatchLike()&&!opt.isRediffLike()) defMode = "apply"; 00072 std::string mode = opt.checkString("mode",defMode.c_str()); 00073 bool inplace = opt.checkBool("inplace",false); 00074 bool showPatch = opt.isPatchLike() && mode=="apply" && (!apply); 00075 bool patchy = opt.isPatchLike()||opt.isRediffLike()||opt.isMergeLike(); 00076 bool scan_for_patch = opt.checkBool("scan-for-patch",false); 00077 bool couldChangeInput = false; 00078 bool extractHeader = opt.checkBool("header"); 00079 bool extractIndex = opt.checkBool("index"); 00080 bool omitHeader = opt.checkBool("omit-header"); 00081 const vector<string>& include_columns = opt.getCompareFlags().include_columns; 00082 const vector<string>& exclude_columns = opt.getCompareFlags().exclude_columns; 00083 bool have_includes = (include_columns.size() > 0); 00084 bool have_excludes = (exclude_columns.size() > 0); 00085 00086 CompareFlags flags = opt.getCompareFlags(); 00087 PoolImpl pool; 00088 flags.pool = &pool; 00089 PolyBook pool_book; 00090 if (pool_file!="") { 00091 if (!pool_book.attachReadWrite(pool_file.c_str())) { 00092 fprintf(stderr,"Failed to attach %s\n", pool_file.c_str()); 00093 return 1; 00094 } 00095 pool.attachBook(pool_book); 00096 } 00097 00098 vector<string> core = opt.getCore(); 00099 if (opt.isMergeLike()) { 00100 if (core.size()>0) { 00101 parent_file = core[0]; 00102 core.erase(core.begin()); 00103 } 00104 } 00105 if (opt.isPatchLike()&&!opt.isFormatLike()) { 00106 couldChangeInput = true; 00107 if (core.size()>0) { 00108 if ((!have_cmd)||core.size()>1) { 00109 patch_file = core.back(); 00110 core.erase(core.begin()+core.size()-1); 00111 } 00112 } 00113 } 00114 00115 BookCompare cmp; 00116 cmp.setVerbose(verbose); 00117 00118 PolyBook _pivot; 00119 PolyBook *pivot; 00120 PolyBook _local; 00121 PolyBook *local = &_local; 00122 PolyBook _remote; 00123 PolyBook *remote = &_remote; 00124 00125 string local_file; 00126 if (core.size()>=1) { 00127 local_file = core[0]; 00128 } 00129 00130 string remote_file; 00131 bool read_and_will_write = false; 00132 if (core.size()>=2) { 00133 if (opt.isFormatLike()) { 00134 output = core[1]; 00135 read_and_will_write = true; 00136 } else { 00137 remote_file = core[1]; 00138 } 00139 } 00140 00141 dbg_printf("\n{} Diff::apply checking local file if any\n"); 00142 00143 if (local_file!="") { 00144 if (read_and_will_write) { 00145 if (!_local.readAndWillWrite(local_file.c_str(), 00146 "", 00147 output.c_str(), 00148 "")) { 00149 fprintf(stderr,"Failed to read %s\n", local_file.c_str()); 00150 return 1; 00151 } 00152 } else { 00153 if (!_local.read(local_file.c_str())) { 00154 fprintf(stderr,"Failed to read %s\n", local_file.c_str()); 00155 return 1; 00156 } 00157 } 00158 flags.local_uri = local_file; 00159 } 00160 if (inplace) { 00161 output = local_file; 00162 } 00163 00164 // an option to select a particular table - for ssformat 00165 if (flags.ordered_tables.size()>0 && opt.isFormatLike()) { 00166 TextBook *orig = _local.give(); 00167 if (orig) { 00168 orig->namedSheets(); 00169 FilteredTextBook *book = new FilteredTextBook(orig,flags.ordered_tables); 00170 if (book==NULL) { 00171 fprintf(stderr,"Failed to allocate output\n"); 00172 return 1; 00173 } 00174 book->namedSheets(); 00175 _local.take(book); 00176 _local.namedSheets(); 00177 } 00178 } 00179 00180 // an option to extract header - mostly of interest for ssformat 00181 if (extractHeader) { 00182 PolySheet sheet = _local.readSheetByIndex(0); 00183 CompareFlags flags; 00184 NameSniffer sniff(sheet,flags); 00185 ShortTextBook *book = new ShortTextBook(); 00186 if (book==NULL) { 00187 fprintf(stderr,"Failed to allocate output\n"); 00188 return 1; 00189 } 00190 for (int i=0; i<sheet.width(); i++) { 00191 book->sheet.addField(sniff.suggestColumnName(i).c_str(),false); 00192 } 00193 book->sheet.addRecord(); 00194 _local.take(book); 00195 } 00196 00197 // an option to extract index - mostly of interest for ssformat 00198 if (extractIndex) { 00199 PolySheet sheet = _local.readSheetByIndex(0); 00200 CompareFlags flags; 00201 NameSniffer nsniff(sheet,flags); 00202 IndexSniffer sniff(sheet,flags,nsniff); 00203 vector<int> indexes = sniff.suggestIndexes(); 00204 dbg_printf("Index count %d\n", (int)indexes.size()); 00205 ShortTextBook *book = new ShortTextBook(); 00206 if (book==NULL) { 00207 fprintf(stderr,"Failed to allocate output\n"); 00208 return 1; 00209 } 00210 book->sheet.copy(sheet); 00211 int at = 0; 00212 for (int i=0; i<sheet.width(); i++) { 00213 if (indexes[i]==0) { 00214 book->sheet.deleteColumn(at); 00215 } else { 00216 at++; 00217 } 00218 } 00219 _local.take(book); 00220 } 00221 00222 // options to omit/include columns 00223 if (have_excludes && opt.isFormatLike()) { 00224 for (int i=0; i<(int)exclude_columns.size(); i++) { 00225 have_cmd = true; 00226 string cmd = "@- |"; 00227 cmd += exclude_columns[i]; 00228 cmd += "|"; 00229 cmds.push_back(cmd); 00230 } 00231 couldChangeInput = true; 00232 } 00233 if (have_includes && opt.isFormatLike()) { 00234 string cmd = "@@= |"; 00235 for (int i=0; i<(int)include_columns.size(); i++) { 00236 cmd += include_columns[i]; 00237 cmd += "|"; 00238 } 00239 have_cmd = true; 00240 cmds.push_back(cmd); 00241 couldChangeInput = true; 00242 } 00243 00244 dbg_printf("\n{} Diff::apply checking remote file if any\n"); 00245 00246 bool patch_is_remote = false; 00247 if (remote_file!="") { 00248 //if (!_remote.read(remote_file.c_str())) { 00249 if (flags.offload_to_sql_when_possible) { 00250 if (!_remote.readForReference(remote_file.c_str(),_local)) { 00251 fprintf(stderr,"Failed to read %s\n", remote_file.c_str()); 00252 return 1; 00253 } 00254 } else { 00255 if (!_remote.read(remote_file.c_str())) { 00256 fprintf(stderr,"Failed to read %s\n", remote_file.c_str()); 00257 return 1; 00258 } 00259 } 00260 flags.remote_uri = remote_file; 00261 if (scan_for_patch) { 00262 if (_remote.getSheetCount()>=1) { 00263 PolySheet sheet = _remote.readSheetByIndex(0); 00264 if (sheet.isValid()) { 00265 if (sheet.width()>=1) { 00266 for (int i=0; i<3 && i<sheet.height(); i++) { 00267 if (sheet.cellString(0,i)=="@@") { 00268 patch_is_remote = true; 00269 mode = "apply"; 00270 patchy = true; 00271 showPatch = !apply; 00272 } 00273 } 00274 } 00275 } 00276 } 00277 } 00278 } 00279 00280 dbg_printf("\n{} Diff::apply doing inplace tweaks if needed\n"); 00281 00282 bool cloned = false; 00283 if (couldChangeInput&&(mode=="apply"||mode=="merge"||mode=="novel")&&!apply) { 00284 if (local_file!=""&&local->inplace()&&local->getNames().size()>0&&!inplace&&output!=local_file) { 00285 if (output=="-"&&tmp=="") { 00286 fprintf(stderr,"This operation could modify an input. Please confirm by specifying one of:\n"); 00287 fprintf(stderr," --inplace, to confirm you want the input changed.\n --output OUTPUT_FILE, to redirect the output.\n --tmp TMP_FILE, use temporary storage to avoid changing the input.\n"); 00288 return 1; 00289 } 00290 if (tmp=="") { 00291 tmp = output; 00292 } 00293 dbg_printf("Copy %s -> %s\n", local_file.c_str(), tmp.c_str()); 00294 if (!copyBook(*local,local_file.c_str(),tmp.c_str(),&cloned)) { 00295 fprintf(stderr,"Failed to write %s\n", tmp.c_str()); 00296 return 1; 00297 } 00298 dbg_printf("Cloned copy? %d\n", cloned); 00299 if (!_local.read(tmp.c_str())) { 00300 fprintf(stderr,"Failed to switch to %s\n", tmp.c_str()); 00301 return 1; 00302 } 00303 } 00304 } 00305 00306 // an option to omit header - mostly of interest for ssformat 00307 if (omitHeader) { 00308 for (int i=0; i<_local.getSheetCount(); i++) { 00309 PolySheet sheet = _local.readSheetByIndex(i); 00310 CompareFlags flags; 00311 NameSniffer sniff(sheet,flags); 00312 if (sniff.isEmbedded()) { 00313 RowRef row0(0); 00314 RowRef rowh(sniff.getHeaderHeight()-1); 00315 sheet.deleteRows(row0,rowh); 00316 } 00317 } 00318 } 00319 00320 dbg_printf("\n{} Diff::apply dealing with pivot if any\n"); 00321 00322 if (parent_file!="") { 00323 if (!_pivot.read(parent_file.c_str())) { 00324 fprintf(stderr,"Failed to read %s\n", parent_file.c_str()); 00325 return 1; 00326 } 00327 flags.pivot_uri = parent_file; 00328 pivot = &_pivot; 00329 } else { 00330 pivot = &_local; 00331 flags.pivot_sides_with_local = true; 00332 flags.has_pivot = false; 00333 } 00334 00335 if (equality) { 00336 if (local->equals(*remote,flags)) { 00337 return 0; 00338 } 00339 return 1; 00340 } 00341 00342 dbg_printf("\n{} Diff::apply creating tool: %s %s\n", mode.c_str(), version.c_str()); 00343 00344 Patcher *diff = createTool(mode,version); 00345 MergeOutputPool pooler; 00346 if (diff==NULL) { 00347 fprintf(stderr,"Failed to create handler for patch mode: '%s'\n", 00348 mode.c_str()); 00349 return 1; 00350 } 00351 PolyBook obook; 00352 if (diff->needOutputBook()) { 00353 dbg_printf("\n{} Diff::apply preparing output book\n"); 00354 // output touched 00355 if (!obook.attach(output.c_str())) { 00356 delete diff; diff = NULL; 00357 return 1; 00358 } 00359 diff->attachOutputBook(obook); 00360 } 00361 00362 PolyBook tbook; 00363 if (diff->outputStartsFromInput()&&!cloned) { 00364 dbg_printf("\n{} Diff::apply fiddling with output book #1\n"); 00365 char ch = 'x'; 00366 if (output.length()>0) { 00367 ch = output[output.length()-1]; 00368 } 00369 if (ch!='-') { // crude test for stdin/out 00370 // output_touched 00371 dbg_printf("{} Diff::apply writing local to output\n"); 00372 if (!_local.write(output.c_str())) { 00373 delete diff; diff = NULL; 00374 return 1; 00375 } 00376 dbg_printf("{} Diff::apply re-reading local\n"); 00377 if (!_local.read(local_file.c_str())) { 00378 fprintf(stderr,"Failed to read %s\n", local_file.c_str()); 00379 return 1; 00380 } 00381 // output touched 00382 dbg_printf("{} Diff::apply reading output if exists\n"); 00383 if (!tbook.readIfExists(output.c_str())) { 00384 fprintf(stderr,"Failed to read %s\n", output.c_str()); 00385 return 1; 00386 } 00387 } else { 00388 tbook.take(new CsvTextBook(true)); 00389 Property p; 00390 tbook.copy(_local,p); 00391 } 00392 diff->attachBook(tbook); 00393 pooler.attachBook(tbook); 00394 } else { 00395 dbg_printf("\n{} Diff::apply fiddling with output book #2\n"); 00396 diff->attachBook(*local); 00397 pooler.attachBook(*local); 00398 } 00399 00400 if (apply) { 00401 dbg_printf("\n{} Diff::apply prepare for application\n"); 00402 SheetPatcher *apply_diff = SheetPatcher::createForApply(); 00403 COOPY_ASSERT(apply_diff!=NULL); 00404 apply_diff->showSummary(diff); 00405 diff = apply_diff; 00406 diff->attachBook(*local); 00407 pooler.attachBook(*local); 00408 } 00409 00410 dbg_printf("\n{} Diff::apply start output\n"); 00411 if (!diff->startOutput(output,flags)) { 00412 fprintf(stderr,"Patch output failed\n"); 00413 delete diff; 00414 diff = NULL; 00415 return 1; 00416 } 00417 00418 if (meta_file!="") { 00419 dbg_printf("{} Diff::apply add meta file\n"); 00420 pooler.startOutput(output,flags); 00421 pooler.setFlags(flags); 00422 PatchParser parser(&pooler,meta_file,flags); 00423 bool ok = parser.apply(); 00424 pooler.stopOutput(output,flags); 00425 if (!ok) { 00426 fprintf(stderr,"Failed to read %s\n", meta_file.c_str()); 00427 delete diff; 00428 diff = NULL; 00429 return 1; 00430 } 00431 } 00432 00433 if (!resolving) { 00434 dbg_printf("\n{} Diff::apply diff/patch/merge\n"); 00435 diff->setFlags(flags); 00436 bool filter = flags.ordered_tables.size()>0 || flags.acts.size()>0 || 00437 flags.create_unknown_sheets || patchy || flags.resolve!=""; 00438 MergeOutputFilter filter_diff(diff); 00439 if (filter) { 00440 filter = true; 00441 //if (flags.create_unknown_sheets) { 00442 if (diff->getBook()) { 00443 filter_diff.attachBook(*(diff->getBook())); 00444 } 00445 } 00446 Patcher *active_diff = diff; 00447 if (filter) { 00448 active_diff = &filter_diff; 00449 filter_diff.startOutput("-",flags); 00450 filter_diff.setFlags(flags); 00451 } 00452 if (patch_file==""&&(!have_cmd)&&(!patch_is_remote)&&(!opt.isFormatLike())) { 00453 cmp.compare(*pivot,*local,*remote,*active_diff,flags); 00454 } else { 00455 bool ok = false; 00456 if (have_cmd) { 00457 dbg_printf("\n{} Diff::apply diff/patch/merge with command\n"); 00458 PatchParser parser(active_diff,cmds,flags); 00459 ok = parser.apply(); 00460 } else if (patch_is_remote) { 00461 dbg_printf("\n{} Diff::apply diff/patch/merge from remote\n"); 00462 PatchParser parser(active_diff,remote,flags); 00463 ok = parser.apply(); 00464 } else if (patch_file!="") { 00465 dbg_printf("\n{} Diff::apply diff/patch/merge from file\n"); 00466 PatchParser parser(active_diff,patch_file,flags); 00467 ok = parser.apply(); 00468 } else { 00469 dbg_printf("\n{} Diff::apply diff/patch/merge from other\n"); 00470 vector<string> acts; 00471 PatchParser parser(active_diff,acts,flags); 00472 ok = parser.apply(); 00473 } 00474 if (!ok) { 00475 fprintf(stderr,"Patch failed\n"); 00476 } 00477 } 00478 if (filter) { 00479 filter_diff.stopOutput("-",flags); 00480 } 00481 } else { 00482 dbg_printf("\n{} Diff::apply resolve\n"); 00483 flags.resolving = true; 00484 if (flags.remote_uri == "") { 00485 flags.remote_uri = flags.local_uri; 00486 } 00487 if (!opt.checkBool("resolving")) { 00488 vector<string> names = local->getNames(); 00489 for (int j=0; j<names.size(); j++) { 00490 PolySheet t = local->readSheetByIndex(j); 00491 t.mustHaveSchema(); 00492 SheetSchema *ss = t.getSchema(); 00493 if (ss) { 00494 for (int i=0; i<ss->getColumnCount(); i++) { 00495 string s = ss->getColumnInfo(i).getName(); 00496 if (s=="_MERGE_") { 00497 resolved = false; 00498 } 00499 } 00500 } 00501 } 00502 } else { 00503 cmp.resolve(*pivot,*local,*remote,*diff,flags); 00504 } 00505 } 00506 00507 dbg_printf("\n{} Diff::apply finish up\n"); 00508 00509 bool will_write_output = false; 00510 if (showPatch) { 00511 if ((!local->inplace())||(tmp!=output)) { 00512 if (output!=local_file || !local->inplace()) { 00513 will_write_output = true; 00514 } 00515 } 00516 } 00517 00518 diff->stopOutput(output,flags); 00519 if (diff->needOutputBook()) { 00520 if (obook.isValid()) { 00521 if (!diff->outputStartsFromInput()) { 00522 if (!will_write_output) { 00523 obook.flush(); 00524 } 00525 } 00526 } else { 00527 local->flush(); 00528 } 00529 } 00530 00531 if (pool_file!="") { 00532 pool.save(); 00533 pool_book.flush(); 00534 } 00535 00536 if (diff->outputStartsFromInput()) { 00537 // output touched 00538 if (!tbook.inplace()) { 00539 dbg_printf("{} Diff::apply write tbook\n"); 00540 if (!tbook.write(output.c_str())) { 00541 fprintf(stderr,"Failed to write %s\n", output.c_str()); 00542 return 1; 00543 } 00544 } 00545 } 00546 if (mode=="apply"&&!showPatch) { 00547 if (diff->getChangeCount()>0) { 00548 if (!local->inplace()) { 00549 dbg_printf("{} Diff::apply write local #1\n"); 00550 if (!local->write(local_file.c_str())) { 00551 fprintf(stderr,"Failed to write %s\n", local_file.c_str()); 00552 return 1; 00553 } 00554 } 00555 } 00556 } 00557 if (showPatch) { 00558 if ((!local->inplace())||(tmp!=output)) { 00559 if (output!=local_file || !local->inplace()) { 00560 dbg_printf("{} Diff::apply write local #2\n"); 00561 if (!local->write(output.c_str())) { 00562 fprintf(stderr,"Failed to write %s\n", output.c_str()); 00563 delete diff; diff = NULL; 00564 return 1; 00565 } 00566 } 00567 } 00568 } 00569 00570 00571 TextBook *output_book = diff->getBook(); 00572 if (opt.checkBool("paint")) { 00573 // Reopen file for painting - not optimal, but adequate 00574 PolyBook src; 00575 if (!src.attach(output.c_str())) { 00576 fprintf(stderr,"Failed to attach %s\n", output.c_str()); 00577 return 1; 00578 } 00579 Highlighter h; 00580 h.apply(src); 00581 src.flush(); 00582 } 00583 00584 bool conflicted = diff->isConflicted(); 00585 delete diff; 00586 diff = NULL; 00587 00588 if (opt.isMergeLike()) { 00589 if (conflicted) { 00590 fprintf(stderr,"Conflict detected.\n"); 00591 return 1; 00592 } 00593 } 00594 if (!resolved) { 00595 fprintf(stderr,"Conflict unresolved.\n"); 00596 return 1; 00597 } 00598 00599 return 0; 00600 } 00601