COOPY » Guide
version 0.6.5
|
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