COOPY » Guide  version 0.6.5
/home/paulfitz/cvs/coopy_scm/coopy/src/libcoopy_full/Diff.cpp
Go to the documentation of this file.
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 
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines