COOPY » Guide  version 0.6.5
/home/paulfitz/cvs/coopy_scm/coopy/src/libcoopy_full/Options.cpp
Go to the documentation of this file.
00001 #include <stdio.h>
00002 
00003 #include <coopy/Options.h>
00004 #include <coopy/CsvSheet.h>
00005 #include <coopy/CsvWrite.h>
00006 #include <coopy/Coopy.h>
00007 #include <coopy/MergeOutputFilter.h>
00008 #include <coopy/PatchParser.h>
00009 #include <coopy/SheetPatcher.h>
00010 #include <coopy/PoolImpl.h>
00011 #include <coopy/Stringer.h>
00012 
00013 #include <coopy/CoopyVersion.h>
00014 
00015 #include <algorithm>
00016 
00017 #ifdef USE_GNULIB_GETOPT
00018 #include <gnulib_getopt/getopt.h>
00019 #else
00020 #include <getopt.h>
00021 #endif
00022 
00023 #define QUOTED_BASE(x) # x
00024 #define QUOTED_VERSION(x) QUOTED_BASE(x)
00025 
00026 using namespace std;
00027 using namespace coopy::store;
00028 using namespace coopy::cmp;
00029 using namespace coopy::app;
00030 
00031 
00032 
00033 
00034 
00035 class OptionCompare {
00036 public:
00037   bool operator() (const Option& o1, const Option& o2) {
00038     if (o1.is_default&&!o2.is_default) return true;
00039     if (o2.is_default&&!o1.is_default) return false;
00040     return (o1.long_name<o2.long_name);
00041   }
00042 };
00043 
00044 class OptionRender {
00045 public:
00046   virtual void render(const Options& opt) = 0;
00047 };
00048 
00049 class OptionRenderCmdLine : public OptionRender {
00050 public:
00051   int w;
00052 
00053   OptionRenderCmdLine() {
00054     w = 78;
00055   }
00056 
00057   void showOptions(const Options& opt, int filter);
00058   virtual void render(const Options& opt);
00059 };
00060 
00061 class OptionRenderDoxygen : public OptionRender {
00062 public:
00063   void showOptions(const Options& opt, int filter, const string& prefix,
00064                    bool detailed = false, bool compact = false);
00065   virtual void render(const Options& opt);
00066 };
00067 
00068 static void addSpaces(int n) {
00069   for (int i=0; i<n; i++) {
00070     printf(" ");
00071   }
00072 }
00073 
00074 static void wrapCode(const string& code, int off) {
00075   string add = "";
00076   for (int i=0; i<off; i++) { add += " "; }
00077   string txt = code;
00078   Stringer::replace(txt,"\\","\\\n");
00079   Stringer::replace(txt,"\n",string("\n")+add);
00080   printf("%s%s\n",add.c_str(),txt.c_str());
00081 }
00082 
00083 static void wrapText(const string& desc, int w, int tot) {
00084   string txt = desc;
00085   if (txt.length()>0) {
00086     txt[0] = toupper(txt[0]);
00087   }
00088   while (txt.length()>0) {
00089     string next = txt.substr(0,w-tot+1);
00090     if (next.length()>w-tot) {
00091       string::size_type at = next.rfind(" ");
00092       if (at!=string::npos) {
00093         next = txt.substr(0,at);
00094         txt = txt.substr(at+1,txt.length());
00095       } else {
00096         next = txt.substr(0,w-tot);
00097         txt = txt.substr(w-tot,txt.length());
00098       }
00099     } else {
00100       txt = "";
00101     }
00102     printf("%s\n", next.c_str());
00103     if (txt.length()>0) {
00104       if (tot>0) {
00105         addSpaces(tot);
00106       }
00107     }
00108   }
00109 }
00110 
00111 void OptionRenderCmdLine::showOptions(const Options& opt, int filter) {
00112   bool flaggy = true;
00113   int start = 2;
00114   int len = 15;
00115   if (filter==OPTION_PATCH_FORMAT) {
00116     flaggy = false;
00117     len = 6;
00118     start = 18;
00119   }
00120 
00121   vector<Option> mopts = opt.getOptionList();
00122   sort(mopts.begin(),mopts.end(),OptionCompare());
00123 
00124   for (int i=0; i<(int)mopts.size(); i++) {
00125     Option& o = mopts[i];
00126     int tot = start+len+1;
00127     if (o.coverage&filter) {
00128       addSpaces(start);
00129       string pre = o.long_name;
00130       if (flaggy) {
00131         pre = string("--")+pre;
00132       }
00133       if (o.arg!="") {
00134         pre += "=";
00135         pre += o.arg;
00136       }
00137       if (pre.length()>len) {
00138         printf("%s\n", pre.c_str());
00139         addSpaces(start);
00140         pre = "";
00141       }
00142       while (pre.length()<len) pre += " ";
00143       printf("%s ", pre.c_str());
00144 
00145       string desc = o.desc;
00146       if (o.is_default) {
00147         desc = string("[default] ") + desc;
00148       }
00149       wrapText(desc, w, tot);
00150       if (o.long_name=="format") {
00151         showOptions(opt,OPTION_PATCH_FORMAT);
00152       }
00153     }
00154   }
00155 }
00156 
00157 void OptionRenderCmdLine::render(const Options& opt) {
00158   printf("%s version %s\n", opt.getName().c_str(), opt.getVersion().c_str());
00159   printf("Usage\n");
00160   printf("\n");
00161   const vector<string>& usages = opt.getUsages();
00162   for (int i=0; i<(int)usages.size(); i++) {
00163     printf("  %s\n", usages[i].c_str());
00164   }
00165   printf("\n");
00166   const string& desc = opt.getDescription();
00167   wrapText(desc,w,0);
00168   //printf("%s\n", desc.c_str());
00169   printf("\n");
00170   printf("Options\n");
00171   int f = opt.getOptionFilter();
00172   showOptions(opt,f);
00173 
00174   const vector<Example>& examples = opt.getExamples();
00175   if (examples.size()==0) return;
00176   printf("\n");
00177   printf("Examples\n");
00178 
00179   const vector<string> reqs = opt.getExampleReqs();
00180   if (reqs.size()>0) {
00181     printf("  You can generate test file(s) to use with the examples that follow:\n");
00182     for (int i=0; i<(int)reqs.size(); i++) {
00183       if (reqs[i][0]!='_') {
00184         printf("    %s --test-file %s\n", opt.getName().c_str(), reqs[i].c_str());
00185       }
00186     }
00187     const vector<string> recipes = opt.getExampleRecipes(reqs);
00188     for (int i=0; i<(int)recipes.size(); i++) {
00189       printf("    %s\n", recipes[i].c_str());
00190     }
00191     printf("\n");
00192   }
00193 
00194   for (int i=0; i<(int)examples.size(); i++) {
00195     if (i>0) printf("\n");
00196     const Example& eg = examples[i];
00197     //printf("  %s\n", eg.code.c_str());
00198     wrapCode(eg.code,2);
00199     printf("    ");
00200     wrapText(eg.desc,w,4);
00201   }
00202 }
00203 
00204 
00205 void OptionRenderDoxygen::showOptions(const Options& opt, int filter,
00206                                       const string& prefix, bool detailed,
00207                                       bool compact) {
00208   bool flaggy = (filter!=OPTION_PATCH_FORMAT);
00209   vector<Option> mopts = opt.getOptionList();
00210   sort(mopts.begin(),mopts.end(),OptionCompare());
00211 
00212   
00213   for (int i=0; i<(int)mopts.size(); i++) {
00214     Option& o = mopts[i];
00215     if (o.coverage&filter) {
00216       string pre = o.long_name;
00217       if (flaggy) {
00218         pre = string("--")+pre;
00219       }
00220       if (o.arg!="") {
00221         pre += "=";
00222         pre += o.arg;
00223       }
00224       
00225       string anchor = prefix + "_" + o.long_name;
00226       string desc = o.desc;
00227       if (o.is_default) {
00228         desc = string("<i>[default]</i> ") + desc;
00229       }
00230       if (!detailed) {
00231           printf(" \\li \\ref %s \"%s\"\n", anchor.c_str(), pre.c_str());
00232       } else if (compact) {
00233         printf("  \\li <b>%s</b>: ", pre.c_str());
00234         printf("%s\n", desc.c_str());
00235       } else {
00236         printf("\\anchor %s <b>%s</b> <br />", anchor.c_str(), pre.c_str());    
00237         printf("%s\n\n\n", desc.c_str());
00238       }
00239     }
00240   }
00241 }
00242 
00243 void OptionRenderDoxygen::render(const Options& opt) {
00244   printf("/**\n");
00245   printf(" *\n\n");
00246 
00247   printf("@page %s %s\n\n", opt.getName().c_str(),
00248          opt.getName().c_str());
00249 
00250   printf("%s\n\n", opt.getDescription().c_str());
00251 
00252   printf("\n\n\\section %s_usage Usage\n", opt.getName().c_str());
00253   const vector<string>& usages = opt.getUsages();
00254   for (int i=0; i<(int)usages.size(); i++) {
00255     printf(" \\li %s\n", usages[i].c_str());
00256   }
00257   const vector<Example>& examples = opt.getExamples();
00258   printf("\\if MAN_PAGE_COND\n");
00259   printf("\\else\n");
00260   printf("\n\n\\section %s_index Index\n", opt.getName().c_str());
00261   printf("  \\li \\ref %s_options\n", opt.getName().c_str());
00262   printf("  \\li \\ref %s_options_detail\n", opt.getName().c_str());
00263   if (examples.size()>0) {
00264     printf("  \\li \\ref %s_examples\n", opt.getName().c_str());
00265   }
00266   bool show_patch = opt.checkBool("show-patch",true);
00267   bool show_input_format = opt.checkBool("show-input-format",true);
00268   if (show_patch) {
00269     printf("  \\li \\ref %s_patch\n", opt.getName().c_str());
00270   }
00271   if (show_input_format) {
00272     printf("  \\li \\ref %s_table\n", opt.getName().c_str());
00273   }
00274   printf("  \\li \\ref %s_version\n", opt.getName().c_str());
00275   printf("\\endif\n");
00276 
00277   printf("\n\n\\section %s_options Option summary\n", opt.getName().c_str());
00278 
00279   int f = opt.getOptionFilter();
00280   string anchor = opt.getName() + "_main";
00281   showOptions(opt,f,anchor);
00282 
00283   printf("\n\n\\section %s_options_detail Option details\n", opt.getName().c_str());
00284   showOptions(opt,f,anchor,true);
00285 
00286   if (examples.size()>0) {
00287     printf("\n\n\\section %s_examples Examples\n", opt.getName().c_str());
00288   }
00289 
00290   const vector<string> reqs = opt.getExampleReqs();
00291   if (reqs.size()>0) {
00292     printf("You can generate test file(s) for the examples that follow:\n");
00293     printf("\\verbatim\n");
00294     for (int i=0; i<(int)reqs.size(); i++) {
00295       if (reqs[i][0]!='_') {
00296         printf("%s --test-file %s\n", opt.getName().c_str(), reqs[i].c_str());
00297       }
00298     }
00299     printf("\\endverbatim\n");
00300     printf("\n\n");
00301   }
00302 
00303   for (int i=0; i<(int)examples.size(); i++) {
00304     if (i>0) printf("\n\n");
00305     const Example& eg = examples[i];
00306     printf("\n\n\\subsection %s_examples_%d Example %d\n", 
00307            opt.getName().c_str(),
00308            i+1, i+1);
00309     printf("\\verbatim\n");
00310     wrapCode(eg.code,0);
00311     printf("\\endverbatim\n");
00312     printf("%s\n\n",eg.desc.c_str());
00313   }
00314 
00315   if (show_patch) {
00316     printf("\n\n\\section %s_patch Patch formats\n", opt.getName().c_str());
00317     showOptions(opt,OPTION_PATCH_FORMAT,anchor,true,true);
00318   }
00319 
00320   if (show_input_format) {
00321     printf("\n\n\\section %s_table Database/spreadsheet file formats\n", opt.getName().c_str());
00322     vector<FormatDesc> descs = PolyBook::getFormatList();
00323     for (int i=0; i<(int)descs.size(); i++) {
00324       const FormatDesc& fd = descs[i];
00325       printf("%s<br />\n", fd.name.c_str());
00326       for (int i=0; i<(int)fd.exts.size(); i++) {
00327         printf("  \\li<b>%s</b>: %s\n",
00328                fd.exts[i].ext.c_str(),
00329                fd.exts[i].notes.c_str());
00330       }
00331       const vector<FormatDesc::Option>& opts = fd.opts;
00332       if (opts.size()>0) {
00333         string result;
00334         printf("  \\li<b>.json</b>: {<br />\n");
00335         for (int i=0; i<(int)opts.size(); i++) {
00336           const FormatDesc::Option& o = opts[i];
00337           result += "      \"";
00338           result += o.tag;
00339           result += "\": ";
00340           PolyValue v = o.val;
00341           if (o.val.isString()) {
00342             result += "\"";
00343           }
00344           result += o.val.asString();
00345           if (o.val.isString()) {
00346             result += "\"";
00347           }
00348           if (i<(int)opts.size()-1) {
00349             result += ",";
00350           }
00351           if (o.notes!="") {
00352             result += "  <i>// ";
00353             result += o.notes;
00354             result += "</i>";
00355           }
00356           result += "<br />\n";
00357         }
00358         printf("%s}\n", result.c_str());
00359       }
00360       const vector<FormatDesc::Dbi>& dbis = fd.dbis;
00361       for (int i=0; i<(int)dbis.size(); i++) {
00362         printf("  \\li <b>%s</b>", dbis[i].ex.c_str());
00363         if (dbis[i].notes!="") {
00364           printf(" (%s) ", dbis[i].notes.c_str());
00365         }
00366         printf("\n");
00367       }
00368       printf("\n\n");
00369     }
00370   }
00371 
00372   printf("\n\n\\section %s_version Version\n", opt.getName().c_str());
00373 
00374   printf("%s version %s\n", opt.getName().c_str(), opt.getVersion().c_str());
00375 
00376   printf("\n\n");
00377   printf(" *\n");
00378   printf(" */\n");
00379 }
00380 
00381 
00382 
00383 
00384 
00385 void Options::add(int cov, const char *name, const char *desc) {
00386   Option o;
00387   o.coverage = cov;
00388   o.desc = desc;
00389   string n(name);
00390   if (n[0]=='*') {
00391     n = n.substr(1,n.length());
00392     o.is_default = true;
00393   }
00394   string::size_type idx = n.find("=");
00395   if (idx!=string::npos) {
00396     o.long_name = n.substr(0,idx);
00397     o.arg = n.substr(idx+1,n.length());
00398   } else {
00399     o.long_name = n;
00400   }
00401   opts.push_back(o);
00402 }
00403 
00404 
00405 Options::Options(const char *name) : name(name) {
00406   addAll("help",
00407          "show how to use this program");
00408   addTransform("output=OUTPUTFILE",
00409                "direct output to this file (default is standard output)");
00410   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00411       "format=FORMAT",
00412       "set difference format for output");
00413   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00414       "variant=VARIANT",
00415       "set the desired dialect when using a poorly defined output format (currently for SQL, available variants are: sqlite, access)");
00416   add(OPTION_FOR_DIFF|OPTION_FOR_MERGE|OPTION_FOR_PATCH|OPTION_FOR_FORMAT,
00417       "input-formats",
00418       "list supported input database formats");
00419   add(OPTION_FOR_DIFF|OPTION_FOR_MERGE,
00420       "headerless",
00421       "treat any embedded column names as regular parts of the table (for formats like CSV)");
00422   addTransform("patch-formats",
00423                "list supported patch formats");
00424   addCompare("id=COLUMN",
00425              "set primary key (repeat option for multi-column key)");
00426   addCompare("bid=COLUMN",
00427              "boost a column (repeat option for multiple columns)");
00428   addCompare("named",
00429              "trust names of columns, omitting checks for column renames");
00430   addCompare("unordered",
00431              "treat order of rows as unimportant");
00432   addCompare("fixed-columns",
00433              "ignore new or removed columns");
00434   addCompare("head-trimmed",
00435              "ignore rows removed at the beginning of a table (such as a log file)");
00436   addCompare("tail-trimmed",
00437              "ignore rows removed at the end of a table (such as a log file)");
00438   add(OPTION_FOR_DIFF|OPTION_FOR_MERGE|OPTION_FOR_FORMAT|OPTION_FOR_PATCH|OPTION_FOR_RESOLVE|OPTION_FOR_REDIFF,
00439       "default-table=TABLE",
00440       "name to use when a table name is needed and not supplied");
00441   add(OPTION_FOR_FORMAT,
00442       "table=TABLE",
00443       "operate on a single named table of a workbook/database");
00444   add(OPTION_FOR_FORMAT,
00445       "paint",
00446       "add color highlighting appropriate for highlighter diffs");
00447   add(OPTION_FOR_DIFF|OPTION_FOR_MERGE|OPTION_FOR_PATCH|OPTION_FOR_REDIFF,
00448       "table=TABLE",
00449       "filter for a named table of a workbook/database (repeat option for multiple tables)");
00450   add(OPTION_FOR_DIFF,
00451       "apply",
00452       "apply difference between FILE1 and FILE2 immediately to FILE1");
00453   add(OPTION_FOR_DIFF,
00454       "parent=PARENT",
00455       "use named workbook/database as common ancestor in difference calculations");
00456   add(OPTION_FOR_MERGE|OPTION_FOR_PATCH,
00457       "inplace",
00458       "if modifications are made, make them in place without a copy");
00459 
00460   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00461       "omit-format-name",
00462       "omit any version-dependent header from diff");
00463 
00464   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00465       "omit-sheet-name",
00466       "omit any sheet/table name from diff");
00467 
00468   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00469       "low-memory",
00470       "prioritize low memory usage over speed");
00471 
00472   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00473       "context=N",
00474       "Number of rows of context before and after changes for highlighter diffs (\"all\" to include all rows)");
00475 
00476   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF|OPTION_FOR_PATCH,
00477       "act=ACT",
00478       "filter for an action of a particular type (update, insert, delete, none, schema)");
00479 
00480   add(OPTION_FOR_PATCH,
00481       "cmd=CMD",
00482       "specify an action inline in tdiff format");
00483 
00484   add(OPTION_FOR_FORMAT,
00485       "header",
00486       "extract column names only");
00487 
00488   add(OPTION_FOR_FORMAT,
00489       "omit-header",
00490       "remove column names");
00491 
00492   add(OPTION_FOR_FORMAT,
00493       "index",
00494       "extract content of key columns only");
00495 
00496   add(OPTION_FOR_FORMAT,
00497       "include-column=COLUMN",
00498       "include only the specified column in the output (repeat option to include multiple columns)");
00499 
00500   add(OPTION_FOR_FORMAT,
00501       "exclude-column=COLUMN",
00502       "include all but the specified column in the output (repeat option to exclude multiple columns)");
00503 
00504   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00505       "include-column=COLUMN",
00506       "include the specified column even if unchanged");
00507 
00508   add(OPTION_FOR_DIFF|OPTION_FOR_REDIFF,
00509       "exclude-column=COLUMN",
00510       "exclude the specified column even if changed");
00511 
00512   add(OPTION_FOR_RESOLVE|OPTION_FOR_REDIFF|OPTION_FOR_PATCH,
00513       "theirs",
00514       "in case of conflict use cell value that wasn't the local choice");
00515 
00516   add(OPTION_FOR_RESOLVE|OPTION_FOR_REDIFF|OPTION_FOR_PATCH,
00517       "ours",
00518       "in case of conflict use cell value that was the local choice");
00519 
00520   add(OPTION_FOR_RESOLVE|OPTION_FOR_REDIFF|OPTION_FOR_PATCH,
00521       "neither",
00522       "in case of conflict use cell value from common ancestor");
00523 
00524   add(OPTION_FOR_RESOLVE,
00525       "dry-run",
00526       "make no changes, just describe what would happen");
00527 
00528   add(OPTION_FOR_COOPY,
00529       "gui",
00530       "force GUI to be shown");
00531 
00532   add(OPTION_FOR_COOPY,
00533       "silent",
00534       "keep output to a minimum");
00535 
00536   add(OPTION_FOR_COOPY,
00537       "pull",
00538       "pull in data from remote repository to local clone");
00539 
00540   add(OPTION_FOR_COOPY,
00541       "push",
00542       "push out data to remote repository from local clone");
00543 
00544   add(OPTION_FOR_COOPY,
00545       "key=KEY",
00546       "use specified key when adding or exporting a spreadsheet/database");
00547 
00548   add(OPTION_FOR_COOPY,
00549       "add=FILE",
00550       "attach the given spreadsheet/database to the repository");
00551 
00552   add(OPTION_FOR_COOPY,
00553       "export=FILE",
00554       "export the given spreadsheet/database from the repository");
00555 
00556   add(OPTION_FOR_COOPY,
00557       "clone=URL",
00558       "clone the given repository");
00559 
00560   add(OPTION_FOR_COOPY,
00561       "new",
00562       "create a new, empty repository");
00563 
00564   add(OPTION_FOR_COOPY,
00565       "message=MESSAGE",
00566       "use the specified message as a log entry");
00567 
00568   add(OPTION_PATCH_FORMAT,
00569       "*tdiff",
00570       "reminiscent of the standard unix diff format for text");
00571   add(OPTION_PATCH_FORMAT,
00572       "raw",
00573       "verbose diff format for debugging");
00574   add(OPTION_PATCH_FORMAT,
00575       "sql",
00576       "SQL format (data diffs only)");
00577   add(OPTION_PATCH_FORMAT,
00578       "hilite",
00579       "colorful spreadsheet format");
00580   add(OPTION_PATCH_FORMAT,
00581       "review",
00582       "spreadsheet diff format suitable for quickly accepting or rejecting changes");
00583   add(OPTION_PATCH_FORMAT,
00584       "index",
00585       "tabular output showing relationship between rows and columns");
00586   add(OPTION_PATCH_FORMAT,
00587       "csv",
00588       "csv-compatible diff format");
00589   add(OPTION_PATCH_FORMAT,
00590       "ops",
00591       "summarize modified rows in a table");
00592   add(OPTION_PATCH_FORMAT,
00593       "stats",
00594       "produce statistics on table changes");
00595   add(OPTION_PATCH_FORMAT,
00596       "novel",
00597       "mark all shared rows - remaining rows are unmatched");
00598 
00599   add(OPTION_FOR_DIFF,
00600       "scan-for-patch",
00601       "check if FILE2 looks like a patch, and if so, apply it");
00602 
00603   add(OPTION_FOR_DIFF,
00604       "git",
00605       "expect git-compatible parameters (path old-file old-hex old-mode new-file new-hex new-mode)");
00606 }
00607 
00608 std::string Options::getVersion() const {
00609   return QUOTED_VERSION(COOPY_VERSION);
00610 }
00611 
00612 static void generateNumbers(CsvSheet& csv, bool buggy, bool addy, 
00613                             bool conflict) {
00614   csv.addField("NAME",false);
00615   csv.addField("DIGIT",false);
00616   csv.addRecord();
00617   csv.addField("one",false);
00618   csv.addField("1",false);
00619   csv.addRecord();
00620   csv.addField("two",false);
00621   csv.addField("2",false);
00622   csv.addRecord();
00623   csv.addField("three",false);
00624   csv.addField("3",false);
00625   csv.addRecord();
00626   csv.addField("four",false);
00627   csv.addField("4",false);
00628   csv.addRecord();
00629   csv.addField("five",false);
00630   csv.addField("5",false);
00631   csv.addRecord();
00632   
00633   if (buggy) {
00634     csv.cellString(1,4,"999");
00635     csv.deleteRow(RowRef(3));
00636   }
00637 
00638   if (addy) {
00639     RowRef r = csv.insertRow(RowRef(-1));
00640     csv.cellString(0,r.getIndex(),"six");
00641     csv.cellString(1,r.getIndex(),"6");
00642     r = csv.insertRow(RowRef(-1));
00643     csv.cellString(0,r.getIndex(),"seven");
00644     csv.cellString(1,r.getIndex(),"7");
00645     csv.insertRow(RowRef(1));
00646     csv.cellString(0,1,"zero");
00647     csv.cellString(1,1,"0");
00648   }
00649 
00650   if (conflict) {
00651     if (buggy||addy) {
00652       fprintf(stderr,"* cannot make what you want\n");
00653       exit(1);
00654     }
00655     csv.deleteRow(RowRef(3));
00656     csv.cellString(1,3,"444");
00657   }
00658 }
00659 
00660 static bool generateExample(const string& name) {
00661   if (name=="numbers.csv"||name=="numbers_buggy.csv"||
00662       name=="numbers_add.csv"||name=="numbers_add.sqlite"||
00663       name=="numbers_buggy_add.csv"||name=="numbers_buggy_add.sqlite"||
00664       name=="numbers_conflict.csv"||name=="numbers_conflict.sqlite"||
00665       name=="numbers.sqlite"||name=="numbers_buggy.sqlite") {
00666     CsvSheet csv;
00667     generateNumbers(csv,name.find("_buggy")!=string::npos,
00668                     name.find("_add")!=string::npos,
00669                     name.find("_conflict")!=string::npos);
00670 
00671     WrapBook book1(csv,false);
00672     book1.addReference();
00673     PolyBook book2;
00674     book2.take(&book1);
00675     if (!book2.write(name.c_str())) {
00676       fprintf(stderr,"* failed to generate %s\n", name.c_str());
00677       return false;
00678     }
00679     fprintf(stderr,"* generated %s\n", name.c_str());
00680     return true;
00681   }
00682   if (name=="directory.sqlite") {
00683     PolyBook book;
00684     if (book.attach("directory.sqlite")) {
00685       string dir = " \n\
00686 @@@ locations \n\
00687 x location |id=| \n\
00688 @ |id|street|city| \n\
00689 + |1|305 Memorial Drive|Cambridge| \n\
00690 + |2|Big Crater|Moon| \n\
00691 \n\
00692 @@@ org2loc \n\
00693 x organization |org_id| \n\
00694 x location |loc_id| \n\
00695 @ |org_id|loc_id| \n\
00696 + |1|2| \n\
00697 + |2|1| \n\
00698 + |3|1| \n\
00699 + |3|2| \n\
00700 \n\
00701 @@@ organizations \n\
00702 x organization |id=| \n\
00703 @ |id|name| \n\
00704 + |1|Dracula Inc| \n\
00705 + |2|The Firm| \n\
00706 + |3|Omni Cooperative| \n\
00707 + |4|Nonexistence Unlimited| \n\
00708 ";
00709 
00710       PoolImpl pool;
00711       CompareFlags flags;
00712       flags.create_unknown_sheets = true;
00713       flags.clean_sheets = true;
00714       flags.pool = &pool;
00715       vector<string> cmd;
00716       cmd.push_back(dir);
00717       SheetPatcher sink;
00718       sink.attachBook(book);
00719       sink.setFlags(flags);
00720       MergeOutputFilter filter(&sink);
00721       filter.attachBook(book);
00722       filter.setFlags(flags);
00723       PatchParser parser(&filter,cmd,flags);
00724       parser.apply();
00725       book.flush();
00726       return true;
00727     }
00728     fprintf(stderr,"* failed to generate %s\n", name.c_str());
00729     return false;
00730   }
00731   if (name=="numbers_patch.tdiff") {
00732     CsvSheet csv1, csv2;
00733     generateNumbers(csv1,true,false,false);
00734     generateNumbers(csv2,false,false,false);
00735     Coopy coopy;
00736     coopy.setOutput("numbers_patch.tdiff");
00737     if (coopy.compare(csv1,csv2)) {
00738       fprintf(stderr,"* generated %s\n", name.c_str());
00739       return true;
00740     }
00741     fprintf(stderr,"* failed to generate %s\n", name.c_str());
00742   }
00743   return false;
00744 }
00745 
00746 int Options::apply(int argc, char *argv[]) {
00747   /*fprintf(stderr,">>> IT BEGINS: %s =", getName().c_str());
00748   for (int i=0; i<argc; i++) {
00749     fprintf(stderr," %s", argv[i]);
00750   }
00751   fprintf(stderr,"\n");
00752   */
00753 
00754   //printf("Hello good evening and welcome\n");
00755   //for (int i=0; i<argc; i++) {
00756   //printf("[%d] [%s]\n", i, argv[i]);
00757   //}
00758 
00759   core.clear();
00760   flags = CompareFlags();
00761   option_bool.clear();
00762   mapping.clear();
00763 
00764   option_string["output"] = "-";
00765   option_string["parent"] = "";
00766   option_string["version"] = "";
00767 
00768   bool process = (argc>1);
00769 
00770   // an escape-hatch for systems where getopt_long does not exist
00771   // (javascript/emscripten, I'm looking at you)
00772 
00773   if (argc>=2) {
00774     if (string(argv[1])=="--no-get-opt") process = false;
00775   }
00776 
00777   while (process) {
00778     int option_index = 0;
00779     static struct option long_options[] = {
00780       {(char*)"format", 1, 0, 'f'},
00781 
00782       {(char*)"format-csv", 0, 0, 'c'},
00783       {(char*)"format-sql", 0, 0, 's'},
00784       {(char*)"format-raw", 0, 0, 'r'},
00785       {(char*)"format-tdiff", 0, 0, 't'},
00786       {(char*)"format-index", 0, 0, 'i'},
00787 
00788       {(char*)"input-format", 1, 0, 0},
00789       {(char*)"output-format", 1, 0, 0},
00790 
00791       {(char*)"apply", 0, 0, 'a'},
00792       {(char*)"dry-run", 0, 0, 0},
00793 
00794       {(char*)"equals", 0, 0, 'e'},
00795       {(char*)"index", 0, 0, 'i'},
00796       {(char*)"verbose", 0, 0, 'v'},
00797 
00798       {(char*)"id", 1, 0, 'k'},
00799       {(char*)"bid", 1, 0, 'b'},
00800       {(char*)"coin", 1, 0, 0},
00801       {(char*)"exclude-column", 1, 0, 0},
00802       {(char*)"include-column", 1, 0, 0},
00803 
00804       {(char*)"named", 0, 0, 'd'},
00805 
00806       {(char*)"fixed-columns", 0, 0, 'F'},
00807       {(char*)"ignore-case", 0, 0, 'A'},
00808 
00809       {(char*)"ordered", 0, 0, '1'},
00810       {(char*)"unordered", 0, 0, '0'},
00811 
00812       {(char*)"map", 1, 0, 'm'},
00813 
00814       {(char*)"omit-format-name", 0, 0, 'O'},
00815       {(char*)"omit-sheet-name", 0, 0, 'P'},
00816       {(char*)"header", 0, 0, 0},
00817       {(char*)"omit-header", 0, 0, 0},
00818 
00819       {(char*)"table", 1, 0, 'T'},
00820 
00821       {(char*)"output", 1, 0, 'o'},
00822       {(char*)"format-version", 1, 0, 'V'},
00823       {(char*)"parent", 1, 0, 'p'},
00824       {(char*)"input-formats", 0, 0, 'l'},
00825       {(char*)"help", 0, 0, 'h'},
00826 
00827       {(char*)"head-trimmed", 0, 0, 0},
00828       {(char*)"tail-trimmed", 0, 0, 0},
00829       {(char*)"fake", 0, 0, 0},
00830       {(char*)"inplace", 0, 0, 0},
00831       {(char*)"tmp", 1, 0, 0},
00832       {(char*)"patch", 1, 0, 0},
00833       {(char*)"resolve", 1, 0, 0},
00834       {(char*)"variant", 1, 0, 0},
00835 
00836       {(char*)"theirs", 0, 0, 0},
00837       {(char*)"ours", 0, 0, 0},
00838       {(char*)"neither", 0, 0, 0},
00839 
00840       {(char*)"cmd", 1, 0, 'x'},
00841 
00842       {(char*)"version", 0, 0, 0},
00843       {(char*)"strict", 0, 0, 0},
00844       {(char*)"patch-formats", 0, 0, 0},
00845       {(char*)"default-table", 1, 0, 0},
00846       {(char*)"eol", 1, 0, 0},
00847       {(char*)"headerless", 0, 0, 0},
00848       {(char*)"paint", 0, 0, 0},
00849       {(char*)"scan-for-patch", 0, 0, 0},
00850 
00851       {(char*)"act", 1, 0, 0},
00852 
00853       {(char*)"help-doxygen", 0, 0, 0},
00854       {(char*)"test-file", 1, 0, 0},
00855 
00856       {(char*)"foreign", 0, 0, 0},
00857       {(char*)"native", 0, 0, 0},
00858 
00859       {(char*)"meta", 1, 0, 0},
00860       {(char*)"pool", 1, 0, 0},
00861 
00862       {(char*)"create", 0, 0, 0},
00863 
00864       {(char*)"low-memory", 0, 0, 0},
00865 
00866       {(char*)"git", 0, 0, 0},
00867 
00868       {(char*)"context", 1, 0, 0},
00869 
00870       {0, 0, 0, 0}
00871     };
00872 
00873 #ifdef USE_GNULIB_GETOPT
00874     int c = gnulib_getopt_long(argc, argv, "", long_options, &option_index);
00875 #else
00876     int c = getopt_long(argc, argv, "", long_options, &option_index);
00877 #endif
00878     if (c==-1) break;
00879     switch (c) {
00880     case 0:
00881       {
00882         string k = long_options[option_index].name;
00883         if (k=="head-trimmed") {
00884           flags.head_trimmed = true;
00885         } else if (k=="tail-trimmed") {
00886           flags.tail_trimmed = true;
00887         } else if (k=="fake") {
00888           option_string["mode"] = "raw";
00889         } else if (k=="inplace") {
00890           option_bool["inplace"] = true;
00891         } else if (k=="tmp") {
00892           option_string["tmp"] = optarg;
00893         } else if (k=="patch") {
00894           option_string["patch"] = optarg;
00895         } else if (k=="header") {
00896           option_bool["header"] = true;
00897         } else if (k=="omit-header") {
00898           option_bool["omit-header"] = true;
00899         } else if (k=="input-format") {
00900           option_string["input-format"] = optarg;
00901         } else if (k=="output-format") {
00902           option_string["output-format"] = optarg;
00903         } else if (k=="default-table") {
00904           coopy_set_default_table_name(optarg);
00905         } else if (k=="patch-formats") {
00906           OptionRenderCmdLine render;
00907           render.showOptions(*this,OPTION_PATCH_FORMAT);
00908           exit(0);
00909         } else if (k=="help-doxygen") {
00910           option_bool["help"] = true;
00911           option_bool["help-doxygen"] = true;
00912         } else if (k=="version") {
00913           option_bool["version"] = true;
00914           printf("%s\n", getVersion().c_str());
00915           exit(0);
00916         } else if (k=="strict") {
00917           coopy_set_strict(true);
00918         } else if (k=="variant") {
00919           flags.variant = optarg;
00920         } else if (k=="resolve") {
00921           option_bool["resolving"] = true;
00922           flags.resolve = option_string["resolve"] = optarg;
00923         } else if (k=="theirs") {
00924           option_bool["resolving"] = true;
00925           flags.resolve = option_string["resolve"] = "theirs";
00926         } else if (k=="ours") {
00927           option_bool["resolving"] = true;
00928           flags.resolve = option_string["resolve"] = "ours";
00929         } else if (k=="neither") {
00930           option_bool["resolving"] = true;
00931           flags.resolve = option_string["resolve"] = "neither";
00932         } else if (k=="dry-run") {
00933           option_bool["apply"] = false;
00934         } else if (k=="headerless") {
00935           flags.assume_header = false;
00936         } else if (k=="foreign") {
00937           flags.foreign_pool = true;
00938           flags.foreign_pool_set = true;
00939         } else if (k=="native") {
00940           flags.foreign_pool = false;
00941           flags.foreign_pool_set = true;
00942         } else if (k=="paint") {
00943           option_bool["paint"] = true;
00944         } else if (k=="git") {
00945           option_bool["git"] = true;
00946         } else if (k=="low-memory") {
00947           option_bool["low-memory"] = true;
00948           flags.offload_to_sql_when_possible = true;
00949         } else if (k=="scan-for-patch") {
00950           option_bool["scan-for-patch"] = true;
00951         } else if (k=="test-file") {
00952           bool ok = generateExample(optarg);
00953           if (!ok) {
00954             exit(1);
00955           }
00956           option_bool["gen"] = true;
00957         } else if (k=="eol") {
00958           string eol = optarg;
00959           if (eol!="native"&&eol!="CRLF"&&eol!="LF") {
00960             fprintf(stderr,"EOL style not recognized: use native, CRLF, or LF.\n");
00961             exit(1);
00962           }
00963           fprintf(stderr,"Warning: EOL style does not do anything yet. It is a placeholder.\n");
00964           coopy_set_default_eol_style(eol.c_str());
00965         } else if (k=="act") {
00966           string act = optarg;
00967           if (act=="+") act = "insert";
00968           if (act=="-") act = "delete";
00969           if (act=="=") act = "update";
00970           if (act=="0") act = "none";
00971           if (act=="upsert") {
00972             flags.acts.insert("update");
00973             flags.acts.insert("insert");
00974           } else if (act=="update"||act=="insert"||act=="delete"||act=="none"||act=="schema") {
00975             flags.acts.insert(act);
00976           } else {
00977             fprintf(stderr,"Unknown action %s\n", act.c_str());
00978             fprintf(stderr,"Try: update, insert, delete\n");
00979             return 1;
00980           }
00981         } else if (k == "coin") {
00982           flags.coined.push_back(optarg);
00983         } else if (k == "include-column") {
00984           flags.include_columns.push_back(optarg);
00985         } else if (k == "exclude-column") {
00986           flags.exclude_columns.push_back(optarg);
00987         } else if (k == "coin") {
00988           flags.coined.push_back(optarg);
00989         } else if (k == "meta") {
00990           option_string["meta"] = optarg;
00991         } else if (k == "pool") {
00992           option_string["pool"] = optarg;
00993         } else if (k == "create") {
00994           flags.create_unknown_sheets = true;
00995         } else if (k=="context") {
00996           flags.context_lines = atoi(optarg);
00997           if (string(optarg)=="all") flags.context_lines = -1;
00998           if (string(optarg)=="default") flags.context_lines = -2;
00999         } else {
01000           fprintf(stderr,"Unknown option %s\n", k.c_str());
01001           return 1;
01002         }
01003       }
01004       break;
01005     case 'c':
01006       option_string["mode"] = "csv";
01007       break;
01008     case 's':
01009       option_string["mode"] = "sql";
01010       break;
01011     case 'h':
01012       option_bool["help"] = true;
01013       break;
01014     case 't':
01015       option_string["mode"] = "tdiff";
01016       break;
01017     case 'r':
01018       option_string["mode"] = "raw";
01019       break;
01020     case 'v':
01021       option_bool["verbose"] = true;
01022       coopy_set_verbose(true);
01023       break;
01024     case 'e':
01025       option_bool["equals"] = true;
01026       break;
01027     case 'i':
01028       if (!isFormatLike()) {
01029         option_string["mode"] = "index";
01030       }
01031       option_bool["index"] = true;
01032       break;
01033     case 'd':
01034       flags.trust_column_names = true;
01035       break;
01036     case '0':
01037       flags.use_order = false;
01038       break;
01039     case '1':
01040       flags.use_order = true;
01041       break;
01042 
01043     case 'k':
01044       flags.ids.push_back(optarg);
01045       flags.trust_ids = true;
01046       flags.bias_ids = false;
01047       break;
01048 
01049     case 'b':
01050       flags.ids.push_back(optarg);
01051       flags.trust_ids = false;
01052       flags.bias_ids = true;
01053       break;
01054 
01055     case 'm':
01056       {
01057         if (!mapping.read(optarg)) {
01058           fprintf(stderr,"Failed to read %s\n", optarg);
01059           return 1;
01060         }
01061         flags.mapping_book = &mapping;
01062       }
01063       break;
01064 
01065     case 'O':
01066       flags.omit_format_name = true;
01067       break;
01068     case 'P':
01069       flags.omit_sheet_name = true;
01070       break;
01071 
01072     case 'f':
01073       option_string["mode"] = optarg;
01074       break;
01075     case 'o':
01076       option_string["output"] = optarg;
01077       break;
01078     case 'p':
01079       option_string["parent"] = optarg;
01080       break;
01081     case 'V':
01082       option_string["version"] = optarg;
01083       break;
01084     case 'x':
01085       option_bool["cmd"] = true;
01086       addStringToList("cmd",optarg);
01087       break;
01088 
01089     case 'l':
01090       PolyBook::showFormats();
01091       exit(0);
01092       break;
01093     case 'a':
01094       option_bool["apply"] = true;
01095       break;
01096 
01097     case 'F':
01098       flags.fixed_columns = true;
01099       break;
01100 
01101     case 'A':
01102       flags.ignore_case = true;
01103       break;
01104 
01105     case 'T':
01106       flags.tables.insert(optarg);
01107       flags.ordered_tables.push_back(optarg);
01108       break;
01109 
01110     default:
01111       fprintf(stderr, "Unrecognized option\n");
01112       return 1;
01113     }
01114   }
01115 
01116   argc -= optind;
01117   argv += optind;
01118 
01119   for (int i=0; i<argc; i++) {
01120     core.push_back(argv[i]);
01121   }
01122   if (option_bool.find("gen")!=option_bool.end()) {
01123     exit(0);
01124   }
01125   return 0;
01126 }
01127 
01128 
01129 void Options::beginHelp() {
01130 }
01131 
01132 void Options::addUsage(const char *usage) {
01133   usages.push_back(usage);
01134 }
01135 
01136 void Options::addDescription(const char *desc) {
01137   description = desc;
01138 }
01139 
01140 void Options::endHelp() {
01141   addRecipe("_numbers_patch.tdiff","ssdiff --output numbers_patch.tdiff numbers_buggy.csv numbers.csv").require("numbers_buggy.csv").require("numbers.csv");
01142   addRecipe("_numbers_muddle.csv","ssmerge --output numbers_muddle.csv numbers_buggy.csv numbers.csv numbers_conflict.csv").require("numbers_buggy.csv").require("numbers.csv").require("numbers_conflict.csv");
01143   if (option_bool["help-doxygen"]) {
01144     OptionRenderDoxygen render;
01145     render.render(*this);
01146     return;
01147   }
01148   OptionRenderCmdLine render;
01149   render.render(*this);
01150 }
01151 
01152 
01153 
01154 const std::vector<std::string> Options::getExampleReqs() const {
01155   map<string,int> reqs;
01156   for (int i=0; i<(int)examples.size(); i++) {
01157     const Example& eg = examples[i];
01158     for (int j=0; j<(int)eg.reqs.size(); j++) {
01159       string name = eg.reqs[j];
01160       if (name[0]=='_') {
01161         const Recipe& recipe = recipes.find(name)->second;
01162         for (int k=0; k<(int)recipe.reqs.size(); k++) {
01163           reqs[recipe.reqs[k]] = 1;
01164         }
01165       }
01166       reqs[eg.reqs[j]] = 1;
01167     }
01168   }
01169 
01170   vector<string> reqs_flat;
01171   for (map<string,int>::iterator it=reqs.begin(); it!=reqs.end(); it++) {
01172     reqs_flat.push_back(it->first);
01173   }
01174   sort(reqs_flat.begin(),reqs_flat.end());
01175 
01176   return reqs_flat;
01177 }
01178 
01179 
01180 const std::vector<std::string> Options::getExampleRecipes(const std::vector<std::string>& reqs) const {
01181   std::vector<std::string> result;
01182   for (int i=0; i<(int)reqs.size(); i++) {
01183     if (recipes.find(reqs[i])!=recipes.end()) {
01184       result.push_back(recipes.find(reqs[i])->second.code);
01185     }
01186   }
01187   return result;
01188 }
01189 
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines