| #include "SourcePos.h" | 
 | #include "ValuesFile.h" | 
 | #include "XLIFFFile.h" | 
 | #include "Perforce.h" | 
 | #include "merge_res_and_xliff.h" | 
 | #include "localize.h" | 
 | #include "file_utils.h" | 
 | #include "res_check.h" | 
 | #include "xmb.h" | 
 |  | 
 | #include <host/pseudolocalize.h> | 
 |  | 
 | #include <stdlib.h> | 
 | #include <stdarg.h> | 
 | #include <sstream> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <stdlib.h> | 
 |  | 
 | using namespace std; | 
 |  | 
 | FILE* g_logFile = NULL; | 
 |  | 
 | int test(); | 
 |  | 
 | int | 
 | read_settings(const string& filename, map<string,Settings>* result, const string& rootDir) | 
 | { | 
 |     XMLNode* root = NodeHandler::ParseFile(filename, XMLNode::PRETTY); | 
 |     if (root == NULL) { | 
 |         SourcePos(filename, -1).Error("Error reading file."); | 
 |         return 1; | 
 |     } | 
 |  | 
 |     // <configuration> | 
 |     vector<XMLNode*> configNodes = root->GetElementsByName("", "configuration"); | 
 |     const size_t I = configNodes.size(); | 
 |     for (size_t i=0; i<I; i++) { | 
 |         const XMLNode* configNode = configNodes[i]; | 
 |  | 
 |         Settings settings; | 
 |         settings.id = configNode->GetAttribute("", "id", ""); | 
 |         if (settings.id == "") { | 
 |             configNode->Position().Error("<configuration> needs an id attribute."); | 
 |             delete root; | 
 |             return 1; | 
 |         } | 
 |  | 
 |         settings.oldVersion = configNode->GetAttribute("", "old-cl", ""); | 
 |  | 
 |         settings.currentVersion = configNode->GetAttribute("", "new-cl", ""); | 
 |         if (settings.currentVersion == "") { | 
 |             configNode->Position().Error("<configuration> needs a new-cl attribute."); | 
 |             delete root; | 
 |             return 1; | 
 |         } | 
 |  | 
 |         // <app> | 
 |         vector<XMLNode*> appNodes = configNode->GetElementsByName("", "app"); | 
 |  | 
 |         const size_t J = appNodes.size(); | 
 |         for (size_t j=0; j<J; j++) { | 
 |             const XMLNode* appNode = appNodes[j]; | 
 |  | 
 |             string dir = appNode->GetAttribute("", "dir", ""); | 
 |             if (dir == "") { | 
 |                 appNode->Position().Error("<app> needs a dir attribute."); | 
 |                 delete root; | 
 |                 return 1; | 
 |             } | 
 |  | 
 |             settings.apps.push_back(dir); | 
 |         } | 
 |  | 
 |         // <reject> | 
 |         vector<XMLNode*> rejectNodes = configNode->GetElementsByName("", "reject"); | 
 |  | 
 |         const size_t K = rejectNodes.size(); | 
 |         for (size_t k=0; k<K; k++) { | 
 |             const XMLNode* rejectNode = rejectNodes[k]; | 
 |  | 
 |             Reject reject; | 
 |  | 
 |             reject.file = rejectNode->GetAttribute("", "file", ""); | 
 |             if (reject.file == "") { | 
 |                 rejectNode->Position().Error("<reject> needs a file attribute."); | 
 |                 delete root; | 
 |                 return 1; | 
 |             } | 
 |             string f =  reject.file; | 
 |             reject.file = rootDir; | 
 |             reject.file += '/'; | 
 |             reject.file += f; | 
 |              | 
 |             reject.name = rejectNode->GetAttribute("", "name", ""); | 
 |             if (reject.name == "") { | 
 |                 rejectNode->Position().Error("<reject> needs a name attribute."); | 
 |                 delete root; | 
 |                 return 1; | 
 |             } | 
 |  | 
 |             reject.comment = trim_string(rejectNode->CollapseTextContents()); | 
 |  | 
 |             settings.reject.push_back(reject); | 
 |         } | 
 |  | 
 |         (*result)[settings.id] = settings; | 
 |     } | 
 |  | 
 |     delete root; | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | ValuesFile_to_XLIFFFile(const ValuesFile* values, XLIFFFile* xliff, const string& englishFilename) | 
 | { | 
 |     const set<StringResource>& strings = values->GetStrings(); | 
 |     for (set<StringResource>::const_iterator it=strings.begin(); it!=strings.end(); it++) { | 
 |         StringResource res = *it; | 
 |         res.file = englishFilename; | 
 |         xliff->AddStringResource(res); | 
 |     } | 
 | } | 
 |  | 
 | static bool | 
 | contains_reject(const Settings& settings, const string& file, const TransUnit& tu) | 
 | { | 
 |     const string name = tu.id; | 
 |     const vector<Reject>& reject = settings.reject; | 
 |     const size_t I = reject.size(); | 
 |     for (size_t i=0; i<I; i++) { | 
 |         const Reject& r = reject[i]; | 
 |         if (r.file == file && r.name == name) { | 
 |             return true; | 
 |         } | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | /** | 
 |  * If it's been rejected, then we keep whatever info we have. | 
 |  * | 
 |  * Implements this truth table: | 
 |  * | 
 |  *    S   AT   AS     Keep | 
 |  *   ----------------------- | 
 |  *    0    0    0      0    (this case can't happen) | 
 |  *    0    0    1      0    (it was there, never translated, and removed) | 
 |  *    0    1    0      0    (somehow it got translated, but it was removed) | 
 |  *    0    1    1      0    (it was removed after having been translated) | 
 |  * | 
 |  *    1    0    0      1    (it was just added) | 
 |  *    1    0    1      1    (it was added, has been changed, but it never got translated) | 
 |  *    1    1    0      1    (somehow it got translated, but we don't know based on what) | 
 |  *    1    1    1     0/1   (it's in both.  0 if S=AS b/c there's no need to retranslate if they're | 
 |  *                           the same.  1 if S!=AS because S changed, so it should be retranslated) | 
 |  * | 
 |  * The first four are cases where, whatever happened in the past, the string isn't there | 
 |  * now, so it shouldn't be in the XLIFF file. | 
 |  * | 
 |  * For cases 4 and 5, the string has never been translated, so get it translated. | 
 |  * | 
 |  * For case 6, it's unclear where the translated version came from, so we're conservative | 
 |  * and send it back for them to have another shot at. | 
 |  * | 
 |  * For case 7, we have some data.  We have two choices.  We could rely on the translator's | 
 |  * translation memory or tools to notice that the strings haven't changed, and populate the | 
 |  * <target> field themselves.  Or if the string hasn't changed since last time, we can just | 
 |  * not even tell them about it.  As the project nears the end, it will be convenient to see | 
 |  * the xliff files reducing in size, so we pick the latter.  Obviously, if the string has | 
 |  * changed, then we need to get it retranslated. | 
 |  */ | 
 | bool | 
 | keep_this_trans_unit(const string& file, const TransUnit& unit, void* cookie) | 
 | { | 
 |     const Settings* settings = reinterpret_cast<const Settings*>(cookie); | 
 |  | 
 |     if (contains_reject(*settings, file, unit)) { | 
 |         return true; | 
 |     } | 
 |  | 
 |     if (unit.source.id == "") { | 
 |         return false; | 
 |     } | 
 |     if (unit.altTarget.id == "" || unit.altSource.id == "") { | 
 |         return true; | 
 |     } | 
 |     return unit.source.value->ContentsToString(XLIFF_NAMESPACES) | 
 |             != unit.altSource.value->ContentsToString(XLIFF_NAMESPACES); | 
 | } | 
 |  | 
 | int | 
 | validate_config(const string& settingsFile, const map<string,Settings>& settings, | 
 |         const string& config) | 
 | { | 
 |     if (settings.find(config) == settings.end()) { | 
 |         SourcePos(settingsFile, -1).Error("settings file does not contain setting: %s\n", | 
 |                 config.c_str()); | 
 |         return 1; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | int | 
 | validate_configs(const string& settingsFile, const map<string,Settings>& settings, | 
 |         const vector<string>& configs) | 
 | { | 
 |     int err = 0; | 
 |     for (size_t i=0; i<configs.size(); i++) { | 
 |         string config = configs[i]; | 
 |         err |= validate_config(settingsFile, settings, config); | 
 |     } | 
 |     return err; | 
 | } | 
 |  | 
 | int | 
 | select_files(vector<string> *resFiles, const string& config, | 
 |         const map<string,Settings>& settings, const string& rootDir) | 
 | { | 
 |     int err; | 
 |     vector<vector<string> > allResFiles; | 
 |     vector<string> configs; | 
 |     configs.push_back(config); | 
 |     err = select_files(&allResFiles, configs, settings, rootDir); | 
 |     if (err == 0) { | 
 |         *resFiles = allResFiles[0]; | 
 |     } | 
 |     return err; | 
 | } | 
 |  | 
 | int | 
 | select_files(vector<vector<string> > *allResFiles, const vector<string>& configs, | 
 |         const map<string,Settings>& settings, const string& rootDir) | 
 | { | 
 |     int err; | 
 |     printf("Selecting files..."); | 
 |     fflush(stdout); | 
 |  | 
 |     for (size_t i=0; i<configs.size(); i++) { | 
 |         const string& config = configs[i]; | 
 |         const Settings& setting = settings.find(config)->second; | 
 |  | 
 |         vector<string> resFiles; | 
 |         err = Perforce::GetResourceFileNames(setting.currentVersion, rootDir, | 
 |                                                 setting.apps, &resFiles, true); | 
 |         if (err != 0) { | 
 |             fprintf(stderr, "error with perforce.  bailing\n"); | 
 |             return err; | 
 |         } | 
 |  | 
 |         allResFiles->push_back(resFiles); | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | do_export(const string& settingsFile, const string& rootDir, const string& outDir, | 
 |             const string& targetLocale, const vector<string>& configs) | 
 | { | 
 |     bool success = true; | 
 |     int err; | 
 |  | 
 |     if (false) { | 
 |         printf("settingsFile=%s\n", settingsFile.c_str()); | 
 |         printf("rootDir=%s\n", rootDir.c_str()); | 
 |         printf("outDir=%s\n", outDir.c_str()); | 
 |         for (size_t i=0; i<configs.size(); i++) { | 
 |             printf("config[%zd]=%s\n", i, configs[i].c_str()); | 
 |         } | 
 |     } | 
 |  | 
 |     map<string,Settings> settings; | 
 |     err = read_settings(settingsFile, &settings, rootDir); | 
 |     if (err != 0) { | 
 |         return err; | 
 |     } | 
 |  | 
 |     err = validate_configs(settingsFile, settings, configs); | 
 |     if (err != 0) { | 
 |         return err; | 
 |     } | 
 |  | 
 |     vector<vector<string> > allResFiles; | 
 |     err = select_files(&allResFiles, configs, settings, rootDir); | 
 |     if (err != 0) { | 
 |         return err; | 
 |     } | 
 |  | 
 |     size_t totalFileCount = 0; | 
 |     for (size_t i=0; i<allResFiles.size(); i++) { | 
 |         totalFileCount += allResFiles[i].size(); | 
 |     } | 
 |     totalFileCount *= 3; // we try all 3 versions of the file | 
 |  | 
 |     size_t fileProgress = 0; | 
 |     vector<Stats> stats; | 
 |     vector<pair<string,XLIFFFile*> > xliffs; | 
 |  | 
 |     for (size_t i=0; i<configs.size(); i++) { | 
 |         const string& config = configs[i]; | 
 |         const Settings& setting = settings[config]; | 
 |  | 
 |         if (false) { | 
 |             fprintf(stderr, "Configuration: %s (%zd of %zd)\n", config.c_str(), i+1, | 
 |                     configs.size()); | 
 |             fprintf(stderr, "  Old CL:     %s\n", setting.oldVersion.c_str()); | 
 |             fprintf(stderr, "  Current CL: %s\n", setting.currentVersion.c_str()); | 
 |         } | 
 |  | 
 |         Configuration english; | 
 |             english.locale = "en_US"; | 
 |         Configuration translated; | 
 |             translated.locale = targetLocale; | 
 |         XLIFFFile* xliff = XLIFFFile::Create(english, translated, setting.currentVersion); | 
 |  | 
 |         const vector<string>& resFiles = allResFiles[i]; | 
 |         const size_t J = resFiles.size(); | 
 |         for (size_t j=0; j<J; j++) { | 
 |             string resFile = resFiles[j]; | 
 |  | 
 |             // parse the files into a ValuesFile | 
 |             // pull out the strings and add them to the XLIFFFile | 
 |              | 
 |             // current file | 
 |             print_file_status(++fileProgress, totalFileCount); | 
 |             ValuesFile* currentFile = get_values_file(resFile, english, CURRENT_VERSION, | 
 |                                                         setting.currentVersion, true); | 
 |             if (currentFile != NULL) { | 
 |                 ValuesFile_to_XLIFFFile(currentFile, xliff, resFile); | 
 |                 //printf("currentFile=[%s]\n", currentFile->ToString().c_str()); | 
 |             } else { | 
 |                 fprintf(stderr, "error reading file %s@%s\n", resFile.c_str(), | 
 |                             setting.currentVersion.c_str()); | 
 |                 success = false; | 
 |             } | 
 |  | 
 |             // old file | 
 |             print_file_status(++fileProgress, totalFileCount); | 
 |             ValuesFile* oldFile = get_values_file(resFile, english, OLD_VERSION, | 
 |                                                         setting.oldVersion, false); | 
 |             if (oldFile != NULL) { | 
 |                 ValuesFile_to_XLIFFFile(oldFile, xliff, resFile); | 
 |                 //printf("oldFile=[%s]\n", oldFile->ToString().c_str()); | 
 |             } | 
 |  | 
 |             // translated version | 
 |             // (get the head of the tree for the most recent translation, but it's considered | 
 |             // the old one because the "current" one hasn't been made yet, and this goes into | 
 |             // the <alt-trans> tag if necessary | 
 |             print_file_status(++fileProgress, totalFileCount); | 
 |             string transFilename = translated_file_name(resFile, targetLocale); | 
 |             ValuesFile* transFile = get_values_file(transFilename, translated, OLD_VERSION, | 
 |                                                         setting.currentVersion, false); | 
 |             if (transFile != NULL) { | 
 |                 ValuesFile_to_XLIFFFile(transFile, xliff, resFile); | 
 |             } | 
 |  | 
 |             delete currentFile; | 
 |             delete oldFile; | 
 |             delete transFile; | 
 |         } | 
 |  | 
 |         Stats beforeFilterStats = xliff->GetStats(config); | 
 |  | 
 |         // run through the XLIFFFile and strip out TransUnits that have identical | 
 |         // old and current source values and are not in the reject list, or just | 
 |         // old values and no source values | 
 |         xliff->Filter(keep_this_trans_unit, (void*)&setting); | 
 |  | 
 |         Stats afterFilterStats = xliff->GetStats(config); | 
 |         afterFilterStats.totalStrings = beforeFilterStats.totalStrings; | 
 |  | 
 |         // add the reject comments | 
 |         for (vector<Reject>::const_iterator reject = setting.reject.begin(); | 
 |                 reject != setting.reject.end(); reject++) { | 
 |             TransUnit* tu = xliff->EditTransUnit(reject->file, reject->name); | 
 |             tu->rejectComment = reject->comment; | 
 |         } | 
 |  | 
 |         // config-locale-current_cl.xliff | 
 |         stringstream filename; | 
 |         if (outDir != "") { | 
 |             filename << outDir << '/'; | 
 |         } | 
 |         filename << config << '-' << targetLocale << '-' << setting.currentVersion << ".xliff"; | 
 |         xliffs.push_back(pair<string,XLIFFFile*>(filename.str(), xliff)); | 
 |  | 
 |         stats.push_back(afterFilterStats); | 
 |     } | 
 |  | 
 |     // today is a good day to die | 
 |     if (!success || SourcePos::HasErrors()) { | 
 |         return 1; | 
 |     } | 
 |  | 
 |     // write the XLIFF files | 
 |     printf("\nWriting %zd file%s...\n", xliffs.size(), xliffs.size() == 1 ? "" : "s"); | 
 |     for (vector<pair<string,XLIFFFile*> >::iterator it = xliffs.begin(); it != xliffs.end(); it++) { | 
 |         const string& filename = it->first; | 
 |         XLIFFFile* xliff = it->second; | 
 |         string text = xliff->ToString(); | 
 |         write_to_file(filename, text); | 
 |     } | 
 |  | 
 |     // the stats | 
 |     printf("\n" | 
 |            "                                  to          without     total\n" | 
 |            " config               files       translate   comments    strings\n" | 
 |            "-----------------------------------------------------------------------\n"); | 
 |     Stats totals; | 
 |         totals.config = "total"; | 
 |         totals.files = 0; | 
 |         totals.toBeTranslated = 0; | 
 |         totals.noComments = 0; | 
 |         totals.totalStrings = 0; | 
 |     for (vector<Stats>::iterator it=stats.begin(); it!=stats.end(); it++) { | 
 |         string cfg = it->config; | 
 |         if (cfg.length() > 20) { | 
 |             cfg.resize(20); | 
 |         } | 
 |         printf(" %-20s  %-9zd   %-9zd   %-9zd   %-19zd\n", cfg.c_str(), it->files, | 
 |                 it->toBeTranslated, it->noComments, it->totalStrings); | 
 |         totals.files += it->files; | 
 |         totals.toBeTranslated += it->toBeTranslated; | 
 |         totals.noComments += it->noComments; | 
 |         totals.totalStrings += it->totalStrings; | 
 |     } | 
 |     if (stats.size() > 1) { | 
 |         printf("-----------------------------------------------------------------------\n" | 
 |                " %-20s  %-9zd   %-9zd   %-9zd   %-19zd\n", totals.config.c_str(), totals.files, | 
 |                     totals.toBeTranslated, totals.noComments, totals.totalStrings); | 
 |     } | 
 |     printf("\n"); | 
 |     return 0; | 
 | } | 
 |  | 
 | struct PseudolocalizeSettings { | 
 |     XLIFFFile* xliff; | 
 |     bool expand; | 
 | }; | 
 |  | 
 |  | 
 | string | 
 | pseudolocalize_string(const string& source, const PseudolocalizeSettings* settings) | 
 | { | 
 |     return pseudolocalize_string(source); | 
 | } | 
 |  | 
 | static XMLNode* | 
 | pseudolocalize_xml_node(const XMLNode* source, const PseudolocalizeSettings* settings) | 
 | { | 
 |     if (source->Type() == XMLNode::TEXT) { | 
 |         return XMLNode::NewText(source->Position(), pseudolocalize_string(source->Text(), settings), | 
 |                                 source->Pretty()); | 
 |     } else { | 
 |         XMLNode* target; | 
 |         if (source->Namespace() == XLIFF_XMLNS && source->Name() == "g") { | 
 |             // XXX don't translate these | 
 |             target = XMLNode::NewElement(source->Position(), source->Namespace(), | 
 |                                     source->Name(), source->Attributes(), source->Pretty()); | 
 |         } else { | 
 |             target = XMLNode::NewElement(source->Position(), source->Namespace(), | 
 |                                     source->Name(), source->Attributes(), source->Pretty()); | 
 |         } | 
 |  | 
 |         const vector<XMLNode*>& children = source->Children(); | 
 |         const size_t I = children.size(); | 
 |         for (size_t i=0; i<I; i++) { | 
 |             target->EditChildren().push_back(pseudolocalize_xml_node(children[i], settings)); | 
 |         } | 
 |  | 
 |         return target; | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | pseudolocalize_trans_unit(const string&file, TransUnit* unit, void* cookie) | 
 | { | 
 |     const PseudolocalizeSettings* settings = (PseudolocalizeSettings*)cookie; | 
 |  | 
 |     const StringResource& source = unit->source; | 
 |     StringResource* target = &unit->target; | 
 |     *target = source; | 
 |  | 
 |     target->config = settings->xliff->TargetConfig(); | 
 |  | 
 |     delete target->value; | 
 |     target->value = pseudolocalize_xml_node(source.value, settings); | 
 | } | 
 |  | 
 | int | 
 | pseudolocalize_xliff(XLIFFFile* xliff, bool expand) | 
 | { | 
 |     PseudolocalizeSettings settings; | 
 |  | 
 |     settings.xliff = xliff; | 
 |     settings.expand = expand; | 
 |     xliff->Map(pseudolocalize_trans_unit, &settings); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | do_pseudo(const string& infile, const string& outfile, bool expand) | 
 | { | 
 |     int err; | 
 |  | 
 |     XLIFFFile* xliff = XLIFFFile::Parse(infile); | 
 |     if (xliff == NULL) { | 
 |         return 1; | 
 |     } | 
 |  | 
 |     pseudolocalize_xliff(xliff, expand); | 
 |  | 
 |     err = write_to_file(outfile, xliff->ToString()); | 
 |  | 
 |     delete xliff; | 
 |  | 
 |     return err; | 
 | } | 
 |  | 
 | void | 
 | log_printf(const char *fmt, ...) | 
 | { | 
 |     int ret; | 
 |     va_list ap; | 
 |  | 
 |     if (g_logFile != NULL) { | 
 |         va_start(ap, fmt); | 
 |         ret = vfprintf(g_logFile, fmt, ap); | 
 |         va_end(ap); | 
 |         fflush(g_logFile); | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | close_log_file() | 
 | { | 
 |     if (g_logFile != NULL) { | 
 |         fclose(g_logFile); | 
 |     } | 
 | } | 
 |  | 
 | void | 
 | open_log_file(const char* file) | 
 | { | 
 |     g_logFile = fopen(file, "w"); | 
 |     printf("log file: %s -- %p\n", file, g_logFile); | 
 |     atexit(close_log_file); | 
 | } | 
 |  | 
 | static int | 
 | usage() | 
 | { | 
 |     fprintf(stderr, | 
 |             "usage: localize export OPTIONS CONFIGS...\n" | 
 |             "   REQUIRED OPTIONS\n" | 
 |             "     --settings SETTINGS   The settings file to use.  See CONFIGS below.\n" | 
 |             "     --root TREE_ROOT      The location in Perforce of the files.  e.g. //device\n" | 
 |             "     --target LOCALE       The target locale.  See LOCALES below.\n" | 
 |             "\n" | 
 |             "   OPTIONAL OPTIONS\n" | 
 |             "      --out DIR            Directory to put the output files.  Defaults to the\n" | 
 |             "                           current directory if not supplied.  Files are\n" | 
 |             "                           named as follows:\n" | 
 |             "                               CONFIG-LOCALE-CURRENT_CL.xliff\n" | 
 |             "\n" | 
 |             "\n" | 
 |             "usage: localize import XLIFF_FILE...\n" | 
 |             "\n" | 
 |             "Import a translated XLIFF file back into the tree.\n" | 
 |             "\n" | 
 |             "\n" | 
 |             "usage: localize xlb XMB_FILE VALUES_FILES...\n" | 
 |             "\n" | 
 |             "Read resource files from the tree file and write the corresponding XLB file\n" | 
 |             "\n" | 
 |             "Supply all of the android resource files (values files) to export after that.\n" | 
 |             "\n" | 
 |             "\n" | 
 |             "\n" | 
 |             "CONFIGS\n" | 
 |             "\n" | 
 |             "LOCALES\n" | 
 |             "Locales are specified in the form en_US  They will be processed correctly\n" | 
 |             "to locate the resouce files in the tree.\n" | 
 |             "\n" | 
 |             "\n" | 
 |             "usage: localize pseudo OPTIONS INFILE [OUTFILE]\n" | 
 |             "   OPTIONAL OPTIONS\n" | 
 |             "     --big                 Pad strings so they get longer.\n" | 
 |             "\n" | 
 |             "Read INFILE, an XLIFF file, and output a pseudotranslated version of that file.  If\n" | 
 |             "OUTFILE is specified, the results are written there; otherwise, the results are\n" | 
 |             "written back to INFILE.\n" | 
 |             "\n" | 
 |             "\n" | 
 |             "usage: localize rescheck FILES...\n" | 
 |             "\n" | 
 |             "Reads the base strings and prints warnings about bad resources from the given files.\n" | 
 |             "\n"); | 
 |     return 1; | 
 | } | 
 |  | 
 | int | 
 | main(int argc, const char** argv) | 
 | { | 
 |     //open_log_file("log.txt"); | 
 |     //g_logFile = stdout; | 
 |  | 
 |     if (argc == 2 && 0 == strcmp(argv[1], "--test")) { | 
 |         return test(); | 
 |     } | 
 |  | 
 |     if (argc < 2) { | 
 |         return usage(); | 
 |     } | 
 |  | 
 |     int index = 1; | 
 |      | 
 |     if (0 == strcmp("export", argv[index])) { | 
 |         string settingsFile; | 
 |         string rootDir; | 
 |         string outDir; | 
 |         string baseLocale = "en"; | 
 |         string targetLocale; | 
 |         string language, region; | 
 |         vector<string> configs; | 
 |  | 
 |         index++; | 
 |         while (index < argc) { | 
 |             if (0 == strcmp("--settings", argv[index])) { | 
 |                 settingsFile = argv[index+1]; | 
 |                 index += 2; | 
 |             } | 
 |             else if (0 == strcmp("--root", argv[index])) { | 
 |                 rootDir = argv[index+1]; | 
 |                 index += 2; | 
 |             } | 
 |             else if (0 == strcmp("--out", argv[index])) { | 
 |                 outDir = argv[index+1]; | 
 |                 index += 2; | 
 |             } | 
 |             else if (0 == strcmp("--target", argv[index])) { | 
 |                 targetLocale = argv[index+1]; | 
 |                 index += 2; | 
 |             } | 
 |             else if (argv[index][0] == '-') { | 
 |                 fprintf(stderr, "unknown argument %s\n", argv[index]); | 
 |                 return usage(); | 
 |             } | 
 |             else { | 
 |                 break; | 
 |             } | 
 |         } | 
 |         for (; index<argc; index++) { | 
 |             configs.push_back(argv[index]); | 
 |         } | 
 |  | 
 |         if (settingsFile == "" || rootDir == "" || configs.size() == 0 || targetLocale == "") { | 
 |             return usage(); | 
 |         } | 
 |         if (!split_locale(targetLocale, &language, ®ion)) { | 
 |             fprintf(stderr, "illegal --target locale: '%s'\n", targetLocale.c_str()); | 
 |             return usage(); | 
 |         } | 
 |  | 
 |  | 
 |         return do_export(settingsFile, rootDir, outDir, targetLocale, configs); | 
 |     } | 
 |     else if (0 == strcmp("import", argv[index])) { | 
 |         vector<string> xliffFilenames; | 
 |  | 
 |         index++; | 
 |         for (; index<argc; index++) { | 
 |             xliffFilenames.push_back(argv[index]); | 
 |         } | 
 |  | 
 |         return do_merge(xliffFilenames); | 
 |     } | 
 |     else if (0 == strcmp("xlb", argv[index])) { | 
 |         string outfile; | 
 |         vector<string> resFiles; | 
 |  | 
 |         index++; | 
 |         if (argc < index+1) { | 
 |             return usage(); | 
 |         } | 
 |  | 
 |         outfile = argv[index]; | 
 |  | 
 |         index++; | 
 |         for (; index<argc; index++) { | 
 |             resFiles.push_back(argv[index]); | 
 |         } | 
 |  | 
 |         return do_xlb_export(outfile, resFiles); | 
 |     } | 
 |     else if (0 == strcmp("pseudo", argv[index])) { | 
 |         string infile; | 
 |         string outfile; | 
 |         bool big = false; | 
 |  | 
 |         index++; | 
 |         while (index < argc) { | 
 |             if (0 == strcmp("--big", argv[index])) { | 
 |                 big = true; | 
 |                 index += 1; | 
 |             } | 
 |             else if (argv[index][0] == '-') { | 
 |                 fprintf(stderr, "unknown argument %s\n", argv[index]); | 
 |                 return usage(); | 
 |             } | 
 |             else { | 
 |                 break; | 
 |             } | 
 |         } | 
 |  | 
 |         if (index == argc-1) { | 
 |             infile = argv[index]; | 
 |             outfile = argv[index]; | 
 |         } | 
 |         else if (index == argc-2) { | 
 |             infile = argv[index]; | 
 |             outfile = argv[index+1]; | 
 |         } | 
 |         else { | 
 |             fprintf(stderr, "unknown argument %s\n", argv[index]); | 
 |             return usage(); | 
 |         } | 
 |  | 
 |         return do_pseudo(infile, outfile, big); | 
 |     } | 
 |     else if (0 == strcmp("rescheck", argv[index])) { | 
 |         vector<string> files; | 
 |  | 
 |         index++; | 
 |         while (index < argc) { | 
 |             if (argv[index][0] == '-') { | 
 |                 fprintf(stderr, "unknown argument %s\n", argv[index]); | 
 |                 return usage(); | 
 |             } | 
 |             else { | 
 |                 break; | 
 |             } | 
 |         } | 
 |         for (; index<argc; index++) { | 
 |             files.push_back(argv[index]); | 
 |         } | 
 |  | 
 |         if (files.size() == 0) { | 
 |             return usage(); | 
 |         } | 
 |  | 
 |         return do_rescheck(files); | 
 |     } | 
 |     else { | 
 |         return usage(); | 
 |     } | 
 |  | 
 |     if (SourcePos::HasErrors()) { | 
 |         SourcePos::PrintErrors(stderr); | 
 |         return 1; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  |