Home | History | Annotate | Download | only in webkitmerge
      1 /*
      2  * Copyright 2009, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include <map>
     27 #include <string>
     28 #include <vector>
     29 #include <cstdio>
     30 #include <stdlib.h>
     31 #include <string.h>
     32 
     33 using namespace std;
     34 
     35 string WEBKITLIB = "external/webkit";
     36 
     37 string oldBaseStr;
     38 string oldCmdStr;
     39 string newBaseStr;
     40 string newCmdStr;
     41 string sandboxBaseStr;
     42 string sandboxCmdStr;
     43 string outputDir;
     44 string scratchDir;
     45 
     46 const char* oldBase;
     47 const char* oldCmd;
     48 const char* newBase;
     49 const char* newCmd;
     50 const char* sandboxBase;
     51 const char* sandboxCmd;
     52 
     53 bool assert_debug;
     54 
     55 #define myassert(a) do { \
     56     if (!(a)) { \
     57         fprintf(stderr, "%s %d %s\n", __FUNCTION__, __LINE__, #a); \
     58         fflush(stderr); \
     59         if (assert_debug) for(;;); else exit(0); \
     60     } \
     61 } while(false)
     62 
     63 class Options {
     64 public:
     65     Options() : emitGitCommands(false), emitPerforceCommands(false),
     66         mergeMake(true), copyOther(true), mergeCore(true),
     67         removeEmptyDirs(true), removeSVNDirs(true), debug(false),
     68         execute(false), verbose(false), cleared(false)
     69     {
     70     }
     71 
     72     bool finish();
     73     void clearOnce()
     74     {
     75         if (cleared)
     76             return;
     77         mergeMake = copyOther = mergeCore = removeEmptyDirs = removeSVNDirs = false;
     78         cleared = true;
     79     }
     80     string androidWebKit;
     81     string baseWebKit;
     82     string newWebKit;
     83     bool emitGitCommands;
     84     bool emitPerforceCommands;
     85     bool mergeMake;
     86     bool copyOther;
     87     bool mergeCore;
     88     bool removeEmptyDirs;
     89     bool removeSVNDirs;
     90     bool debug;
     91     bool execute;
     92     bool verbose;
     93 private:
     94     bool cleared;
     95 };
     96 
     97 Options options;
     98 
     99 char* GetFile(string fileNameStr, size_t* sizePtr = NULL, int* lines = NULL)
    100 {
    101     const char* fileName = fileNameStr.c_str();
    102     FILE* file = fopen(fileName, "r");
    103     if (file == NULL)
    104     {
    105         fprintf(stderr, "can't read %s\n", fileName);
    106         myassert(0);
    107         return 0;
    108     }
    109     fseek(file, 0, SEEK_END);
    110     size_t size = ftell(file);
    111     if (sizePtr)
    112         *sizePtr = size;
    113     fseek(file, 0, SEEK_SET);
    114     char* buffer = new char[size + 2];
    115     fread(buffer, size, 1, file);
    116     buffer[size] = buffer[size + 1] = '\0';
    117     int lineCount = 0;
    118     for (size_t index = 0; index < size; index++) {
    119         if (buffer[index] == '\n') {
    120             buffer[index] = '\0';
    121             lineCount++;
    122         }
    123     }
    124     if (lines)
    125         *lines = lineCount;
    126     fclose(file);
    127     return buffer;
    128 }
    129 
    130 bool Options::finish()
    131 {
    132     ::assert_debug = options.debug;
    133     if (androidWebKit.size() == 0) {
    134         fprintf(stderr, "missing --android parameter");
    135         return false;
    136     }
    137     if (baseWebKit.size() == 0) {
    138         fprintf(stderr, "missing --basewebkit parameter");
    139         return false;
    140     }
    141     if (newWebKit.size() == 0) {
    142         fprintf(stderr, "missing --newwebkit parameter");
    143         return false;
    144     }
    145     sandboxBaseStr = androidWebKit + "/" + WEBKITLIB;
    146     sandboxCmdStr = sandboxBaseStr;
    147     int err = system("pwd > pwd.txt");
    148     myassert(err != -1);
    149     outputDir = string(GetFile("pwd.txt"));
    150     system("rm pwd.txt");
    151     myassert(outputDir.size() > 0);
    152     scratchDir = outputDir;
    153     outputDir += "/scripts/";
    154     string outputMkdir = "test -d " + outputDir + " || mkdir " + outputDir;
    155     system(outputMkdir.c_str());
    156     scratchDir += "/scratch/";
    157     string scratchMkdir = "test -d " + scratchDir + " || mkdir " + scratchDir;
    158     system(scratchMkdir.c_str());
    159     oldBaseStr = baseWebKit;
    160     oldCmdStr = oldBaseStr;
    161     newBaseStr = newWebKit;
    162     newCmdStr = newBaseStr;
    163     oldBase = oldBaseStr.c_str();
    164     oldCmd = oldCmdStr.c_str();
    165     newBase = newBaseStr.c_str();
    166     newCmd = newCmdStr.c_str();
    167     sandboxBase = sandboxBaseStr.c_str();
    168     sandboxCmd = sandboxCmdStr.c_str();
    169     return true;
    170 }
    171 
    172 // scratch files
    173 string ScratchFile(const char* name)
    174 {
    175     return scratchDir + name + ".txt";
    176 }
    177 
    178 FILE* commandFile;
    179 FILE* copyDirFile;
    180 FILE* oopsFile;
    181 
    182 string SedEscape(const char* str)
    183 {
    184     string result;
    185     char ch;
    186     while ((ch = *str++) != '\0') {
    187         if (ch == '/' || ch == '\\' || ch == '$')
    188             result += '\\';
    189         result += ch;
    190     }
    191     return result;
    192 }
    193 
    194 char* List(const char* base, char* name, const char* workingDir)
    195 {
    196     string listStr = "ls -F \"";
    197     listStr += string(base) + "/" + workingDir + "\" > " + ScratchFile(name);
    198     int err = system(listStr.c_str());
    199     myassert(err == 0);
    200     return GetFile(ScratchFile(name));
    201 }
    202 
    203 bool Merge(const char* oldDir, const char* oldFile, const char* newDir, const char* newFile,
    204     const char* outFile)
    205 {
    206     char scratch[2048];
    207 
    208     sprintf(scratch, "merge -p -q \"%s/%s/%s\" \"%s/%s/%s\"  \"%s/%s/%s\" > %s",
    209             sandboxBase, oldDir, oldFile, oldBase, oldDir, oldFile, newBase, newDir, newFile, outFile);
    210     int err = system(scratch);
    211     myassert(err == 0 || err ==1 || err == 256);
    212     return err == 0;
    213 }
    214 
    215 bool Merge(const char* dir, const char* file)
    216 {
    217     return Merge(dir, file, dir, file, "/dev/null");
    218 }
    219 
    220 /*
    221 static const char* skipNonSpace(char** linePtr) {
    222     char* line = *linePtr;
    223     while (line[0] && isspace(line[0]) == false)
    224         line++;
    225     *linePtr = line;
    226     return line;
    227 }
    228 */
    229 
    230 static bool endsWith(const char* str, const char* end) {
    231 	size_t endLen = strlen(end);
    232 	const char* endStr = str + strlen(str) - endLen;
    233 	return endStr >= str && strcmp(endStr, end) == 0;
    234 }
    235 
    236 static void skipSpace(char** linePtr) {
    237     char* line = *linePtr;
    238     while (isspace(line[0]))
    239         line++;
    240     *linePtr = line;
    241 }
    242 
    243 /*
    244 static void setTrimmed(string& str, char* loc, int len) {
    245     char* start = loc;
    246     skipSpace(&loc);
    247     len -= loc - start;
    248     while (len > 0 && isspace(loc[len - 1]))
    249         len--;
    250     str = string(loc, len);
    251 }
    252 */
    253 
    254 static bool skipText(char** linePtr, const char* text) {
    255     skipSpace(linePtr);
    256     size_t length = strlen(text);
    257     bool result = strncmp(*linePtr, text, length) == 0;
    258     if (result)
    259         *linePtr += length;
    260     skipSpace(linePtr);
    261     return result;
    262 }
    263 
    264 static bool isTokenChar(char ch) {
    265     return isalnum(ch) || ch == '_';
    266 }
    267 
    268 struct Pair {
    269     char* name;
    270     int brace;
    271 };
    272 
    273 class Parse {
    274 public:
    275     char* m_text;
    276     bool m_inComment;
    277     bool m_inFunction;
    278     bool m_inFindFunctionType;
    279     vector<Pair> m_classes;
    280     vector<Pair> m_namespaces;
    281     vector<Pair> m_preprocessorConditions;
    282     string m_functionName;
    283     string m_functionDeclaration;
    284     string m_classDeclaration;
    285     int m_braceLevel;
    286     int m_functionBrace;
    287 
    288     Parse(char* text) : m_text(text), m_inComment(false), m_inFunction(false), m_inFindFunctionType(false),
    289         m_braceLevel(0), m_functionBrace(0) {}
    290 
    291     int CheckForBrace()
    292     {
    293         int indent = 0;
    294         // don't count braces in strings, chars, comments
    295         do {
    296             char* openBrace = strchr(m_text, '{');
    297             char* closeBrace = strchr(m_text, '}');
    298             if (openBrace == NULL && closeBrace == NULL)
    299                 break;
    300             char* brace = openBrace == NULL ? closeBrace : closeBrace == NULL ? openBrace :
    301                 openBrace < closeBrace ? openBrace : closeBrace;
    302             char* doubleQ = strchr(m_text, '"');
    303             char* singleQ = strchr(m_text, '\'');
    304             char* quote = doubleQ == NULL ? singleQ : singleQ == NULL ? doubleQ :
    305                 doubleQ < singleQ ? doubleQ : singleQ;
    306             char quoteMark = quote == doubleQ ? '"' : '\'';
    307             if (quote && quote < brace) {
    308                 myassert(quote[-1] != '\\');
    309                 do {
    310                     quote = strchr(quote + 1, quoteMark);
    311                     myassert(doubleQ);
    312                 } while (quote[-1] == '\\');
    313                 m_text = quote + 1;
    314                 continue;
    315             }
    316             indent += openBrace != NULL ? 1 : -1;
    317             m_text = openBrace + 1;
    318         } while (m_text[0] != '\0');
    319         return indent;
    320     }
    321 
    322 int ParseLine()
    323 {
    324     size_t textLen = strlen(m_text);
    325     char* lineStart = m_text;
    326     char* commentStart;
    327     if (m_text[0] == '\0')
    328         goto nextLine;
    329     if (m_inComment == false && m_text[0] == '/') {
    330         m_inComment = m_text[1] == '*';
    331         if (m_text[1] == '/' || m_text[1] == '*')
    332             goto nextLine;
    333     }
    334     commentStart = m_text; // before anything else, turn embedded comments into their own lines
    335     if (m_inComment == false && skipText(&commentStart, "//")) {
    336         if (commentStart < lineStart + textLen)
    337             commentStart[0] = '/';
    338         else
    339             commentStart[-1] = ' ';
    340         commentStart -= 2;
    341         commentStart[0] = '\0';
    342        textLen = commentStart - lineStart;
    343        goto nextLine;
    344     }
    345     if (m_inComment || skipText(&commentStart, "/*")) {
    346         char* commentEnd = commentStart;
    347         if (skipText(&commentEnd, "*/")) {
    348             if (commentEnd < lineStart + textLen) {
    349                 commentEnd[-1] = '\0';
    350                 textLen = commentEnd - lineStart - 2;
    351             }
    352             if (m_inComment) {
    353                 m_inComment = false;
    354                 goto nextLine;
    355             }
    356         }
    357          if (m_inComment)
    358             goto nextLine;
    359         memcpy(commentStart - 2, "\0/*", 3);
    360         textLen = commentStart - lineStart - 2;
    361    }
    362     if (skipText(&m_text, "#include"))
    363         goto nextLine;
    364     if (skipText(&m_text, "#if") || skipText(&m_text, "#else")) {
    365         Pair condition = { lineStart, m_braceLevel };
    366         m_preprocessorConditions.push_back(condition);
    367         goto nextLine;
    368     }
    369     {
    370         bool is_endif = false;
    371         if (skipText(&m_text, "#elif") || (is_endif = skipText(&m_text, "#endif")) != false) { // pop prior elif, if
    372             char* lastText;
    373             do {
    374                 int last = m_preprocessorConditions.size() - 1;
    375                 myassert(last >= 0);
    376                 lastText = m_preprocessorConditions[last].name;
    377                 m_preprocessorConditions.pop_back();
    378             } while (is_endif && skipText(&lastText, "#else") == true);
    379             goto nextLine;
    380         }
    381     }
    382     if (skipText(&m_text, "namespace")) {
    383         Pair space = { lineStart, m_braceLevel };
    384         m_namespaces.push_back(space);
    385         goto checkForBrace;
    386     }
    387     if (m_inFunction)
    388         goto checkForBrace;
    389     // detect functions by looking for token preceding open-paren.
    390     if (m_inFindFunctionType == false) {
    391         char* openParen = strchr(m_text, '(');
    392         if (openParen) {
    393             char* last = openParen;
    394             while (last > m_text && isspace(last[-1]))
    395                 --last;
    396             while (last > m_text && ((isTokenChar(last[-1]) || last[-1] == ':') && last[-2] == ':'))
    397                 --last;
    398             myassert(isdigit(last[0]) == false);
    399             if (isTokenChar(last[0])) {
    400                 m_inFindFunctionType = true;
    401                 m_functionName = string(last);
    402             }
    403         }
    404     }
    405     {
    406         char* openBrace = strchr(m_text, '{');
    407         char* semiColon = strchr(m_text, ';');
    408         if  (semiColon != NULL && semiColon < openBrace)
    409             openBrace = NULL;
    410     // functions are of the form: type (class::)*function(parameter-list) {
    411         // all of which may be on separate lines
    412         // so keep track of returntype, class, function, paramlist, openbrace separately
    413         if (m_inFindFunctionType == true) { // look ahead to see which comes first, a semicolon or an open brace
    414             if (openBrace) {
    415                 m_functionBrace = ++m_braceLevel;
    416                 m_inFunction = true;
    417                 m_inFindFunctionType = false;
    418                 m_text = openBrace + 1;
    419                 goto checkForBrace;
    420             }
    421             if (semiColon != NULL) { // a function declaration
    422                 m_inFindFunctionType = false;
    423                 m_functionDeclaration = m_functionName;
    424                 m_functionName.erase(0, m_functionName.length());
    425             } else
    426                 goto nextLine;
    427         }
    428         // FIXME what if class line has neither brace nor semi?
    429         if (skipText(&m_text, "class")) {
    430             if (openBrace > m_text) {
    431                 Pair _class = { lineStart, m_braceLevel };
    432                 m_classes.push_back(_class);
    433             } else if (semiColon != NULL) {
    434                 m_classDeclaration = lineStart; // !!! FIXME should have function form as above
    435             }
    436         }
    437     }
    438 checkForBrace:
    439     m_braceLevel += CheckForBrace();
    440     if (m_functionBrace > 0 && m_braceLevel <= m_functionBrace) {
    441         m_functionName.erase(0, m_functionName.length());
    442         m_functionBrace = 0;
    443     }
    444 nextLine:
    445     return textLen;
    446 }
    447 
    448 };
    449 
    450 char* const GetAndroidDiffs(const char* dir, const char* filename)
    451 {
    452     char scratch[2048];
    453     string diffsFile = ScratchFile(__FUNCTION__);
    454     sprintf(scratch, "diff \"%s/%s/%s\"  \"%s/%s/%s\" > %s", sandboxBase, dir,
    455         filename, oldBase, dir, filename, diffsFile.c_str());
    456     int err = system(scratch);
    457     myassert(err == 0 || err == 256);
    458     char* const diffs = GetFile(diffsFile);
    459     return diffs;
    460 }
    461 
    462 void CheckForExec(const char* base1, const char* dir1, const char* file1,
    463     const char* base2, const char* dir2, const char* file2, bool* ex1, bool* ex2)
    464 {
    465     size_t file1Len = strlen(file1);
    466     size_t file2Len = strlen(file2);
    467     bool file1Ex = file1[file1Len - 1] == '*';
    468     bool file2Ex = file2[file2Len - 1] == '*';
    469     if (file1Ex != file2Ex) {
    470         fprintf(stderr, "warning: %s/%s/%s has %sexec bit set while"
    471             " %s/%s/%s has %sexec bit set\n",
    472             base1, dir1, file1, file1Ex ? "" : "no ",
    473             base2, dir2, file2, file2Ex ? "" : "no ");
    474     }
    475     if (ex1) *ex1 = file1Ex;
    476     if (ex2) *ex2 = file2Ex;
    477 }
    478 
    479 bool CompareFiles(const char* base1, const char* dir1, const char* file1,
    480      const char* base2, const char* dir2, const char* file2)
    481 {
    482     char scratch[2048];
    483     bool file1Ex, file2Ex;
    484     string compareFileStr = ScratchFile(__FUNCTION__);
    485     CheckForExec(base1, dir1, file1, base2, dir2, file2, &file1Ex, &file2Ex);
    486     sprintf(scratch, "diff --brief \"%s/%s/%.*s\" \"%s/%s/%.*s\" > %s",
    487         base1, dir1, (int) strlen(file1) - (int) file1Ex, file1,
    488         base2, dir2, (int) strlen(file2) - (int) file2Ex, file2,
    489         compareFileStr.c_str());
    490     int err = system(scratch);
    491     myassert(err == 0 || err == 256);
    492     char* scratchText = GetFile(compareFileStr);
    493     size_t len = strlen(scratchText);
    494     delete[] scratchText;
    495     return len > 0;
    496 }
    497 
    498 bool CompareFiles(const char* base1, const char* base2, const char* dir, const char* file)
    499 {
    500     return CompareFiles(base1, dir, file, base2, dir, file);
    501 }
    502 
    503 int Compare(char* one, size_t len1, char* two, size_t len2)
    504 {
    505     char* o_end = one + len1;
    506     char* t_end = two + len2;
    507     do {
    508         if (one == o_end)
    509             return two == t_end ? 0 : 1;
    510         if (two == t_end)
    511             return -1;
    512         char o = *one++;
    513         char t = *two++;
    514         if (o == t)
    515             continue;
    516         if (o == '/')
    517             return -1;
    518         if (t == '/')
    519             return 1;
    520         return o < t ? -1 : 1;
    521     } while (true);
    522     myassert(0);
    523     return 0;
    524 }
    525 
    526 string Find(const char* oldList)
    527 {
    528     string result;
    529     char scratch[2048];
    530     // look in WebCore and JavaScriptCore
    531     string findWebCore = ScratchFile("FindWebCore");
    532     sprintf(scratch, "cd %s%s ;  find . -name \"%s\" > %s",
    533             newBase, "/WebCore", oldList, findWebCore.c_str());
    534     int err = system(scratch);
    535     myassert(err == 0 || err == 256);
    536     int webCount;
    537     char* foundInWebCore = GetFile(findWebCore, NULL, &webCount);
    538     char* originalFoundInWebCore = foundInWebCore;
    539     string findJavaScriptCore = ScratchFile("FindJavaScriptCore");
    540     sprintf(scratch, "cd %s%s ;  find . -name \"%s\" > %s",
    541             newBase, "/JavaScriptCore", oldList, findJavaScriptCore.c_str());
    542     err = system(scratch);
    543     myassert(err == 0 || err == 256);
    544     int javaScriptCount;
    545     char* foundInJavaScriptCore = GetFile(findJavaScriptCore, NULL, &javaScriptCount);
    546     char* originalFoundInJavaScriptCore = foundInJavaScriptCore;
    547     if (webCount == 1 && javaScriptCount == 0) {
    548         result = "WebCore/" + string(&foundInWebCore[2]);
    549     } else if (webCount == 0 && javaScriptCount == 1) {
    550         result = "JavaScriptCore/" + string(&foundInJavaScriptCore[2]);
    551     } else if (webCount == 1 && javaScriptCount == 1 &&
    552             strncmp(&foundInWebCore[2], "ForwardingHeaders/", 18) == 0) {
    553         result = "JavaScriptCore/" + string(&foundInJavaScriptCore[2]);
    554     } else if (webCount == 1 && javaScriptCount == 1 &&
    555             strncmp(&foundInJavaScriptCore[2], "API/tests/", 10) == 0) {
    556         result = "JavaScriptCore/" + string(&foundInJavaScriptCore[2]);
    557     } else if (webCount + javaScriptCount > 0) {
    558         fprintf(stderr, "deleted file \"%s\" has more than one possible rename:\n", oldList);
    559         int index;
    560         for (index = 0; index < webCount; index++) {
    561             fprintf(stderr, "WebCore/%s\n", &foundInWebCore[2]);
    562             foundInWebCore += strlen(foundInWebCore) + 1;
    563         }
    564         for (index = 0; index < javaScriptCount; index++) {
    565             fprintf(stderr, "JavaScriptCore/%s\n", &foundInJavaScriptCore[2]);
    566             foundInJavaScriptCore += strlen(foundInJavaScriptCore) + 1;
    567         }
    568     }
    569     delete[] originalFoundInWebCore;
    570     delete[] originalFoundInJavaScriptCore;
    571     return result;
    572 }
    573 
    574 char* GetMakeAndExceptions(const char* dir, const char* filename, size_t* makeSize,
    575     string* excludedFilesPtr, string* excludedGeneratedPtr,
    576     string* excludedDirsPtr, string* androidFilesPtr,
    577     char** startPtr, char** localStartPtr)
    578 {
    579     char scratch[1024];
    580     sprintf(scratch, "%s/%s/%s", sandboxBase, dir, filename);
    581     char* makeFile = GetFile(scratch, makeSize);
    582     char* start = makeFile;
    583     do { // find first filename in makefile
    584         if (strncmp(start, "# LOCAL_SRC_FILES_EXCLUDED := \\", 30) == 0)
    585             break;
    586         start += strlen(start) + 1;
    587     } while (start < makeFile + *makeSize);
    588     myassert(start[0] != '\0');
    589     start += strlen(start) + 1;
    590     // construct one very large regular expression that looks like:
    591     // echo '%s' | grep -v -E 'DerivedSources.cpp|WebCorePrefix.cpp...'
    592     // to filter out matches that aren't allowed
    593     // wildcards '*' in the original need to be expanded to '.*'
    594     string excludedFiles = "grep -v -E '";
    595     while (strncmp(start, "#\t", 2) == 0) {
    596         start += 2;
    597         char ch;
    598         while ((ch = *start++) != ' ') {
    599             if (ch == '*')
    600                 excludedFiles += '.';
    601             else if (ch == '.')
    602                 excludedFiles += '\\';
    603             excludedFiles += ch;
    604         }
    605         excludedFiles += "|";
    606         myassert(*start == '\\');
    607         start += 2;
    608     }
    609     excludedFiles[excludedFiles.size() - 1] = '\'';
    610     *excludedFilesPtr = excludedFiles;
    611     do {
    612         if (strncmp(start, "# LOCAL_GENERATED_FILES_EXCLUDED := \\", 37) == 0 ||
    613                 strncmp(start, "# LOCAL_DIR_WILDCARD_EXCLUDED := \\", 34) == 0)
    614             break;
    615         start += strlen(start) + 1;
    616     } while (start < makeFile + *makeSize);
    617     if (strncmp(start, "# LOCAL_GENERATED_FILES_EXCLUDED := \\", 37) == 0) {
    618         string excludedGenerated = "grep -v -E '";
    619         start += strlen(start) + 1;
    620         while (strncmp(start, "#\t", 2) == 0) {
    621             start += 2;
    622             char ch;
    623             while ((ch = *start++) != ' ') {
    624                 if (ch == '*')
    625                     excludedGenerated += '.';
    626                 else if (ch == '.')
    627                     excludedGenerated += '\\';
    628                 excludedGenerated += ch;
    629             }
    630             excludedGenerated += "|";
    631             myassert(*start == '\\');
    632             start += 2;
    633         }
    634         myassert(excludedGeneratedPtr);
    635         excludedGenerated[excludedGenerated.size() - 1] = '\'';
    636         *excludedGeneratedPtr = excludedGenerated;
    637     }
    638     do { // find first filename in makefile
    639         if (strncmp(start, "# LOCAL_DIR_WILDCARD_EXCLUDED := \\", 34) == 0)
    640             break;
    641         start += strlen(start) + 1;
    642     } while (start < makeFile + *makeSize);
    643     if (start[0] != '\0') {
    644         string excludedDirs = "-e '/\\.vcproj\\// d' -e '/\\.svn\\// d' ";
    645         do {
    646             start += strlen(start) + 1;
    647             char* exceptionDirStart = start;
    648             if (strncmp(exceptionDirStart, "#\t", 2) != 0) {
    649                 myassert(exceptionDirStart[0] == '\0');
    650                 break;
    651             }
    652             exceptionDirStart += 2;
    653             char* exceptionDirEnd = exceptionDirStart;
    654             do {
    655                 exceptionDirEnd = strchr(exceptionDirEnd, '\\');
    656             } while (exceptionDirEnd && *++exceptionDirEnd == '/');
    657             myassert(exceptionDirEnd);
    658             --exceptionDirEnd;
    659             myassert(exceptionDirEnd[-1] == ' ');
    660             myassert(exceptionDirEnd[-2] == '*');
    661             myassert(exceptionDirEnd[-3] == '/');
    662             exceptionDirEnd[-3] = '\0';
    663             excludedDirs += "-e '/";
    664             if (exceptionDirStart[0] == '/')
    665                 excludedDirs += "\\";
    666             excludedDirs += exceptionDirStart;
    667             excludedDirs += "\\// d' ";
    668             start = exceptionDirEnd;
    669         } while (true);
    670         *excludedDirsPtr = excludedDirs;
    671     }
    672     *startPtr = start;
    673     // optionally look for android-specific files
    674     char* makeEnd = makeFile + *makeSize;
    675     do { // find first filename in makefile
    676         if (strcmp(start, "# LOCAL_ANDROID_SRC_FILES_INCLUDED := \\") == 0)
    677             break;
    678     } while ((start += strlen(start) + 1), start < makeEnd);
    679     if (start >= makeEnd)
    680         return makeFile;
    681     start += strlen(start) + 1;
    682     string androidFiles = "grep -v -E '";
    683     do {
    684         myassert(strncmp(start, "#\t", 2) == 0);
    685         start += 2;
    686         char ch;
    687         bool isIdl = strstr(start, "idl \\") != 0;
    688         char* lastSlash = strrchr(start, '/') + 1;
    689         while ((ch = *start++) != ' ') {
    690             if (ch == '*')
    691                 androidFiles += '.';
    692             else if (ch == '.')
    693                 androidFiles += '\\';
    694             androidFiles += ch;
    695             if (!isIdl)
    696                 continue;
    697             if (ch == '/' && start == lastSlash)
    698                 androidFiles += "JS";
    699             if (ch == '.') {
    700                 myassert(strcmp(start, "idl \\") == 0);
    701                 start += 4;
    702                 androidFiles += 'h';
    703                 break;
    704             }
    705         }
    706         androidFiles += "|";
    707         myassert(*start == '\\');
    708         start += 2;
    709     } while (start[0] == '#');
    710     androidFiles[androidFiles.size() - 1] = '\'';
    711     *androidFilesPtr = androidFiles;
    712     return makeFile;
    713 }
    714 
    715 vector<char*> GetDerivedSourcesMake(const char* dir)
    716 {
    717     vector<char*> result;
    718     char scratch[1024];
    719     sprintf(scratch, "%s/%s/%s", newBase, dir, "DerivedSources.make");
    720     size_t fileSize;
    721     char* file = GetFile(scratch, &fileSize);
    722     char* fileEnd = file + fileSize;
    723     myassert(file);
    724     size_t len;
    725     do { // find first filename in makefile
    726         len = strlen(file);
    727         if (strcmp(file, "all : \\") == 0)
    728             break;
    729         file += len + 1;
    730     } while (file < fileEnd);
    731     myassert(strcmp(file, "all : \\") == 0);
    732     file += len + 1;
    733     while (file[0] != '#') {
    734         len = strlen(file);
    735         char* st = file;
    736         skipSpace(&st);
    737         if (st[0] != '\\' && st[0] != '$')
    738             result.push_back(st);
    739         file += len + 1;
    740     }
    741     return result;
    742 }
    743 
    744 bool MarkDerivedFound(vector<char*>& derived, char* check, size_t len)
    745 {
    746     bool found = false;
    747     for (unsigned index = 0; index < derived.size(); index++) {
    748         char* der = derived[index];
    749         if (strncmp(der, check, len) == 0) {
    750             der[0] = '\0';
    751             found = true;
    752         }
    753     }
    754     return found;
    755 }
    756 
    757 void UpdateDerivedMake()
    758 {
    759     const char* dir = "WebCore";
    760     int err;
    761     vector<char*> derived = GetDerivedSourcesMake(dir);
    762     size_t makeSize;
    763     char* start, * localStart;
    764     string excludedDirs, excludedFiles, excludedGenerated, androidFiles;
    765     char* makeFile = GetMakeAndExceptions(dir, "Android.derived.mk", &makeSize,
    766         &excludedFiles, &excludedGenerated, &excludedDirs, &androidFiles,
    767         &start, &localStart);
    768     if (options.emitPerforceCommands)
    769         fprintf(commandFile, "p4 edit %s/%s/%s\n", sandboxCmd, dir, "Android.derived.mk");
    770     fprintf(commandFile, "cat %s/%s/%s | sed \\\n", sandboxCmd, dir, "Android.derived.mk");
    771     string updateDerivedMake = ScratchFile(__FUNCTION__);
    772     string filelist = string("cd ") + newBase + "/" + dir +
    773         " ;  find . -name '*.idl' | " + excludedFiles +
    774         " | sed -e 's/.\\///' " + excludedDirs +
    775         " | sed 's@\\(.*\\)/\\(.*\\)\\.idl@    $(intermediates)/\\1/JS\\2.h@' "
    776         " | sort -o " + updateDerivedMake;
    777     err = system(filelist.c_str());
    778     myassert(err == 0 || err == 256);
    779     char* bindings = GetFile(updateDerivedMake);
    780     bool inGen = false;
    781     char* fileEnd = makeFile + makeSize;
    782     char* nextStart;
    783     do {
    784         size_t startLen = strlen(start);
    785         nextStart = start + startLen + 1;
    786         bool onGen = false;
    787         char* st = start;
    788         if (inGen == false) {
    789             if (strncmp(st, "GEN", 3) != 0)
    790                 continue;
    791             st += 3;
    792             skipSpace(&st);
    793             if (strncmp(st, ":=", 2) != 0)
    794                 continue;
    795             st += 2;
    796             onGen = true;
    797         }
    798         skipSpace(&st);
    799         inGen = start[startLen - 1] == '\\';
    800         if (inGen) {
    801             if (st[0] == '\\' && st[1] == '\0')
    802                 continue;
    803             if (strcmp(st, "$(addprefix $(intermediates)/, \\") == 0)
    804                 continue;
    805         } else if (st[0] == ')' && st[1] == '\0')
    806             continue;
    807         static const char bindHead[] = "bindings/js/";
    808         const size_t bindLen = sizeof(bindHead) - 1;
    809         string escaped;
    810         if (strncmp(st, bindHead, bindLen) == 0) {
    811             st += bindLen;
    812             if (MarkDerivedFound(derived, st, strlen(st)) == false) {
    813                 fprintf(stderr, "*** webkit removed js binding: %s"
    814                     " (must be removed manually)\n", st);
    815                 escaped = SedEscape(st);
    816                 fprintf(commandFile, "-e '/%s/ d' ", escaped.c_str());
    817             }
    818             continue;
    819         }
    820         myassert(strncmp(st, "$(intermediates)", 16) == 0);
    821         if (onGen && inGen == false) { // no continuation
    822             char* lastSlash;
    823             myassert(strrchr(st, '/') != NULL);
    824             while ((lastSlash = strrchr(st, '/')) != NULL) {
    825                 char* lastEnd = strchr(lastSlash, ' ');
    826                 if (lastEnd == NULL)
    827                     lastEnd = lastSlash + strlen(lastSlash);
    828                 myassert(lastSlash != 0);
    829                 lastSlash++;
    830                 size_t lastLen = lastEnd - lastSlash;
    831                 if (MarkDerivedFound(derived, lastSlash, lastLen) == false) {
    832                     fprintf(stderr, "*** webkit removed generated file:"
    833                         " %.*s (must be removed manually)\n", (int) lastLen,
    834                         lastSlash);
    835               //      escaped = SedEscape(st);
    836               //      fprintf(commandFile, "-e '/%s/ d' \\\n", escaped.c_str());
    837                 }
    838                 char* priorDollar = strrchr(st, '$');
    839                 myassert(priorDollar != NULL);
    840                 char* nextDollar = strchr(st, '$');
    841                 if (nextDollar == priorDollar)
    842                     break;
    843                 priorDollar[0] = '\0';
    844             }
    845             continue;
    846         }
    847         char* nextSt = nextStart;
    848         skipSpace(&nextSt);
    849         myassert(strncmp(nextSt, "$(intermediates)", 16) != 0 || strcmp(st, nextSt) < 0);
    850    //     do {
    851         char* bind = bindings;
    852         myassert(bind);
    853         skipSpace(&bind);
    854         int compare = strncmp(bind, st, strlen(bind) - 2);
    855         if (compare < 0) { // add a file
    856             escaped = SedEscape(st);
    857             char* filename = strrchr(bindings, '/');
    858             myassert(filename);
    859             filename += 3;
    860             // FIX ME: exclude items in DerivedSources.make all : $(filter-out ...
    861             char* bi = bindings;
    862             skipSpace(&bi);
    863             char* bindName = strrchr(bi, '/');
    864             myassert(bindName != NULL);
    865             bindName++;
    866             string biStr = SedEscape(bi);
    867             fprintf(commandFile, "-e '/%s/ i\\\n_TAB_%s \\\\\n' ",
    868                 escaped.c_str(), biStr.c_str());
    869             MarkDerivedFound(derived, bindName, strlen(bindName));
    870             nextStart = start;
    871             bindings += strlen(bindings) + 1;
    872             inGen = true;
    873         } else if (compare > 0) {
    874             // if file to be deleted is locally added by android, leave it alone
    875             myassert(strncmp(st, "$(intermediates)/", 17) == 0);
    876             string subst = string(st).substr(17, strlen(st) - 17 - (inGen ? 2 : 0));
    877             string localDerivedStr = ScratchFile("LocalDerived");
    878             string filter = string("echo '") + subst + "' | " + androidFiles +
    879                 " > " + localDerivedStr;
    880             if (options.debug)
    881                 fprintf(stderr, "LocalDerived.txt : %s\n", filter.c_str());
    882             err = system(filter.c_str());
    883             myassert(err == 0 || err == 256);
    884             char* localDerived = GetFile(localDerivedStr);
    885             if (localDerived[0] != '\0') {
    886                 escaped = SedEscape(st);
    887                 fprintf(commandFile, "-e '/%s/ d' ", escaped.c_str());
    888             }
    889         } else {
    890             char* stName = strrchr(st, '/');
    891             myassert(stName);
    892             stName++;
    893             MarkDerivedFound(derived, stName, strlen(stName));
    894             bindings += strlen(bindings) + 1;
    895         }
    896      //   } while (strstr(start, "$(intermediates)") != NULL);
    897         // if changing directories, add any new files to the end of this directory first
    898         if (bindings[0] != '\0' && strstr(nextStart, "$(intermediates)") == NULL) {
    899             st = start;
    900             skipSpace(&st);
    901             escaped = SedEscape(st);
    902             st = strchr(st, '/');
    903             char* stDirEnd = strchr(st + 1, '/');
    904             do {
    905                 bind = strchr(bindings, '/');
    906                 if (!bind)
    907                     break;
    908                 char* bindEnd = strchr(bind + 1, '/');
    909                 if (!bindEnd)
    910                     break;
    911                 if (bindEnd - bind != stDirEnd - st)
    912                     break;
    913                 if (strncmp(st, bind, stDirEnd - st) != 0)
    914                     break;
    915                 if (inGen == false)
    916                     fprintf(commandFile, "-e '/%s/ s/$/ \\\\/' ", escaped.c_str());
    917                 char* bi = bindings;
    918                 skipSpace(&bi);
    919                 string biStr = SedEscape(bi);
    920                 fprintf(commandFile, "-e '/%s/ a\\\n_TAB_%s\n' ",
    921                     escaped.c_str(), biStr.c_str());
    922                 MarkDerivedFound(derived, bindEnd + 1, strlen(bindEnd + 1));
    923                 escaped = biStr;
    924                 inGen = false;
    925                 bindings += strlen(bindings) + 1;
    926             } while (true);
    927         }
    928     } while (start = nextStart, start < fileEnd);
    929     for (unsigned index = 0; index < derived.size(); index++) {
    930         char* der = derived[index];
    931         if (der[0] == '\0')
    932             continue;
    933         string excludedGeneratedStr = ScratchFile("ExcludedGenerated");
    934         string filter = string("echo '") + der + "' | " + excludedGenerated +
    935             " > " + excludedGeneratedStr;
    936         err = system(filter.c_str());
    937         myassert(err == 0 || err == 256);
    938         char* excluded = GetFile(excludedGeneratedStr);
    939         if (excluded[0] != '\0')
    940             fprintf(stderr, "*** missing rule to generate %s\n", der);
    941     }
    942     fprintf(commandFile, " | sed 's/^_TAB_/\t/' > %s/%s/%s\n", sandboxCmd, dir, "xAndroid.derived.mk");
    943     fprintf(commandFile, "mv  %s/%s/%s %s/%s/%s\n",
    944         sandboxCmd, dir, "xAndroid.derived.mk", sandboxCmd, dir, "Android.derived.mk");
    945     if (options.emitGitCommands)
    946         fprintf(commandFile, "git add %s/%s\n", dir, "Android.derived.mk");
    947 }
    948 
    949 int MatchLen(const char* one, const char* two, size_t len)
    950 {
    951     bool svgIn1 = strstr(one, "svg") || strstr(one, "SVG");
    952     bool svgIn2 = strstr(two, "svg") || strstr(two, "SVG");
    953     if (svgIn1 != svgIn2)
    954         return 0;
    955     int signedLen = (int) len;
    956     int original = signedLen;
    957     while (*one++ == *two++ && --signedLen >= 0)
    958         ;
    959     return original - signedLen;
    960 }
    961 
    962 // create the list of sed commands to update the WebCore Make file
    963 void UpdateMake(const char* dir)
    964 {
    965     // read in the makefile
    966     size_t makeSize;
    967     char* start, * localStart = NULL;
    968     string excludedDirs, excludedFiles, androidFiles;
    969     char* makeFile = GetMakeAndExceptions(dir, "Android.mk", &makeSize,
    970         &excludedFiles, NULL, &excludedDirs, &androidFiles, &start, &localStart);
    971     char* lastFileName = NULL;
    972     size_t lastFileNameLen = 0;
    973     int lastLineNumber = -1;
    974     // get the actual list of files
    975     string updateMakeStr = ScratchFile(__FUNCTION__);
    976     string filelist = string("cd ") + newBase + "/" + dir + " ;"
    977         "  find . -name '*.cpp' -or -name '*.c' -or -name '*.y' | " +
    978         excludedFiles + " |  sed -e 's/.\\///' " + excludedDirs +
    979         " | sort -o " + updateMakeStr;
    980     if (options.debug)
    981         fprintf(stderr, "make %s/%s filter: %s\n", dir, "Android.mk", filelist.c_str());
    982     int err = system(filelist.c_str());
    983     myassert(err == 0 || err == 256);
    984     char* newList = GetFile(updateMakeStr);
    985     do { // find first filename in makefile
    986         if (strncmp(start, "LOCAL_SRC_FILES := \\", 20) == 0)
    987             break;
    988         start += strlen(start) + 1;
    989     } while (start < makeFile + makeSize);
    990     myassert(start[0] != '\0');
    991     if (options.emitPerforceCommands)
    992         fprintf(commandFile, "p4 edit %s/%s/%s\n", sandboxCmd, dir, "Android.mk");
    993     fprintf(commandFile, "cat %s/%s/%s | sed ", sandboxCmd, dir, "Android.mk");
    994     int lineNumber = 0;
    995     do {
    996         start += strlen(start) + 1;
    997         lineNumber++;
    998         if (start - makeFile >= makeSize || start[0] == '$')
    999             break;
   1000         if (start[0] == '\0' || !isspace(start[0]))
   1001             continue;
   1002         skipSpace(&start);
   1003         if (start[0] == '\0' || start[0] == '\\')
   1004             continue;
   1005         size_t startLen = strlen(start);
   1006         if (start[startLen - 1] == '\\')
   1007             --startLen;
   1008         while (isspace(start[startLen - 1]))
   1009             --startLen;
   1010         size_t newListLen = strlen(newList);
   1011         if (lastFileName != NULL) {
   1012             myassert(strncmp(start, lastFileName, startLen) > 0 ||
   1013                 startLen > lastFileNameLen);
   1014         }
   1015         if (strstr(start, "android") != NULL || strstr(start, "Android") != NULL) {
   1016             if (startLen == newListLen && strncmp(newList, start, startLen) == 0)
   1017                 newList += newListLen + 1;
   1018             lastFileName = start;
   1019             lastFileNameLen = startLen;
   1020             lastLineNumber = lineNumber;
   1021             continue;
   1022         }
   1023         int compare;
   1024         bool backslash = lastFileName &&
   1025             lastFileName[strlen(lastFileName) - 1] == '\\';
   1026         do {
   1027             compare = strncmp(newList, start, startLen);
   1028             if (compare == 0 && startLen != newListLen)
   1029                 compare = newListLen < startLen ? -1 : 1;
   1030             if (newList[0] == '\0' || compare >= 0)
   1031                 break;
   1032             // add a file
   1033             if (lastFileName && lineNumber - lastLineNumber > 1 &&
   1034                     MatchLen(lastFileName, newList, lastFileNameLen) >
   1035                     MatchLen(start, newList, startLen)) {
   1036                 string escaped = SedEscape(lastFileName);
   1037                 if (!backslash)
   1038                     fprintf(commandFile, "-e '/%s/ s/$/ \\\\/' ", escaped.c_str());
   1039                 fprintf(commandFile, "-e '/%s/ a\\\n_TAB_%s%s\n' ",
   1040                     SedEscape(lastFileName).c_str(), newList,
   1041                     backslash ? " \\\\" : "");
   1042                 lastFileName = newList;
   1043                 lastFileNameLen = newListLen;
   1044             } else {
   1045                 fprintf(commandFile, "-e '/%s/ i\\\n_TAB_%s \\\\\n' ",
   1046                     SedEscape(start).c_str(), newList);
   1047             }
   1048             newList += newListLen + 1;
   1049             newListLen = strlen(newList);
   1050         } while (true);
   1051         if (newList[0] == '\0' || compare > 0) {
   1052             // don't delete files added by Android
   1053             string localMakeStr = ScratchFile("LocalMake");
   1054             string filter = "echo '" + string(start).substr(0, startLen) +
   1055                 "' | " + androidFiles + " > " + localMakeStr;
   1056             int err = system(filter.c_str());
   1057             myassert(err == 0 || err == 256);
   1058             char* localMake = GetFile(localMakeStr);
   1059             if (localMake[0] != '\0') {
   1060                 string escaped = SedEscape(start);
   1061                 fprintf(commandFile, "-e '/%s/ d' ", escaped.c_str());
   1062             }
   1063         } else
   1064             newList += newListLen + 1;
   1065         lastFileName = start;
   1066         lastFileNameLen = startLen;
   1067         lastLineNumber = lineNumber;
   1068     } while (true);
   1069     fprintf(commandFile, " | sed 's/^_TAB_/\t/' > %s/%s/%s\n", sandboxCmd, dir, "xAndroid.mk");
   1070     fprintf(commandFile, "mv  %s/%s/%s %s/%s/%s\n",
   1071         sandboxCmd, dir, "xAndroid.mk", sandboxCmd, dir, "Android.mk");
   1072     if (options.emitGitCommands)
   1073         fprintf(commandFile, "git add %s/%s\n", dir, "Android.mk");
   1074 }
   1075 
   1076 static bool emptyDirectory(const char* base, const char* work, const char* dir) {
   1077     string findEmptyStr = "find \"";
   1078     string emptyDirStr = ScratchFile("emptyDirectory");
   1079     if (base[0] != '\0')
   1080         findEmptyStr += string(base) + "/" + work + "/" + dir + "\"";
   1081     else
   1082         findEmptyStr += string(work) + "/" + dir + "\"";
   1083     findEmptyStr += " -type f -print > " + emptyDirStr;
   1084     int err = system(findEmptyStr.c_str());
   1085     if (err != 0)
   1086         return true;
   1087     FILE* file = fopen(emptyDirStr.c_str(), "r");
   1088     if (file == NULL)
   1089     {
   1090         fprintf(stderr, "can't read %s\n", emptyDirStr.c_str());
   1091         myassert(0);
   1092         return true;
   1093     }
   1094     fseek(file, 0, SEEK_END);
   1095     size_t size = ftell(file);
   1096     fclose(file);
   1097     return size == 0;
   1098 }
   1099 
   1100 static bool emptyDirectory(const char* work, const char* dir) {
   1101     return emptyDirectory("", work, dir);
   1102 }
   1103 
   1104 void CompareDirs(const char* workingDir, bool renamePass)
   1105 {
   1106     map<string, string> renameMap;
   1107     char* oldList = List(oldBase, "old", workingDir);
   1108     char* newList = List(newBase, "new", workingDir);
   1109     char* sandList = List(sandboxBase, "sandbox", workingDir);
   1110     char* oldMem = oldList;
   1111     char* newMem = newList;
   1112     char* sandboxMem = sandList;
   1113     // identify files to be added, removed by comparing old, new lists
   1114     do {
   1115         size_t oldLen = strlen(oldList);
   1116         size_t newLen = strlen(newList);
   1117         size_t sandLen = strlen(sandList);
   1118         if (oldLen == 0 && newLen == 0 && sandLen == 0)
   1119             break;
   1120         bool oldDir = false;
   1121         bool oldExecutable = false;
   1122         if (oldLen > 0) {
   1123             char last = oldList[oldLen - 1];
   1124             oldDir = last ==  '/';
   1125             oldExecutable = last ==  '*';
   1126             if (oldDir || oldExecutable)
   1127                 --oldLen;
   1128         }
   1129         bool newDir = false;
   1130         bool newExecutable = false;
   1131         if (newLen > 0) {
   1132             char last = newList[newLen - 1];
   1133             newDir = last ==  '/';
   1134             newExecutable = last ==  '*';
   1135             if (newDir || newExecutable)
   1136                 --newLen;
   1137         }
   1138         bool sandDir = false;
   1139         bool sandExecutable = false;
   1140         if (sandLen > 0) {
   1141             char last = sandList[sandLen - 1];
   1142             sandDir = last ==  '/';
   1143             sandExecutable = last ==  '*';
   1144             if (sandDir || sandExecutable)
   1145                 --sandLen;
   1146         }
   1147         string oldFileStr = string(oldList).substr(0, oldLen);
   1148         const char* oldFile = oldFileStr.c_str();
   1149         string newFileStr = string(newList).substr(0, newLen);
   1150         const char* newFile = newFileStr.c_str();
   1151         string sandFileStr = string(sandList).substr(0, sandLen);
   1152         const char* sandFile = sandFileStr.c_str();
   1153         int order = Compare(oldList, oldLen, newList, newLen);
   1154         int sandOrder = 0;
   1155         if ((oldLen > 0 || sandLen > 0) && order <= 0) {
   1156             sandOrder = Compare(sandList, sandLen, oldList, oldLen);
   1157             if (sandOrder > 0 && renamePass == false)
   1158                 fprintf(stderr, "error: file in old webkit missing from sandbox: %s/%s\n",
   1159                         workingDir, oldFile);
   1160             if (sandOrder < 0 && renamePass == false) {
   1161                 // file added by android -- should always have name 'android?'
   1162                 const char* android = strstr(sandFile, "ndroid");
   1163                 if (android == NULL)
   1164                     fprintf(stderr, "warning: expect added %s to contain 'android': %s/%s\n",
   1165                             sandDir ? "directory" : "file" , workingDir, sandFile);
   1166             }
   1167             if (sandOrder == 0) {
   1168                 myassert(oldDir == sandDir);
   1169                 if (oldExecutable != sandExecutable)
   1170                     CheckForExec(oldBase, workingDir, oldList,
   1171                         sandboxBase, workingDir, sandList, 0, 0);
   1172             }
   1173             if (sandOrder <= 0)
   1174                 sandList += strlen(sandList) + 1;
   1175             if (sandOrder < 0)
   1176                 continue;
   1177         }
   1178         if (order < 0) { // file in old list is not in new
   1179             // check to see if file is read only ; if so, call p4 delete -- otherwise, just call delete
   1180             if (oldDir == false) {
   1181                 bool modifiedFile = false;
   1182                 // check to see if android modified deleted file
   1183                 if (sandOrder == 0) {
   1184                     string rename(workingDir);
   1185                     rename.append("/");
   1186                     rename.append(oldFile);
   1187                     if (renamePass) {
   1188                         string newName = Find(oldFile);
   1189                         if (newName.length() > 0) {
   1190                             map<string, string>::iterator iter = renameMap.find(rename);
   1191                             myassert(iter == renameMap.end()); // if I see the same file twice, must be a bug
   1192                             renameMap[rename] = newName;
   1193                             myassert(rename != newName);
   1194                             if (options.debug)
   1195                                 fprintf(stderr, "map %s to %s\n", rename.c_str(), newName.c_str());
   1196                         }
   1197                     }
   1198                     if (renamePass == false) {
   1199                         bool oldSandboxDiff = CompareFiles(oldBase, sandboxBase, workingDir, oldList);
   1200                         const char* renamedDir = workingDir;
   1201                         map<string, string>::iterator iter = renameMap.find(rename);
   1202                         if (iter != renameMap.end()) {
   1203                             string newName = renameMap[rename];
   1204                             renamedDir = newName.c_str();
   1205                             char* renamed = (char*) strrchr(renamedDir, '/');
   1206                             *renamed++ = '\0'; // splits rename into two strings
   1207                             if (options.emitPerforceCommands) {
   1208                                 fprintf(commandFile, "p4 integrate \"%s/%s/%s\" \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile,
   1209                                     sandboxCmd, renamedDir, renamed);
   1210                                 fprintf(commandFile, "p4 resolve \"%s/%s/%s\"\n", sandboxCmd, renamedDir, renamed);
   1211                             } else if (options.emitGitCommands) {
   1212                                 fprintf(commandFile, "git mv \"%s/%s\" \"%s/%s\"\n", workingDir, oldFile,
   1213                                     renamedDir, renamed);
   1214                             }
   1215                             if (oldSandboxDiff) {
   1216                                 if (options.emitPerforceCommands)
   1217                                     fprintf(commandFile, "p4 open \"%s/%s/%s\"\n", sandboxCmd, renamedDir, renamed);
   1218                                 fprintf(commandFile,  "merge -q \"%s/%s/%s\" \"%s/%s/%s\" \"%s/%s/%s\"\n",
   1219                                     sandboxCmd, renamedDir, renamed, oldCmd, workingDir, oldFile, newCmd, renamedDir, renamed);
   1220                                 bool success = Merge(workingDir, oldFile, renamedDir, renamed, "/dev/null");
   1221                                 if (success == false) {
   1222 									fprintf(stderr, "*** Manual merge required: %s/%s\n", renamedDir, renamed);
   1223 									fprintf(commandFile, "cat \"%s/%s/%s\" | sed -e 's/^<<<<<<<.*$/#ifdef MANUAL_MERGE_REQUIRED/' "
   1224 										"-e 's/^=======$/#else \\/\\/ MANUAL_MERGE_REQUIRED/' "
   1225 										"-e 's/^>>>>>>>.*$/#endif \\/\\/ MANUAL_MERGE_REQUIRED/' > \"%s/%s/x%s\"\n",
   1226 										sandboxCmd, renamedDir, renamed, sandboxCmd, renamedDir, renamed);
   1227 									fprintf(commandFile, "mv \"%s/%s/x%s\" \"%s/%s/%s\"\n",
   1228 										sandboxCmd, renamedDir, renamed, sandboxCmd, renamedDir, renamed);
   1229                                 }
   1230                                 if (options.emitGitCommands)
   1231                                     fprintf(commandFile, "git add \"%s/%s\"\n", renamedDir, renamed);
   1232                             } else {
   1233                                 bool oldNewDiff = CompareFiles(oldBase, workingDir, oldList, newBase, renamedDir, renamed);
   1234                                 if (oldNewDiff) {
   1235                                     if (options.emitPerforceCommands)
   1236                                         fprintf(oopsFile, "p4 open \"%s/%s/%s\"\n", sandboxCmd, renamedDir, renamed);
   1237                                     fprintf(oopsFile, "cp \"%s/%s/%s\" \"%s/%s/%s\"\n",
   1238                                             newCmd, renamedDir, renamed, sandboxCmd, renamedDir, renamed);
   1239                                     if (options.emitGitCommands)
   1240                                         fprintf(oopsFile, "git add \"%s/%s\"\n", renamedDir, renamed);
   1241                                 }
   1242                             }
   1243                         } else if (oldSandboxDiff) {
   1244                             modifiedFile = true;
   1245                             fprintf(stderr, "*** Modified file deleted: %s/%s\n", workingDir, oldFile);
   1246 //                                FindDeletedAndroidChanges(workingDir, oldFile);
   1247                         }
   1248                     } // if renamePass == false
   1249                 } // if sandOrder == 0
   1250                 if (modifiedFile) {
   1251                     fprintf(commandFile, "cat \"%s/%s/%s\" | sed -e '1 i\\\n#ifdef MANUAL_MERGE_REQUIRED\n' "
   1252                         "-e '$ a\\\n#endif \\/\\/ MANUAL_MERGE_REQUIRED\n' > \"%s/%s/x%s\"\n",
   1253                         sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile);
   1254                     fprintf(commandFile, "mv \"%s/%s/x%s\" \"%s/%s/%s\"\n",
   1255                         sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile);
   1256                 } else if (options.emitPerforceCommands)
   1257                     fprintf(commandFile, "p4 delete \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile);
   1258                 else if (options.emitGitCommands)
   1259                     fprintf(commandFile, "git rm \"%s/%s\"\n", workingDir, oldFile);
   1260                 else
   1261                     fprintf(commandFile, "rm \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile);
   1262             } else { // if oldDir != false
   1263  // !!! FIXME               start here;
   1264                     // check to see if old directory is empty ... (e.g., WebCore/doc)
   1265                     // ... and/or isn't in sandbox anyway (e.g., WebCore/LayoutTests)
   1266                         // if old directory is in sandbox (e.g. WebCore/kcanvas) that should work
   1267                 if (options.emitPerforceCommands)
   1268                     fprintf(commandFile, "p4 delete \"%s/%s/%s/...\"\n", sandboxCmd, workingDir, oldFile);
   1269                 else if (options.emitGitCommands)
   1270                     fprintf(commandFile, "git rm \"%s/%s/...\"\n", workingDir, oldFile);
   1271                 else
   1272                     fprintf(commandFile, "rm \"%s/%s/%s/...\"\n", sandboxCmd, workingDir, oldFile);
   1273                 if (renamePass == false)
   1274                     fprintf(stderr, "*** Directory deleted: %s/%s\n", workingDir, oldFile);
   1275             }
   1276             oldList += strlen(oldList) + 1;
   1277             continue;
   1278         }
   1279         if (order > 0) {
   1280              if (renamePass == false) {
   1281                 string rename(workingDir);
   1282                 rename.append("/");
   1283                 rename.append(newFile);
   1284                 bool skip = false;
   1285                 for (map<string, string>::iterator iter = renameMap.begin(); iter != renameMap.end(); iter++) {
   1286                     if (iter->second == rename) {
   1287                         skip = true;
   1288                         break;
   1289                     }
   1290                 }
   1291                 if (skip == false) {
   1292                     if (newDir) {
   1293                         if (strcmp(sandFile, newFile) != 0 &&
   1294                                 emptyDirectory(newBase, workingDir, newFile) == false) {
   1295                             fprintf(copyDirFile, "find \"%s/%s/%s\" -type d -print | "
   1296                                 "sed 's@%s/\\(.*\\)@mkdir %s/\\1@' | bash -s\n",
   1297                                 newCmd, workingDir, newFile, newCmd, sandboxCmd);
   1298                             fprintf(copyDirFile, "find \"%s/%s/%s\" -type f -print | "
   1299                                 "sed 's@%s/\\(.*\\)@cp %s/\\1 %s/\\1@' | bash -s\n",
   1300                                 newCmd, workingDir, newFile, newCmd, newCmd, sandboxCmd);
   1301                             if (options.emitPerforceCommands)
   1302                                 fprintf(copyDirFile, "find \"%s/%s/%s\" -type f -print | "
   1303                                     "p4 -x - add\n",
   1304                                     sandboxCmd, workingDir, newFile);
   1305                             else if (options.emitGitCommands)
   1306                                 fprintf(copyDirFile, "git add \"%s/%s\"\n",
   1307                                     workingDir, newFile);
   1308                         }
   1309                     } else {
   1310 //                        if (emptyDirectory(sandboxBase, workingDir)) {
   1311 //                            fprintf(commandFile, "mkdir \"%s/%s\"\n", sandboxCmd, workingDir);
   1312 //                        }
   1313                         bool edit = false;
   1314                         size_t newLen1 = strlen(newFile);
   1315                         for (map<string, string>::iterator iter = renameMap.begin(); iter != renameMap.end(); iter++) {
   1316                             if (strcmp(iter->second.c_str(), workingDir) == 0) {
   1317                                 const char* first = iter->first.c_str();
   1318                                 size_t firstLen = strlen(first);
   1319                                 if (firstLen > newLen1 && strcmp(newFile,
   1320                                         &first[firstLen - newLen1]) == 0) {
   1321                                     edit = true;
   1322                                     break;
   1323                                 }
   1324                             }
   1325                         }
   1326                         if (edit == false) {
   1327                             fprintf(commandFile, "cp \"%s/%s/%s\" \"%s/%s/%s\"\n",
   1328                                 newCmd, workingDir, newFile, sandboxCmd, workingDir, newFile);
   1329                                 if (options.emitPerforceCommands)
   1330                                     fprintf(commandFile, "p4 add \"%s/%s/%s\"\n", sandboxCmd, workingDir, newFile);
   1331                                 else if (options.emitGitCommands)
   1332                                     fprintf(commandFile, "git add \"%s/%s\"\n", workingDir, newFile);
   1333                         }
   1334                     }
   1335                 }
   1336             }
   1337             newList += strlen(newList) + 1;
   1338             continue;
   1339         }
   1340         if (oldDir) {
   1341             myassert(newDir);
   1342             size_t newLen1 = strlen(workingDir) + strlen(oldList);
   1343             char* newFile = new char[newLen1 + 1];
   1344             sprintf(newFile, "%s/%.*s", workingDir, (int) strlen(oldList) - 1,
   1345                 oldList);
   1346             if (sandOrder > 0) { // file is on old and new but not sandbox
   1347                 if (emptyDirectory(newBase, newFile) == false) {
   1348                     fprintf(copyDirFile, "find \"%s/%s\" -type d -print | "
   1349                         "sed 's@%s/\\(.*\\)@mkdir %s/\\1@' | bash -s\n",
   1350                         newCmd, newFile, newCmd, sandboxCmd);
   1351                     fprintf(copyDirFile, "find \"%s/%s\" -type f -print | "
   1352                         "sed 's@%s/\\(.*\\)@cp %s/\\1 %s/\\1@' | bash -s\n",
   1353                         newCmd, newFile, newCmd, newCmd, sandboxCmd);
   1354                     if (options.emitPerforceCommands)
   1355                         fprintf(copyDirFile, "find \"%s/%s\" -type f -print | "
   1356                             "p4 -x - add\n",
   1357                             sandboxCmd, newFile);
   1358                     else if (options.emitGitCommands)
   1359                         fprintf(copyDirFile, "git add \"%s\"", newFile);
   1360                 }
   1361              } else
   1362                 CompareDirs(newFile, renamePass);
   1363             delete[] newFile;
   1364         } else {
   1365             // at this point, the file is in both old and new webkits; see if it changed
   1366                // ignore executables, different or not (or always copy, or do text compare? or find binary compare? )
   1367             if (oldExecutable != newExecutable)
   1368                 fprintf(stderr, "*** %s/%s differs in the execute bit (may cause problems for perforce)\n", workingDir, oldFile);
   1369         //            myassert(sandOrder != 0 || sandFile[sandLen - 1] == '*');
   1370         //    Diff(oldBase, sandboxBase, workingDir, oldFile);
   1371             bool oldNewDiff = CompareFiles(oldBase, newBase, workingDir, oldList);
   1372             if (oldNewDiff && sandOrder == 0 && renamePass == false) { // if it changed, see if android also changed it
   1373                 if (options.emitPerforceCommands)
   1374                     fprintf(commandFile, "p4 edit \"%s/%s/%s\"\n", sandboxCmd, workingDir, oldFile);
   1375                 bool oldSandboxDiff = CompareFiles(oldBase, sandboxBase, workingDir, oldFile);
   1376                 if (oldSandboxDiff) {
   1377                     fprintf(commandFile,  "merge -q \"%s/%s/%s\" \"%s/%s/%s\" \"%s/%s/%s\"\n",
   1378                         sandboxCmd, workingDir, oldFile, oldCmd, workingDir, oldFile, newCmd, workingDir, oldFile);
   1379                     bool success = Merge(workingDir, oldFile);
   1380                     if (success == false) {
   1381 						fprintf(stderr, "*** Manual merge required: %s/%s\n", workingDir, oldFile);
   1382 						fprintf(commandFile, "cat \"%s/%s/%s\" | sed -e 's/^<<<<<<<.*$/#ifdef MANUAL_MERGE_REQUIRED/' "
   1383 							"-e 's/^=======$/#else \\/\\/ MANUAL_MERGE_REQUIRED/' -e 's/^>>>>>>>.*$/#endif \\/\\/ MANUAL_MERGE_REQUIRED/' > \"%s/%s/x%s\"\n",
   1384 							sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile);
   1385 						fprintf(commandFile, "mv \"%s/%s/x%s\" \"%s/%s/%s\"\n",
   1386 							sandboxCmd, workingDir, oldFile, sandboxCmd, workingDir, oldFile);
   1387                     }
   1388                 } else fprintf(commandFile, "cp \"%s/%s/%s\" \"%s/%s/%s\"\n", newCmd, workingDir, oldFile ,
   1389                     sandboxCmd, workingDir, oldFile);
   1390                 if (options.emitGitCommands)
   1391                     fprintf(commandFile, "git add \"%s/%s\"\n", workingDir, oldFile);
   1392             }
   1393         }
   1394         myassert(oldLen == newLen);
   1395         newList += strlen(newList) + 1;
   1396         oldList += strlen(oldList) + 1;
   1397     } while (true);
   1398     delete[] oldMem;
   1399     delete[] newMem;
   1400     delete[] sandboxMem;
   1401 }
   1402 
   1403 bool Ignore(char* fileName, size_t len)
   1404 {
   1405     if (len == 0)
   1406         return true;
   1407     if (fileName[len - 1] =='/')
   1408         return true;
   1409     if (strcmp(fileName, ".DS_Store") == 0)
   1410         return true;
   1411     if (strcmp(fileName, ".ignoreSVN") == 0)
   1412         return true;
   1413     return false;
   1414 }
   1415 
   1416 void FixStar(char* fileName, size_t len)
   1417 {
   1418     if (fileName[len - 1] =='*')
   1419         fileName[len - 1] = '\0';
   1420 }
   1421 
   1422 void FixColon(char** fileNamePtr, size_t* lenPtr)
   1423 {
   1424     char* fileName = *fileNamePtr;
   1425     size_t len = *lenPtr;
   1426     if (fileName[len - 1] !=':')
   1427         return;
   1428     fileName[len - 1] = '\0';
   1429     if (strncmp(fileName ,"./", 2) != 0)
   1430         return;
   1431     fileName += 2;
   1432     *fileNamePtr = fileName;
   1433     len -= 2;
   1434     *lenPtr = len;
   1435 }
   1436 
   1437 bool IgnoreDirectory(const char* dir, const char** dirList)
   1438 {
   1439     if (dirList == NULL)
   1440         return false;
   1441     const char* test;
   1442     while ((test = *dirList++) != NULL) {
   1443         if (strncmp(dir, test, strlen(test)) == 0)
   1444             return true;
   1445     }
   1446     return false;
   1447 }
   1448 
   1449 static void doSystem(char* scratch)
   1450 {
   1451     if (false) printf("%s\n", scratch);
   1452     int err = system(scratch);
   1453     myassert(err == 0);
   1454 }
   1455 
   1456 static void copyToCommand(char* scratch, string file)
   1457 {
   1458     doSystem(scratch);
   1459     char* diff = GetFile(file.c_str());
   1460     while (*diff) {
   1461         fprintf(commandFile, "%s\n", diff);
   1462         diff += strlen(diff) + 1;
   1463     }
   1464 }
   1465 
   1466 #define WEBKIT_EXCLUDED_DIRECTORIES \
   1467         "-not -path \"*Tests\" " /* includes LayoutTests, PageLoadTests */ \
   1468         "-not -path \"*Tests/*\" " /* includes LayoutTests, PageLoadTests */ \
   1469         "-not -path \"*Site\" " /* includes BugsSite, WebKitSite */ \
   1470         "-not -path \"*Site/*\" " /* includes BugsSite, WebKitSite */ \
   1471         "-not -path \"./PlanetWebKit/*\" " \
   1472         "-not -path \"./PlanetWebKit\" "
   1473 
   1474 #define ANDROID_EXCLUDED_FILES \
   1475         "-e '/^Files .* differ/ d' " \
   1476         "-e '/^Only in .*/ d' " \
   1477         "-e '/Android.mk/ d' " \
   1478         "-e '/android$/ d' "
   1479 
   1480 #define ANDROID_EXCLUDED_DIRS \
   1481         "-e '/\\/JavaScriptCore\\// d' " \
   1482         "-e '/\\/WebCore\\// d' "
   1483 
   1484 #define ANDROID_EXCLUDED_DIRS_GIT \
   1485         "-e '/ JavaScriptCore\\// d' " \
   1486         "-e '/ WebCore\\// d' "
   1487 
   1488 void CopyOther()
   1489 {
   1490     string excludedFiles = ANDROID_EXCLUDED_FILES;
   1491     if (options.emitGitCommands)
   1492         excludedFiles += ANDROID_EXCLUDED_DIRS_GIT;
   1493     else
   1494         excludedFiles += ANDROID_EXCLUDED_DIRS;
   1495     char scratch[1024];
   1496     // directories to ignore in webkit
   1497     string copyOtherWebKit = ScratchFile("CopyOtherWebKit");
   1498     sprintf(scratch, "cd %s ;  find . -type d -not -empty "
   1499         WEBKIT_EXCLUDED_DIRECTORIES
   1500         " > %s", newBase, copyOtherWebKit.c_str());
   1501     doSystem(scratch);
   1502     // directories to ignore in android
   1503     string copyOtherAndroid = ScratchFile("CopyOtherAndroid");
   1504     sprintf(scratch, "cd %s ;  find . -type d -not -empty "
   1505         "-not -path \"*.git*\" "
   1506         "-not -path \"*android*\" "
   1507         " > %s", sandboxBase, copyOtherAndroid.c_str());
   1508     doSystem(scratch);
   1509     if (0) {
   1510         string copyOtherMkDir = ScratchFile("CopyOtherMkDir");
   1511         sprintf(scratch, "diff %s %s | sed -e 's@< \\./\\(.*\\)$"
   1512             "@mkdir %s/\\1@' "
   1513             "-e '/^[0-9].*/ d' "
   1514             "-e '/>.*/ d' "
   1515             "-e '/---/ d' "
   1516             "-e '/\\/JavaScriptCore\\// d' "
   1517             "-e '/\\/WebCore\\// d' "
   1518             "> %s", copyOtherWebKit.c_str(), copyOtherAndroid.c_str(), sandboxCmd,
   1519             copyOtherMkDir.c_str());
   1520         if (options.debug)
   1521             fprintf(stderr, "%s\n", scratch);
   1522         copyToCommand(scratch, copyOtherMkDir);
   1523     }
   1524     string copyOtherDiff = ScratchFile("CopyOtherDiff");
   1525     int scratchLen = sprintf(scratch, "diff %s %s | sed -e 's@< \\./\\(.*\\)$"
   1526         "@mkdir -p -v %s/\\1 ; find %s/\\1 -type f -depth 1 -exec cp {} %s/\\1 \";\"",
   1527         copyOtherWebKit.c_str(), copyOtherAndroid.c_str(), sandboxCmd, newCmd,
   1528         sandboxCmd);
   1529     if (options.emitGitCommands)
   1530         scratchLen += sprintf(&scratch[scratchLen], " ; cd %s ; find ", sandboxCmd);
   1531     else
   1532         scratchLen += sprintf(&scratch[scratchLen], " ; find %s/", sandboxCmd);
   1533     scratchLen += sprintf(&scratch[scratchLen], "\\1 -type f -depth 1 | ");
   1534     if (options.emitPerforceCommands)
   1535         scratchLen += sprintf(&scratch[scratchLen], "p4 -x - add ");
   1536     else if (options.emitGitCommands)
   1537         scratchLen += sprintf(&scratch[scratchLen], "xargs git add ");
   1538     scratchLen += sprintf(&scratch[scratchLen],
   1539         "@' -e '/^[0-9].*/ d' "
   1540         "-e '/>.*/ d' "
   1541         "-e '/---/ d' "
   1542         "-e '/\\/JavaScriptCore\\// d' "
   1543         "-e '/\\/WebCore\\// d' "
   1544         "> %s", copyOtherDiff.c_str());
   1545     if (options.debug)
   1546         fprintf(stderr, "%s\n", scratch);
   1547     copyToCommand(scratch, copyOtherDiff);
   1548     string deleteOtherDiff = ScratchFile("DeleteOtherDiff");
   1549     scratchLen = sprintf(scratch, "diff -r -q %s %s | sed -e "
   1550         "'s@Only in %s/\\(.*\\)\\: \\(.*\\)@",
   1551         newBase, sandboxBase, sandboxBase);
   1552     if (options.emitPerforceCommands)
   1553         scratchLen += sprintf(&scratch[scratchLen], "p4 delete %s/", sandboxCmd);
   1554     else if (options.emitGitCommands)
   1555         scratchLen += sprintf(&scratch[scratchLen], "git rm ");
   1556     else
   1557         scratchLen += sprintf(&scratch[scratchLen], "rm %s/", sandboxCmd);
   1558     scratchLen += sprintf(&scratch[scratchLen], "\\1/\\2@' %s > %s",
   1559         excludedFiles.c_str(), deleteOtherDiff.c_str());
   1560     if (options.debug)
   1561         fprintf(stderr, "%s\n", scratch);
   1562     copyToCommand(scratch, deleteOtherDiff);
   1563     string addOtherDiff = ScratchFile("AddOtherDiff");
   1564     scratchLen = sprintf(scratch, "diff -r -q %s %s | sed -e "
   1565         "'s@Only in %s/\\(.*\\)\\: \\(.*\\)"
   1566             "@mkdir -p -v %s/\\1 ; cp %s/\\1/\\2 %s/\\1/\\2 ; ",
   1567             newBase, sandboxBase, newBase, sandboxCmd, newCmd, sandboxCmd);
   1568     if (options.emitPerforceCommands)
   1569         scratchLen += sprintf(&scratch[scratchLen],
   1570             "p4 add %s/\\1/\\2", sandboxCmd);
   1571     else if (options.emitGitCommands)
   1572         scratchLen += sprintf(&scratch[scratchLen],
   1573             "git add \\1/\\2");
   1574     scratchLen += sprintf(&scratch[scratchLen], "@' %s > %s",
   1575         excludedFiles.c_str(), addOtherDiff.c_str());
   1576     if (options.debug)
   1577         fprintf(stderr, "%s\n", scratch);
   1578     copyToCommand(scratch, addOtherDiff);
   1579     string editOtherDiff = ScratchFile("EditOtherDiff");
   1580     scratchLen = sprintf(scratch, "diff -r -q %s %s | sed -e "
   1581         "'s@Files %s/\\(.*\\) and %s/\\(.*\\) differ@",
   1582         newBase, sandboxBase, newBase, sandboxBase);
   1583     if (options.emitPerforceCommands)
   1584         scratchLen += sprintf(&scratch[scratchLen],
   1585             "p4 edit %s/\\2 ; ", sandboxCmd);
   1586     scratchLen += sprintf(&scratch[scratchLen], "cp %s/\\1 %s/\\2 ",
   1587        newCmd, sandboxCmd);
   1588     if (options.emitGitCommands)
   1589         scratchLen += sprintf(&scratch[scratchLen],
   1590             " ; git add \\2");
   1591     scratchLen += sprintf(&scratch[scratchLen], "@' %s > %s",
   1592         excludedFiles.c_str(), editOtherDiff.c_str());
   1593     if (options.debug)
   1594         fprintf(stderr, "%s\n", scratch);
   1595     copyToCommand(scratch, editOtherDiff);
   1596 }
   1597 
   1598 void MakeExecutable(const string& filename)
   1599 {
   1600     string makeExScript = "chmod +x ";
   1601     makeExScript += filename;
   1602     int err = system(makeExScript.c_str());
   1603     myassert(err == 0);
   1604 }
   1605 
   1606 bool ReadArgs(char* const args[], int argCount)
   1607 {
   1608     int index = 0;
   1609     const char* toolpath = args[index++];
   1610     // first arg is path to this executable
   1611     // use this to build default paths
   1612 
   1613     for (; index < argCount; index++) {
   1614         const char* arg = args[index];
   1615         if (strncmp(arg, "-a", 2) == 0 || strcmp(arg, "--android") == 0) {
   1616             index++;
   1617             options.androidWebKit = args[index];
   1618             continue;
   1619         }
   1620         if (strncmp(arg, "-b", 2) == 0 || strcmp(arg, "--basewebkit") == 0) {
   1621             index++;
   1622             options.baseWebKit = args[index];
   1623             continue;
   1624         }
   1625         if (strncmp(arg, "-c", 2) == 0 || strcmp(arg, "--mergecore") == 0) {
   1626             options.clearOnce();
   1627             options.mergeCore = true;
   1628             continue;
   1629         }
   1630         if (strncmp(arg, "-d", 2) == 0 || strcmp(arg, "--debug") == 0) {
   1631             options.debug = true;
   1632             continue;
   1633         }
   1634         if (strncmp(arg, "-e", 2) == 0 || strcmp(arg, "--emptydirs") == 0) {
   1635             options.clearOnce();
   1636             options.removeEmptyDirs = true;
   1637             continue;
   1638         }
   1639         if (strncmp(arg, "-g", 2) == 0 || strcmp(arg, "--git") == 0) {
   1640             options.emitGitCommands = true;
   1641             if (options.emitPerforceCommands == false)
   1642                 continue;
   1643         }
   1644         if (strncmp(arg, "-m", 2) == 0 || strcmp(arg, "--mergemake") == 0) {
   1645             options.clearOnce();
   1646             options.mergeMake = true;
   1647             continue;
   1648         }
   1649         if (strncmp(arg, "-n", 2) == 0 || strcmp(arg, "--newwebkit") == 0) {
   1650             index++;
   1651             options.newWebKit = args[index];
   1652             continue;
   1653         }
   1654         if (strncmp(arg, "-o", 2) == 0 || strcmp(arg, "--copyother") == 0) {
   1655             options.clearOnce();
   1656             options.copyOther = true;
   1657             continue;
   1658         }
   1659         if (strncmp(arg, "-p", 2) == 0 || strcmp(arg, "--perforce") == 0) {
   1660             options.emitPerforceCommands = true;
   1661             if (options.emitGitCommands == false)
   1662                 continue;
   1663         }
   1664         if (strncmp(arg, "-s", 2) == 0 || strcmp(arg, "--removesvn") == 0) {
   1665             options.clearOnce();
   1666             options.removeSVNDirs = true;
   1667             continue;
   1668         }
   1669         if (strncmp(arg, "-v", 2) == 0 || strcmp(arg, "--verbose") == 0) {
   1670             options.verbose = true;
   1671             fprintf(stderr, "path: %s\n", toolpath);
   1672             int err = system("pwd > pwd.txt");
   1673             myassert(err != -1);
   1674             fprintf(stderr, "pwd: %s\n", GetFile("pwd.txt"));
   1675             system("rm pwd.txt");
   1676             continue;
   1677         }
   1678         if (strncmp(arg, "-x", 2) == 0 || strcmp(arg, "--execute") == 0) {
   1679             options.execute = true;
   1680             continue;
   1681         }
   1682         if (options.emitGitCommands && options.emitPerforceCommands)
   1683             printf("choose one of --git and --perforce\n");
   1684         else if (strncmp(arg, "-h", 2) != 0 && strcmp(arg, "--help") != 0 && strcmp(arg, "-?") != 0)
   1685             printf("%s not understood\n", args[index]);
   1686         printf(
   1687 "WebKit Merge for Android version 1.1\n"
   1688 "Usage: webkitmerge -a path -b path -n path [-g or -p] [-c -d -e -m -o -s -v -x]\n"
   1689 "Options -c -e -m -o -s are set unless one or more are passed.\n"
   1690 "Leave -g and -p unset to copy, merge, and delete files outside of source control.\n"
   1691 "-a --android path     Set the Android webkit path to merge to.\n"
   1692 "-b --basewebkit path  Set the common base for Android and the newer webkit.\n"
   1693 "-c --mergecore        Create merge scripts for WebCore, JavaScriptCore .h .cpp.\n"
   1694 "-d --debug            Show debugging printfs; loop forever on internal assert.\n"
   1695 "-e --emptydirs        Remove empty directories from webkit trees.\n"
   1696 "-g --git              Emit git commands.\n"
   1697 "-h --help             Show this help.\n"
   1698 "-m --mergemake        Create merge scripts for WebCore/Android.mk,\n"
   1699 "                      WebCore/Android.derived.mk, and JavaScriptCore/Android.mk.\n"
   1700 "-n --newwebkit path   Set the webkit to merge from.\n"
   1701 "-o --copyother        Create script to copy other webkit directories.\n"
   1702 "-p --perforce         Emit perforce commands.\n"
   1703 "-s --removesvn        Remove svn directories from webkit trees.\n"
   1704 "-v --verbose          Show status printfs.\n"
   1705 "-x --execute          Execute the merge scripts.\n"
   1706         );
   1707         return false;
   1708     }
   1709     return options.finish();
   1710 }
   1711 
   1712 int main (int argCount, char* const args[])
   1713 {
   1714     if (ReadArgs(args, argCount) == false)
   1715         return 0;
   1716     int err;
   1717     // First remove all .svn directories
   1718     if (options.removeSVNDirs) {
   1719         if (options.verbose)
   1720             fprintf(stderr, "removing svn directories from %s\n", newBase);
   1721         string removeSVNStr = string("find ") + newBase +
   1722             " -type d -name \".svn\" -print0 | xargs -0 rm -rf";
   1723         err = system(removeSVNStr.c_str());
   1724         myassert(err == 0);
   1725     }
   1726     // Remove all empty directories
   1727     if (options.removeEmptyDirs) {
   1728         if (options.verbose)
   1729             fprintf(stderr, "removing empty directories from %s, %s\n",
   1730                 oldBase, newBase);
   1731         string removeEmpty = string("find ") + oldBase + " " + newBase +
   1732             " -type d -empty -delete";
   1733         err = system(removeEmpty.c_str());
   1734         myassert(err == 0);
   1735     }
   1736     if (options.mergeCore /* || options.mergeMake */) {
   1737         if (options.verbose)
   1738             fprintf(stderr, "building rename map\n");
   1739         commandFile = fopen("/dev/null", "w");
   1740         copyDirFile = fopen("/dev/null", "w");
   1741         CompareDirs("WebCore", true); // build rename map
   1742         CompareDirs("JavaScriptCore", true);
   1743         fclose(copyDirFile);
   1744         fclose(commandFile);
   1745     }
   1746     if (options.mergeMake) {
   1747         if (options.verbose)
   1748             fprintf(stderr, "building make.sh\n");
   1749         string makeShell = outputDir + "make.sh";
   1750         commandFile = fopen(makeShell.c_str(), "w");
   1751         if (options.emitGitCommands || options.emitPerforceCommands)
   1752             fprintf(commandFile, "cd %s\n", sandboxCmd);
   1753         UpdateMake("WebCore");
   1754         UpdateMake("JavaScriptCore");
   1755         UpdateDerivedMake();
   1756         fclose(commandFile);
   1757         MakeExecutable(makeShell);
   1758     }
   1759     if (options.copyOther) {
   1760         if (options.verbose)
   1761             fprintf(stderr, "building copyOther.sh\n");
   1762         string copyOtherShell = outputDir + "copyOther.sh";
   1763         commandFile = fopen(copyOtherShell.c_str(), "w");
   1764         if (options.emitGitCommands || options.emitPerforceCommands)
   1765             fprintf(commandFile, "cd %s\n", sandboxCmd);
   1766         CopyOther();
   1767         fclose(commandFile);
   1768         MakeExecutable(copyOtherShell);
   1769     }
   1770     if (options.mergeCore) {
   1771         if (options.verbose)
   1772             fprintf(stderr, "building command.sh copyDir.sh oops.sh\n");
   1773         string commandShell = outputDir + "command.sh";
   1774         commandFile = fopen(commandShell.c_str(), "w");
   1775         if (options.emitGitCommands || options.emitPerforceCommands)
   1776             fprintf(commandFile, "cd %s\n", sandboxCmd);
   1777         string copyDirShell = outputDir + "copyDir.sh";
   1778         copyDirFile = fopen(copyDirShell.c_str(), "w");
   1779         if (options.emitGitCommands || options.emitPerforceCommands)
   1780             fprintf(copyDirFile, "cd %s\n", sandboxCmd);
   1781         string oopsShell = outputDir + "oops.sh";
   1782         oopsFile = fopen(oopsShell.c_str(), "w");
   1783         if (options.emitGitCommands || options.emitPerforceCommands)
   1784             fprintf(oopsFile, "cd %s\n", sandboxCmd);
   1785         CompareDirs("WebCore", false); // generate command script
   1786         CompareDirs("JavaScriptCore", false);
   1787         fclose(oopsFile);
   1788         fclose(copyDirFile);
   1789         fclose(commandFile);
   1790         MakeExecutable(oopsShell);
   1791         MakeExecutable(copyDirShell);
   1792         MakeExecutable(commandShell);
   1793     }
   1794     if (options.execute) {
   1795         if (options.mergeCore) {
   1796             if (options.verbose)
   1797                 fprintf(stderr, "executing command.sh\n");
   1798             string execCommand = "cd " + options.androidWebKit + "; . " + outputDir + "command.sh";
   1799             err = system(execCommand.c_str());
   1800             myassert(err == 0);
   1801             if (options.verbose)
   1802                 fprintf(stderr, "executing copyDir.sh\n");
   1803             string execCopy = "cd " + options.androidWebKit + "; . " + outputDir + "copyDir.sh";
   1804             err = system(execCopy.c_str());
   1805             myassert(err == 0);
   1806         }
   1807         if (options.mergeMake) {
   1808             if (options.verbose)
   1809                 fprintf(stderr, "executing make.sh\n");
   1810             string execMake = "cd " + options.androidWebKit + "; . " + outputDir + "make.sh";
   1811             err = system(execMake.c_str());
   1812             myassert(err == 0);
   1813         }
   1814         if (options.copyOther) {
   1815             if (options.verbose)
   1816                 fprintf(stderr, "executing copyOther.sh\n");
   1817             string execCopyOther = "cd " + options.androidWebKit + "; . " + outputDir + "copyOther.sh";
   1818             err = system(execCopyOther.c_str());
   1819             myassert(err == 0);
   1820         }
   1821     }
   1822     if (options.verbose)
   1823         fprintf(stderr, "done!\n");
   1824     else {
   1825         string rmAllCmd = "rm " + scratchDir + "* ; rmdir " + scratchDir;
   1826         err = system(rmAllCmd.c_str());
   1827         myassert(err == 0);
   1828     }
   1829     return 0;
   1830 }
   1831 
   1832 /* things to do:
   1833     when inserting MANUAL_MERGE_REQUIRED, if contents is #preprocessor, balance first?
   1834 */
   1835