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
33using namespace std;
34
35string WEBKITLIB = "external/webkit";
36
37string oldBaseStr;
38string oldCmdStr;
39string newBaseStr;
40string newCmdStr;
41string sandboxBaseStr;
42string sandboxCmdStr;
43string outputDir;
44string scratchDir;
45
46const char* oldBase;
47const char* oldCmd;
48const char* newBase;
49const char* newCmd;
50const char* sandboxBase;
51const char* sandboxCmd;
52
53bool 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
63class Options {
64public:
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;
93private:
94    bool cleared;
95};
96
97Options options;
98
99char* 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
130bool 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
173string ScratchFile(const char* name)
174{
175    return scratchDir + name + ".txt";
176}
177
178FILE* commandFile;
179FILE* copyDirFile;
180FILE* oopsFile;
181
182string 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
194char* 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
203bool 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
215bool Merge(const char* dir, const char* file)
216{
217    return Merge(dir, file, dir, file, "/dev/null");
218}
219
220/*
221static 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
230static 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
236static void skipSpace(char** linePtr) {
237    char* line = *linePtr;
238    while (isspace(line[0]))
239        line++;
240    *linePtr = line;
241}
242
243/*
244static 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
254static 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
264static bool isTokenChar(char ch) {
265    return isalnum(ch) || ch == '_';
266}
267
268struct Pair {
269    char* name;
270    int brace;
271};
272
273class Parse {
274public:
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
322int 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    }
438checkForBrace:
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    }
444nextLine:
445    return textLen;
446}
447
448};
449
450char* 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
462void 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
479bool 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
498bool 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
503int 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
526string 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
574char* 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
715vector<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
744bool 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
757void 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
949int 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
963void 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
1076static 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
1100static bool emptyDirectory(const char* work, const char* dir) {
1101    return emptyDirectory("", work, dir);
1102}
1103
1104void 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
1403bool 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
1416void FixStar(char* fileName, size_t len)
1417{
1418    if (fileName[len - 1] =='*')
1419        fileName[len - 1] = '\0';
1420}
1421
1422void 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
1437bool 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
1449static void doSystem(char* scratch)
1450{
1451    if (false) printf("%s\n", scratch);
1452    int err = system(scratch);
1453    myassert(err == 0);
1454}
1455
1456static 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
1488void 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
1598void 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
1606bool 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
1712int 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