AaptAssets.cpp revision 6c191299a73388cd593809c0b66bafbd08fd2982
1//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
6#include "ResourceFilter.h"
7#include "Main.h"
8
9#include <utils/misc.h>
10#include <utils/SortedVector.h>
11
12#include <ctype.h>
13#include <dirent.h>
14#include <errno.h>
15
16static const char* kDefaultLocale = "default";
17static const char* kWildcardName = "any";
18static const char* kAssetDir = "assets";
19static const char* kResourceDir = "res";
20static const char* kValuesDir = "values";
21static const char* kMipmapDir = "mipmap";
22static const char* kInvalidChars = "/\\:";
23static const size_t kMaxAssetFileName = 100;
24
25static const String8 kResString(kResourceDir);
26
27/*
28 * Names of asset files must meet the following criteria:
29 *
30 *  - the filename length must be less than kMaxAssetFileName bytes long
31 *    (and can't be empty)
32 *  - all characters must be 7-bit printable ASCII
33 *  - none of { '/' '\\' ':' }
34 *
35 * Pass in just the filename, not the full path.
36 */
37static bool validateFileName(const char* fileName)
38{
39    const char* cp = fileName;
40    size_t len = 0;
41
42    while (*cp != '\0') {
43        if ((*cp & 0x80) != 0)
44            return false;           // reject high ASCII
45        if (*cp < 0x20 || *cp >= 0x7f)
46            return false;           // reject control chars and 0x7f
47        if (strchr(kInvalidChars, *cp) != NULL)
48            return false;           // reject path sep chars
49        cp++;
50        len++;
51    }
52
53    if (len < 1 || len > kMaxAssetFileName)
54        return false;               // reject empty or too long
55
56    return true;
57}
58
59// The default to use if no other ignore pattern is defined.
60const char * const gDefaultIgnoreAssets =
61    "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
62// The ignore pattern that can be passed via --ignore-assets in Main.cpp
63const char * gUserIgnoreAssets = NULL;
64
65static bool isHidden(const char *root, const char *path)
66{
67    // Patterns syntax:
68    // - Delimiter is :
69    // - Entry can start with the flag ! to avoid printing a warning
70    //   about the file being ignored.
71    // - Entry can have the flag "<dir>" to match only directories
72    //   or <file> to match only files. Default is to match both.
73    // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74    //   where prefix/suffix must have at least 1 character (so that
75    //   we don't match a '*' catch-all pattern.)
76    // - The special filenames "." and ".." are always ignored.
77    // - Otherwise the full string is matched.
78    // - match is not case-sensitive.
79
80    if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
81        return true;
82    }
83
84    const char *delim = ":";
85    const char *p = gUserIgnoreAssets;
86    if (!p || !p[0]) {
87        p = getenv("ANDROID_AAPT_IGNORE");
88    }
89    if (!p || !p[0]) {
90        p = gDefaultIgnoreAssets;
91    }
92    char *patterns = strdup(p);
93
94    bool ignore = false;
95    bool chatty = true;
96    char *matchedPattern = NULL;
97
98    String8 fullPath(root);
99    fullPath.appendPath(path);
100    FileType type = getFileType(fullPath);
101
102    int plen = strlen(path);
103
104    // Note: we don't have strtok_r under mingw.
105    for(char *token = strtok(patterns, delim);
106            !ignore && token != NULL;
107            token = strtok(NULL, delim)) {
108        chatty = token[0] != '!';
109        if (!chatty) token++; // skip !
110        if (strncasecmp(token, "<dir>" , 5) == 0) {
111            if (type != kFileTypeDirectory) continue;
112            token += 5;
113        }
114        if (strncasecmp(token, "<file>", 6) == 0) {
115            if (type != kFileTypeRegular) continue;
116            token += 6;
117        }
118
119        matchedPattern = token;
120        int n = strlen(token);
121
122        if (token[0] == '*') {
123            // Match *suffix
124            token++;
125            n--;
126            if (n <= plen) {
127                ignore = strncasecmp(token, path + plen - n, n) == 0;
128            }
129        } else if (n > 1 && token[n - 1] == '*') {
130            // Match prefix*
131            ignore = strncasecmp(token, path, n - 1) == 0;
132        } else {
133            ignore = strcasecmp(token, path) == 0;
134        }
135    }
136
137    if (ignore && chatty) {
138        fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139                type == kFileTypeDirectory ? "dir" : "file",
140                path,
141                matchedPattern ? matchedPattern : "");
142    }
143
144    free(patterns);
145    return ignore;
146}
147
148// =========================================================================
149// =========================================================================
150// =========================================================================
151
152status_t
153AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
154{
155    ResTable_config config;
156
157    // IMSI - MCC
158    if (getMccName(part.string(), &config)) {
159        *axis = AXIS_MCC;
160        *value = config.mcc;
161        return 0;
162    }
163
164    // IMSI - MNC
165    if (getMncName(part.string(), &config)) {
166        *axis = AXIS_MNC;
167        *value = config.mnc;
168        return 0;
169    }
170
171    // locale - language
172    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
173        *axis = AXIS_LANGUAGE;
174        *value = part[1] << 8 | part[0];
175        return 0;
176    }
177
178    // locale - language_REGION
179    if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
180            && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
181        *axis = AXIS_LANGUAGE;
182        *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
183        return 0;
184    }
185
186    // layout direction
187    if (getLayoutDirectionName(part.string(), &config)) {
188        *axis = AXIS_LAYOUTDIR;
189        *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
190        return 0;
191    }
192
193    // smallest screen dp width
194    if (getSmallestScreenWidthDpName(part.string(), &config)) {
195        *axis = AXIS_SMALLESTSCREENWIDTHDP;
196        *value = config.smallestScreenWidthDp;
197        return 0;
198    }
199
200    // screen dp width
201    if (getScreenWidthDpName(part.string(), &config)) {
202        *axis = AXIS_SCREENWIDTHDP;
203        *value = config.screenWidthDp;
204        return 0;
205    }
206
207    // screen dp height
208    if (getScreenHeightDpName(part.string(), &config)) {
209        *axis = AXIS_SCREENHEIGHTDP;
210        *value = config.screenHeightDp;
211        return 0;
212    }
213
214    // screen layout size
215    if (getScreenLayoutSizeName(part.string(), &config)) {
216        *axis = AXIS_SCREENLAYOUTSIZE;
217        *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
218        return 0;
219    }
220
221    // screen layout long
222    if (getScreenLayoutLongName(part.string(), &config)) {
223        *axis = AXIS_SCREENLAYOUTLONG;
224        *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
225        return 0;
226    }
227
228    // orientation
229    if (getOrientationName(part.string(), &config)) {
230        *axis = AXIS_ORIENTATION;
231        *value = config.orientation;
232        return 0;
233    }
234
235    // ui mode type
236    if (getUiModeTypeName(part.string(), &config)) {
237        *axis = AXIS_UIMODETYPE;
238        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
239        return 0;
240    }
241
242    // ui mode night
243    if (getUiModeNightName(part.string(), &config)) {
244        *axis = AXIS_UIMODENIGHT;
245        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
246        return 0;
247    }
248
249    // density
250    if (getDensityName(part.string(), &config)) {
251        *axis = AXIS_DENSITY;
252        *value = config.density;
253        return 0;
254    }
255
256    // touchscreen
257    if (getTouchscreenName(part.string(), &config)) {
258        *axis = AXIS_TOUCHSCREEN;
259        *value = config.touchscreen;
260        return 0;
261    }
262
263    // keyboard hidden
264    if (getKeysHiddenName(part.string(), &config)) {
265        *axis = AXIS_KEYSHIDDEN;
266        *value = config.inputFlags;
267        return 0;
268    }
269
270    // keyboard
271    if (getKeyboardName(part.string(), &config)) {
272        *axis = AXIS_KEYBOARD;
273        *value = config.keyboard;
274        return 0;
275    }
276
277    // navigation hidden
278    if (getNavHiddenName(part.string(), &config)) {
279        *axis = AXIS_NAVHIDDEN;
280        *value = config.inputFlags;
281        return 0;
282    }
283
284    // navigation
285    if (getNavigationName(part.string(), &config)) {
286        *axis = AXIS_NAVIGATION;
287        *value = config.navigation;
288        return 0;
289    }
290
291    // screen size
292    if (getScreenSizeName(part.string(), &config)) {
293        *axis = AXIS_SCREENSIZE;
294        *value = config.screenSize;
295        return 0;
296    }
297
298    // version
299    if (getVersionName(part.string(), &config)) {
300        *axis = AXIS_VERSION;
301        *value = config.version;
302        return 0;
303    }
304
305    return 1;
306}
307
308uint32_t
309AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
310{
311    switch (axis) {
312        case AXIS_MCC:
313            return config.mcc;
314        case AXIS_MNC:
315            return config.mnc;
316        case AXIS_LANGUAGE:
317            return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
318                | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
319        case AXIS_LAYOUTDIR:
320            return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
321        case AXIS_SCREENLAYOUTSIZE:
322            return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
323        case AXIS_ORIENTATION:
324            return config.orientation;
325        case AXIS_UIMODETYPE:
326            return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
327        case AXIS_UIMODENIGHT:
328            return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
329        case AXIS_DENSITY:
330            return config.density;
331        case AXIS_TOUCHSCREEN:
332            return config.touchscreen;
333        case AXIS_KEYSHIDDEN:
334            return config.inputFlags;
335        case AXIS_KEYBOARD:
336            return config.keyboard;
337        case AXIS_NAVIGATION:
338            return config.navigation;
339        case AXIS_SCREENSIZE:
340            return config.screenSize;
341        case AXIS_SMALLESTSCREENWIDTHDP:
342            return config.smallestScreenWidthDp;
343        case AXIS_SCREENWIDTHDP:
344            return config.screenWidthDp;
345        case AXIS_SCREENHEIGHTDP:
346            return config.screenHeightDp;
347        case AXIS_VERSION:
348            return config.version;
349    }
350    return 0;
351}
352
353bool
354AaptGroupEntry::configSameExcept(const ResTable_config& config,
355        const ResTable_config& otherConfig, int axis)
356{
357    for (int i=AXIS_START; i<=AXIS_END; i++) {
358        if (i == axis) {
359            continue;
360        }
361        if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
362            return false;
363        }
364    }
365    return true;
366}
367
368bool
369AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
370{
371    mParamsChanged = true;
372
373    Vector<String8> parts;
374
375    String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
376    String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
377    String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
378
379    const char *p = dir;
380    const char *q;
381    while (NULL != (q = strchr(p, '-'))) {
382        String8 val(p, q-p);
383        val.toLower();
384        parts.add(val);
385        //printf("part: %s\n", parts[parts.size()-1].string());
386        p = q+1;
387    }
388    String8 val(p);
389    val.toLower();
390    parts.add(val);
391    //printf("part: %s\n", parts[parts.size()-1].string());
392
393    const int N = parts.size();
394    int index = 0;
395    String8 part = parts[index];
396
397    // resource type
398    if (!isValidResourceType(part)) {
399        return false;
400    }
401    *resType = part;
402
403    index++;
404    if (index == N) {
405        goto success;
406    }
407    part = parts[index];
408
409    // imsi - mcc
410    if (getMccName(part.string())) {
411        mcc = part;
412
413        index++;
414        if (index == N) {
415            goto success;
416        }
417        part = parts[index];
418    } else {
419        //printf("not mcc: %s\n", part.string());
420    }
421
422    // imsi - mnc
423    if (getMncName(part.string())) {
424        mnc = part;
425
426        index++;
427        if (index == N) {
428            goto success;
429        }
430        part = parts[index];
431    } else {
432        //printf("not mcc: %s\n", part.string());
433    }
434
435    // locale - language
436    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
437        loc = part;
438
439        index++;
440        if (index == N) {
441            goto success;
442        }
443        part = parts[index];
444    } else {
445        //printf("not language: %s\n", part.string());
446    }
447
448    // locale - region
449    if (loc.length() > 0
450            && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
451        loc += "-";
452        part.toUpper();
453        loc += part.string() + 1;
454
455        index++;
456        if (index == N) {
457            goto success;
458        }
459        part = parts[index];
460    } else {
461        //printf("not region: %s\n", part.string());
462    }
463
464    if (getLayoutDirectionName(part.string())) {
465        layoutDir = part;
466
467        index++;
468        if (index == N) {
469            goto success;
470        }
471        part = parts[index];
472    } else {
473        //printf("not layout direction: %s\n", part.string());
474    }
475
476    if (getSmallestScreenWidthDpName(part.string())) {
477        smallestwidthdp = part;
478
479        index++;
480        if (index == N) {
481            goto success;
482        }
483        part = parts[index];
484    } else {
485        //printf("not smallest screen width dp: %s\n", part.string());
486    }
487
488    if (getScreenWidthDpName(part.string())) {
489        widthdp = part;
490
491        index++;
492        if (index == N) {
493            goto success;
494        }
495        part = parts[index];
496    } else {
497        //printf("not screen width dp: %s\n", part.string());
498    }
499
500    if (getScreenHeightDpName(part.string())) {
501        heightdp = part;
502
503        index++;
504        if (index == N) {
505            goto success;
506        }
507        part = parts[index];
508    } else {
509        //printf("not screen height dp: %s\n", part.string());
510    }
511
512    if (getScreenLayoutSizeName(part.string())) {
513        layoutsize = part;
514
515        index++;
516        if (index == N) {
517            goto success;
518        }
519        part = parts[index];
520    } else {
521        //printf("not screen layout size: %s\n", part.string());
522    }
523
524    if (getScreenLayoutLongName(part.string())) {
525        layoutlong = part;
526
527        index++;
528        if (index == N) {
529            goto success;
530        }
531        part = parts[index];
532    } else {
533        //printf("not screen layout long: %s\n", part.string());
534    }
535
536    // orientation
537    if (getOrientationName(part.string())) {
538        orient = part;
539
540        index++;
541        if (index == N) {
542            goto success;
543        }
544        part = parts[index];
545    } else {
546        //printf("not orientation: %s\n", part.string());
547    }
548
549    // ui mode type
550    if (getUiModeTypeName(part.string())) {
551        uiModeType = part;
552
553        index++;
554        if (index == N) {
555            goto success;
556        }
557        part = parts[index];
558    } else {
559        //printf("not ui mode type: %s\n", part.string());
560    }
561
562    // ui mode night
563    if (getUiModeNightName(part.string())) {
564        uiModeNight = part;
565
566        index++;
567        if (index == N) {
568            goto success;
569        }
570        part = parts[index];
571    } else {
572        //printf("not ui mode night: %s\n", part.string());
573    }
574
575    // density
576    if (getDensityName(part.string())) {
577        den = part;
578
579        index++;
580        if (index == N) {
581            goto success;
582        }
583        part = parts[index];
584    } else {
585        //printf("not density: %s\n", part.string());
586    }
587
588    // touchscreen
589    if (getTouchscreenName(part.string())) {
590        touch = part;
591
592        index++;
593        if (index == N) {
594            goto success;
595        }
596        part = parts[index];
597    } else {
598        //printf("not touchscreen: %s\n", part.string());
599    }
600
601    // keyboard hidden
602    if (getKeysHiddenName(part.string())) {
603        keysHidden = part;
604
605        index++;
606        if (index == N) {
607            goto success;
608        }
609        part = parts[index];
610    } else {
611        //printf("not keysHidden: %s\n", part.string());
612    }
613
614    // keyboard
615    if (getKeyboardName(part.string())) {
616        key = part;
617
618        index++;
619        if (index == N) {
620            goto success;
621        }
622        part = parts[index];
623    } else {
624        //printf("not keyboard: %s\n", part.string());
625    }
626
627    // navigation hidden
628    if (getNavHiddenName(part.string())) {
629        navHidden = part;
630
631        index++;
632        if (index == N) {
633            goto success;
634        }
635        part = parts[index];
636    } else {
637        //printf("not navHidden: %s\n", part.string());
638    }
639
640    if (getNavigationName(part.string())) {
641        nav = part;
642
643        index++;
644        if (index == N) {
645            goto success;
646        }
647        part = parts[index];
648    } else {
649        //printf("not navigation: %s\n", part.string());
650    }
651
652    if (getScreenSizeName(part.string())) {
653        size = part;
654
655        index++;
656        if (index == N) {
657            goto success;
658        }
659        part = parts[index];
660    } else {
661        //printf("not screen size: %s\n", part.string());
662    }
663
664    if (getVersionName(part.string())) {
665        vers = part;
666
667        index++;
668        if (index == N) {
669            goto success;
670        }
671        part = parts[index];
672    } else {
673        //printf("not version: %s\n", part.string());
674    }
675
676    // if there are extra parts, it doesn't match
677    return false;
678
679success:
680    this->mcc = mcc;
681    this->mnc = mnc;
682    this->locale = loc;
683    this->screenLayoutSize = layoutsize;
684    this->screenLayoutLong = layoutlong;
685    this->smallestScreenWidthDp = smallestwidthdp;
686    this->screenWidthDp = widthdp;
687    this->screenHeightDp = heightdp;
688    this->orientation = orient;
689    this->uiModeType = uiModeType;
690    this->uiModeNight = uiModeNight;
691    this->density = den;
692    this->touchscreen = touch;
693    this->keysHidden = keysHidden;
694    this->keyboard = key;
695    this->navHidden = navHidden;
696    this->navigation = nav;
697    this->screenSize = size;
698    this->layoutDirection = layoutDir;
699    this->version = vers;
700
701    // what is this anyway?
702    this->vendor = "";
703
704    return true;
705}
706
707String8
708AaptGroupEntry::toString() const
709{
710    String8 s = this->mcc;
711    s += ",";
712    s += this->mnc;
713    s += ",";
714    s += this->locale;
715    s += ",";
716    s += layoutDirection;
717    s += ",";
718    s += smallestScreenWidthDp;
719    s += ",";
720    s += screenWidthDp;
721    s += ",";
722    s += screenHeightDp;
723    s += ",";
724    s += screenLayoutSize;
725    s += ",";
726    s += screenLayoutLong;
727    s += ",";
728    s += this->orientation;
729    s += ",";
730    s += uiModeType;
731    s += ",";
732    s += uiModeNight;
733    s += ",";
734    s += density;
735    s += ",";
736    s += touchscreen;
737    s += ",";
738    s += keysHidden;
739    s += ",";
740    s += keyboard;
741    s += ",";
742    s += navHidden;
743    s += ",";
744    s += navigation;
745    s += ",";
746    s += screenSize;
747    s += ",";
748    s += version;
749    return s;
750}
751
752String8
753AaptGroupEntry::toDirName(const String8& resType) const
754{
755    String8 s = resType;
756    if (this->mcc != "") {
757        if (s.length() > 0) {
758            s += "-";
759        }
760        s += mcc;
761    }
762    if (this->mnc != "") {
763        if (s.length() > 0) {
764            s += "-";
765        }
766        s += mnc;
767    }
768    if (this->locale != "") {
769        if (s.length() > 0) {
770            s += "-";
771        }
772        s += locale;
773    }
774    if (this->layoutDirection != "") {
775        if (s.length() > 0) {
776            s += "-";
777        }
778        s += layoutDirection;
779    }
780    if (this->smallestScreenWidthDp != "") {
781        if (s.length() > 0) {
782            s += "-";
783        }
784        s += smallestScreenWidthDp;
785    }
786    if (this->screenWidthDp != "") {
787        if (s.length() > 0) {
788            s += "-";
789        }
790        s += screenWidthDp;
791    }
792    if (this->screenHeightDp != "") {
793        if (s.length() > 0) {
794            s += "-";
795        }
796        s += screenHeightDp;
797    }
798    if (this->screenLayoutSize != "") {
799        if (s.length() > 0) {
800            s += "-";
801        }
802        s += screenLayoutSize;
803    }
804    if (this->screenLayoutLong != "") {
805        if (s.length() > 0) {
806            s += "-";
807        }
808        s += screenLayoutLong;
809    }
810    if (this->orientation != "") {
811        if (s.length() > 0) {
812            s += "-";
813        }
814        s += orientation;
815    }
816    if (this->uiModeType != "") {
817        if (s.length() > 0) {
818            s += "-";
819        }
820        s += uiModeType;
821    }
822    if (this->uiModeNight != "") {
823        if (s.length() > 0) {
824            s += "-";
825        }
826        s += uiModeNight;
827    }
828    if (this->density != "") {
829        if (s.length() > 0) {
830            s += "-";
831        }
832        s += density;
833    }
834    if (this->touchscreen != "") {
835        if (s.length() > 0) {
836            s += "-";
837        }
838        s += touchscreen;
839    }
840    if (this->keysHidden != "") {
841        if (s.length() > 0) {
842            s += "-";
843        }
844        s += keysHidden;
845    }
846    if (this->keyboard != "") {
847        if (s.length() > 0) {
848            s += "-";
849        }
850        s += keyboard;
851    }
852    if (this->navHidden != "") {
853        if (s.length() > 0) {
854            s += "-";
855        }
856        s += navHidden;
857    }
858    if (this->navigation != "") {
859        if (s.length() > 0) {
860            s += "-";
861        }
862        s += navigation;
863    }
864    if (this->screenSize != "") {
865        if (s.length() > 0) {
866            s += "-";
867        }
868        s += screenSize;
869    }
870    if (this->version != "") {
871        if (s.length() > 0) {
872            s += "-";
873        }
874        s += version;
875    }
876
877    return s;
878}
879
880bool AaptGroupEntry::getMccName(const char* name,
881                                    ResTable_config* out)
882{
883    if (strcmp(name, kWildcardName) == 0) {
884        if (out) out->mcc = 0;
885        return true;
886    }
887    const char* c = name;
888    if (tolower(*c) != 'm') return false;
889    c++;
890    if (tolower(*c) != 'c') return false;
891    c++;
892    if (tolower(*c) != 'c') return false;
893    c++;
894
895    const char* val = c;
896
897    while (*c >= '0' && *c <= '9') {
898        c++;
899    }
900    if (*c != 0) return false;
901    if (c-val != 3) return false;
902
903    int d = atoi(val);
904    if (d != 0) {
905        if (out) out->mcc = d;
906        return true;
907    }
908
909    return false;
910}
911
912bool AaptGroupEntry::getMncName(const char* name,
913                                    ResTable_config* out)
914{
915    if (strcmp(name, kWildcardName) == 0) {
916        if (out) out->mcc = 0;
917        return true;
918    }
919    const char* c = name;
920    if (tolower(*c) != 'm') return false;
921    c++;
922    if (tolower(*c) != 'n') return false;
923    c++;
924    if (tolower(*c) != 'c') return false;
925    c++;
926
927    const char* val = c;
928
929    while (*c >= '0' && *c <= '9') {
930        c++;
931    }
932    if (*c != 0) return false;
933    if (c-val == 0 || c-val > 3) return false;
934
935    if (out) {
936        out->mnc = atoi(val);
937        if (out->mnc == 0) {
938            out->mnc = ACONFIGURATION_MNC_ZERO;
939        }
940    }
941
942    return true;
943}
944
945/*
946 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
947 * "default")?
948 *
949 * TODO: Should insist that the first two letters are lower case, and the
950 * second two are upper.
951 */
952bool AaptGroupEntry::getLocaleName(const char* fileName,
953                                   ResTable_config* out)
954{
955    if (strcmp(fileName, kWildcardName) == 0
956            || strcmp(fileName, kDefaultLocale) == 0) {
957        if (out) {
958            out->language[0] = 0;
959            out->language[1] = 0;
960            out->country[0] = 0;
961            out->country[1] = 0;
962        }
963        return true;
964    }
965
966    if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
967        if (out) {
968            out->language[0] = fileName[0];
969            out->language[1] = fileName[1];
970            out->country[0] = 0;
971            out->country[1] = 0;
972        }
973        return true;
974    }
975
976    if (strlen(fileName) == 5 &&
977        isalpha(fileName[0]) &&
978        isalpha(fileName[1]) &&
979        fileName[2] == '-' &&
980        isalpha(fileName[3]) &&
981        isalpha(fileName[4])) {
982        if (out) {
983            out->language[0] = fileName[0];
984            out->language[1] = fileName[1];
985            out->country[0] = fileName[3];
986            out->country[1] = fileName[4];
987        }
988        return true;
989    }
990
991    return false;
992}
993
994bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
995{
996    if (strcmp(name, kWildcardName) == 0) {
997        if (out) out->screenLayout =
998                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
999                | ResTable_config::LAYOUTDIR_ANY;
1000        return true;
1001    } else if (strcmp(name, "ldltr") == 0) {
1002        if (out) out->screenLayout =
1003                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1004                | ResTable_config::LAYOUTDIR_LTR;
1005        return true;
1006    } else if (strcmp(name, "ldrtl") == 0) {
1007        if (out) out->screenLayout =
1008                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1009                | ResTable_config::LAYOUTDIR_RTL;
1010        return true;
1011    }
1012
1013    return false;
1014}
1015
1016bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
1017                                     ResTable_config* out)
1018{
1019    if (strcmp(name, kWildcardName) == 0) {
1020        if (out) out->screenLayout =
1021                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1022                | ResTable_config::SCREENSIZE_ANY;
1023        return true;
1024    } else if (strcmp(name, "small") == 0) {
1025        if (out) out->screenLayout =
1026                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1027                | ResTable_config::SCREENSIZE_SMALL;
1028        return true;
1029    } else if (strcmp(name, "normal") == 0) {
1030        if (out) out->screenLayout =
1031                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1032                | ResTable_config::SCREENSIZE_NORMAL;
1033        return true;
1034    } else if (strcmp(name, "large") == 0) {
1035        if (out) out->screenLayout =
1036                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1037                | ResTable_config::SCREENSIZE_LARGE;
1038        return true;
1039    } else if (strcmp(name, "xlarge") == 0) {
1040        if (out) out->screenLayout =
1041                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1042                | ResTable_config::SCREENSIZE_XLARGE;
1043        return true;
1044    }
1045
1046    return false;
1047}
1048
1049bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
1050                                     ResTable_config* out)
1051{
1052    if (strcmp(name, kWildcardName) == 0) {
1053        if (out) out->screenLayout =
1054                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1055                | ResTable_config::SCREENLONG_ANY;
1056        return true;
1057    } else if (strcmp(name, "long") == 0) {
1058        if (out) out->screenLayout =
1059                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1060                | ResTable_config::SCREENLONG_YES;
1061        return true;
1062    } else if (strcmp(name, "notlong") == 0) {
1063        if (out) out->screenLayout =
1064                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1065                | ResTable_config::SCREENLONG_NO;
1066        return true;
1067    }
1068
1069    return false;
1070}
1071
1072bool AaptGroupEntry::getOrientationName(const char* name,
1073                                        ResTable_config* out)
1074{
1075    if (strcmp(name, kWildcardName) == 0) {
1076        if (out) out->orientation = out->ORIENTATION_ANY;
1077        return true;
1078    } else if (strcmp(name, "port") == 0) {
1079        if (out) out->orientation = out->ORIENTATION_PORT;
1080        return true;
1081    } else if (strcmp(name, "land") == 0) {
1082        if (out) out->orientation = out->ORIENTATION_LAND;
1083        return true;
1084    } else if (strcmp(name, "square") == 0) {
1085        if (out) out->orientation = out->ORIENTATION_SQUARE;
1086        return true;
1087    }
1088
1089    return false;
1090}
1091
1092bool AaptGroupEntry::getUiModeTypeName(const char* name,
1093                                       ResTable_config* out)
1094{
1095    if (strcmp(name, kWildcardName) == 0) {
1096        if (out) out->uiMode =
1097                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1098                | ResTable_config::UI_MODE_TYPE_ANY;
1099        return true;
1100    } else if (strcmp(name, "desk") == 0) {
1101      if (out) out->uiMode =
1102              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1103              | ResTable_config::UI_MODE_TYPE_DESK;
1104        return true;
1105    } else if (strcmp(name, "car") == 0) {
1106      if (out) out->uiMode =
1107              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1108              | ResTable_config::UI_MODE_TYPE_CAR;
1109        return true;
1110    } else if (strcmp(name, "television") == 0) {
1111      if (out) out->uiMode =
1112              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1113              | ResTable_config::UI_MODE_TYPE_TELEVISION;
1114        return true;
1115    } else if (strcmp(name, "appliance") == 0) {
1116      if (out) out->uiMode =
1117              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1118              | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1119        return true;
1120    } else if (strcmp(name, "watch") == 0) {
1121      if (out) out->uiMode =
1122              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1123              | ResTable_config::UI_MODE_TYPE_WATCH;
1124        return true;
1125    }
1126
1127    return false;
1128}
1129
1130bool AaptGroupEntry::getUiModeNightName(const char* name,
1131                                          ResTable_config* out)
1132{
1133    if (strcmp(name, kWildcardName) == 0) {
1134        if (out) out->uiMode =
1135                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1136                | ResTable_config::UI_MODE_NIGHT_ANY;
1137        return true;
1138    } else if (strcmp(name, "night") == 0) {
1139        if (out) out->uiMode =
1140                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1141                | ResTable_config::UI_MODE_NIGHT_YES;
1142        return true;
1143    } else if (strcmp(name, "notnight") == 0) {
1144      if (out) out->uiMode =
1145              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1146              | ResTable_config::UI_MODE_NIGHT_NO;
1147        return true;
1148    }
1149
1150    return false;
1151}
1152
1153bool AaptGroupEntry::getDensityName(const char* name,
1154                                    ResTable_config* out)
1155{
1156    if (strcmp(name, kWildcardName) == 0) {
1157        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1158        return true;
1159    }
1160
1161    if (strcmp(name, "nodpi") == 0) {
1162        if (out) out->density = ResTable_config::DENSITY_NONE;
1163        return true;
1164    }
1165
1166    if (strcmp(name, "ldpi") == 0) {
1167        if (out) out->density = ResTable_config::DENSITY_LOW;
1168        return true;
1169    }
1170
1171    if (strcmp(name, "mdpi") == 0) {
1172        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1173        return true;
1174    }
1175
1176    if (strcmp(name, "tvdpi") == 0) {
1177        if (out) out->density = ResTable_config::DENSITY_TV;
1178        return true;
1179    }
1180
1181    if (strcmp(name, "hdpi") == 0) {
1182        if (out) out->density = ResTable_config::DENSITY_HIGH;
1183        return true;
1184    }
1185
1186    if (strcmp(name, "xhdpi") == 0) {
1187        if (out) out->density = ResTable_config::DENSITY_XHIGH;
1188        return true;
1189    }
1190
1191    if (strcmp(name, "xxhdpi") == 0) {
1192        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1193        return true;
1194    }
1195
1196    if (strcmp(name, "xxxhdpi") == 0) {
1197        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1198        return true;
1199    }
1200
1201    char* c = (char*)name;
1202    while (*c >= '0' && *c <= '9') {
1203        c++;
1204    }
1205
1206    // check that we have 'dpi' after the last digit.
1207    if (toupper(c[0]) != 'D' ||
1208            toupper(c[1]) != 'P' ||
1209            toupper(c[2]) != 'I' ||
1210            c[3] != 0) {
1211        return false;
1212    }
1213
1214    // temporarily replace the first letter with \0 to
1215    // use atoi.
1216    char tmp = c[0];
1217    c[0] = '\0';
1218
1219    int d = atoi(name);
1220    c[0] = tmp;
1221
1222    if (d != 0) {
1223        if (out) out->density = d;
1224        return true;
1225    }
1226
1227    return false;
1228}
1229
1230bool AaptGroupEntry::getTouchscreenName(const char* name,
1231                                        ResTable_config* out)
1232{
1233    if (strcmp(name, kWildcardName) == 0) {
1234        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1235        return true;
1236    } else if (strcmp(name, "notouch") == 0) {
1237        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1238        return true;
1239    } else if (strcmp(name, "stylus") == 0) {
1240        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1241        return true;
1242    } else if (strcmp(name, "finger") == 0) {
1243        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1244        return true;
1245    }
1246
1247    return false;
1248}
1249
1250bool AaptGroupEntry::getKeysHiddenName(const char* name,
1251                                       ResTable_config* out)
1252{
1253    uint8_t mask = 0;
1254    uint8_t value = 0;
1255    if (strcmp(name, kWildcardName) == 0) {
1256        mask = ResTable_config::MASK_KEYSHIDDEN;
1257        value = ResTable_config::KEYSHIDDEN_ANY;
1258    } else if (strcmp(name, "keysexposed") == 0) {
1259        mask = ResTable_config::MASK_KEYSHIDDEN;
1260        value = ResTable_config::KEYSHIDDEN_NO;
1261    } else if (strcmp(name, "keyshidden") == 0) {
1262        mask = ResTable_config::MASK_KEYSHIDDEN;
1263        value = ResTable_config::KEYSHIDDEN_YES;
1264    } else if (strcmp(name, "keyssoft") == 0) {
1265        mask = ResTable_config::MASK_KEYSHIDDEN;
1266        value = ResTable_config::KEYSHIDDEN_SOFT;
1267    }
1268
1269    if (mask != 0) {
1270        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1271        return true;
1272    }
1273
1274    return false;
1275}
1276
1277bool AaptGroupEntry::getKeyboardName(const char* name,
1278                                        ResTable_config* out)
1279{
1280    if (strcmp(name, kWildcardName) == 0) {
1281        if (out) out->keyboard = out->KEYBOARD_ANY;
1282        return true;
1283    } else if (strcmp(name, "nokeys") == 0) {
1284        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1285        return true;
1286    } else if (strcmp(name, "qwerty") == 0) {
1287        if (out) out->keyboard = out->KEYBOARD_QWERTY;
1288        return true;
1289    } else if (strcmp(name, "12key") == 0) {
1290        if (out) out->keyboard = out->KEYBOARD_12KEY;
1291        return true;
1292    }
1293
1294    return false;
1295}
1296
1297bool AaptGroupEntry::getNavHiddenName(const char* name,
1298                                       ResTable_config* out)
1299{
1300    uint8_t mask = 0;
1301    uint8_t value = 0;
1302    if (strcmp(name, kWildcardName) == 0) {
1303        mask = ResTable_config::MASK_NAVHIDDEN;
1304        value = ResTable_config::NAVHIDDEN_ANY;
1305    } else if (strcmp(name, "navexposed") == 0) {
1306        mask = ResTable_config::MASK_NAVHIDDEN;
1307        value = ResTable_config::NAVHIDDEN_NO;
1308    } else if (strcmp(name, "navhidden") == 0) {
1309        mask = ResTable_config::MASK_NAVHIDDEN;
1310        value = ResTable_config::NAVHIDDEN_YES;
1311    }
1312
1313    if (mask != 0) {
1314        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1315        return true;
1316    }
1317
1318    return false;
1319}
1320
1321bool AaptGroupEntry::getNavigationName(const char* name,
1322                                     ResTable_config* out)
1323{
1324    if (strcmp(name, kWildcardName) == 0) {
1325        if (out) out->navigation = out->NAVIGATION_ANY;
1326        return true;
1327    } else if (strcmp(name, "nonav") == 0) {
1328        if (out) out->navigation = out->NAVIGATION_NONAV;
1329        return true;
1330    } else if (strcmp(name, "dpad") == 0) {
1331        if (out) out->navigation = out->NAVIGATION_DPAD;
1332        return true;
1333    } else if (strcmp(name, "trackball") == 0) {
1334        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1335        return true;
1336    } else if (strcmp(name, "wheel") == 0) {
1337        if (out) out->navigation = out->NAVIGATION_WHEEL;
1338        return true;
1339    }
1340
1341    return false;
1342}
1343
1344bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1345{
1346    if (strcmp(name, kWildcardName) == 0) {
1347        if (out) {
1348            out->screenWidth = out->SCREENWIDTH_ANY;
1349            out->screenHeight = out->SCREENHEIGHT_ANY;
1350        }
1351        return true;
1352    }
1353
1354    const char* x = name;
1355    while (*x >= '0' && *x <= '9') x++;
1356    if (x == name || *x != 'x') return false;
1357    String8 xName(name, x-name);
1358    x++;
1359
1360    const char* y = x;
1361    while (*y >= '0' && *y <= '9') y++;
1362    if (y == name || *y != 0) return false;
1363    String8 yName(x, y-x);
1364
1365    uint16_t w = (uint16_t)atoi(xName.string());
1366    uint16_t h = (uint16_t)atoi(yName.string());
1367    if (w < h) {
1368        return false;
1369    }
1370
1371    if (out) {
1372        out->screenWidth = w;
1373        out->screenHeight = h;
1374    }
1375
1376    return true;
1377}
1378
1379bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1380{
1381    if (strcmp(name, kWildcardName) == 0) {
1382        if (out) {
1383            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1384        }
1385        return true;
1386    }
1387
1388    if (*name != 's') return false;
1389    name++;
1390    if (*name != 'w') return false;
1391    name++;
1392    const char* x = name;
1393    while (*x >= '0' && *x <= '9') x++;
1394    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1395    String8 xName(name, x-name);
1396
1397    if (out) {
1398        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1399    }
1400
1401    return true;
1402}
1403
1404bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1405{
1406    if (strcmp(name, kWildcardName) == 0) {
1407        if (out) {
1408            out->screenWidthDp = out->SCREENWIDTH_ANY;
1409        }
1410        return true;
1411    }
1412
1413    if (*name != 'w') return false;
1414    name++;
1415    const char* x = name;
1416    while (*x >= '0' && *x <= '9') x++;
1417    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1418    String8 xName(name, x-name);
1419
1420    if (out) {
1421        out->screenWidthDp = (uint16_t)atoi(xName.string());
1422    }
1423
1424    return true;
1425}
1426
1427bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1428{
1429    if (strcmp(name, kWildcardName) == 0) {
1430        if (out) {
1431            out->screenHeightDp = out->SCREENWIDTH_ANY;
1432        }
1433        return true;
1434    }
1435
1436    if (*name != 'h') return false;
1437    name++;
1438    const char* x = name;
1439    while (*x >= '0' && *x <= '9') x++;
1440    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1441    String8 xName(name, x-name);
1442
1443    if (out) {
1444        out->screenHeightDp = (uint16_t)atoi(xName.string());
1445    }
1446
1447    return true;
1448}
1449
1450bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1451{
1452    if (strcmp(name, kWildcardName) == 0) {
1453        if (out) {
1454            out->sdkVersion = out->SDKVERSION_ANY;
1455            out->minorVersion = out->MINORVERSION_ANY;
1456        }
1457        return true;
1458    }
1459
1460    if (*name != 'v') {
1461        return false;
1462    }
1463
1464    name++;
1465    const char* s = name;
1466    while (*s >= '0' && *s <= '9') s++;
1467    if (s == name || *s != 0) return false;
1468    String8 sdkName(name, s-name);
1469
1470    if (out) {
1471        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1472        out->minorVersion = 0;
1473    }
1474
1475    return true;
1476}
1477
1478int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1479{
1480    int v = mcc.compare(o.mcc);
1481    if (v == 0) v = mnc.compare(o.mnc);
1482    if (v == 0) v = locale.compare(o.locale);
1483    if (v == 0) v = layoutDirection.compare(o.layoutDirection);
1484    if (v == 0) v = vendor.compare(o.vendor);
1485    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1486    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1487    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1488    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1489    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1490    if (v == 0) v = orientation.compare(o.orientation);
1491    if (v == 0) v = uiModeType.compare(o.uiModeType);
1492    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1493    if (v == 0) v = density.compare(o.density);
1494    if (v == 0) v = touchscreen.compare(o.touchscreen);
1495    if (v == 0) v = keysHidden.compare(o.keysHidden);
1496    if (v == 0) v = keyboard.compare(o.keyboard);
1497    if (v == 0) v = navHidden.compare(o.navHidden);
1498    if (v == 0) v = navigation.compare(o.navigation);
1499    if (v == 0) v = screenSize.compare(o.screenSize);
1500    if (v == 0) v = version.compare(o.version);
1501    return v;
1502}
1503
1504const ResTable_config& AaptGroupEntry::toParams() const
1505{
1506    if (!mParamsChanged) {
1507        return mParams;
1508    }
1509
1510    mParamsChanged = false;
1511    ResTable_config& params(mParams);
1512    memset(&params, 0, sizeof(params));
1513    getMccName(mcc.string(), &params);
1514    getMncName(mnc.string(), &params);
1515    getLocaleName(locale.string(), &params);
1516    getLayoutDirectionName(layoutDirection.string(), &params);
1517    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1518    getScreenWidthDpName(screenWidthDp.string(), &params);
1519    getScreenHeightDpName(screenHeightDp.string(), &params);
1520    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1521    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1522    getOrientationName(orientation.string(), &params);
1523    getUiModeTypeName(uiModeType.string(), &params);
1524    getUiModeNightName(uiModeNight.string(), &params);
1525    getDensityName(density.string(), &params);
1526    getTouchscreenName(touchscreen.string(), &params);
1527    getKeysHiddenName(keysHidden.string(), &params);
1528    getKeyboardName(keyboard.string(), &params);
1529    getNavHiddenName(navHidden.string(), &params);
1530    getNavigationName(navigation.string(), &params);
1531    getScreenSizeName(screenSize.string(), &params);
1532    getVersionName(version.string(), &params);
1533
1534    // Fix up version number based on specified parameters.
1535    int minSdk = 0;
1536    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1537            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1538            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1539        minSdk = SDK_HONEYCOMB_MR2;
1540    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1541                != ResTable_config::UI_MODE_TYPE_ANY
1542            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1543                != ResTable_config::UI_MODE_NIGHT_ANY) {
1544        minSdk = SDK_FROYO;
1545    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1546                != ResTable_config::SCREENSIZE_ANY
1547            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1548                != ResTable_config::SCREENLONG_ANY
1549            || params.density != ResTable_config::DENSITY_DEFAULT) {
1550        minSdk = SDK_DONUT;
1551    }
1552
1553    if (minSdk > params.sdkVersion) {
1554        params.sdkVersion = minSdk;
1555    }
1556
1557    return params;
1558}
1559
1560// =========================================================================
1561// =========================================================================
1562// =========================================================================
1563
1564void* AaptFile::editData(size_t size)
1565{
1566    if (size <= mBufferSize) {
1567        mDataSize = size;
1568        return mData;
1569    }
1570    size_t allocSize = (size*3)/2;
1571    void* buf = realloc(mData, allocSize);
1572    if (buf == NULL) {
1573        return NULL;
1574    }
1575    mData = buf;
1576    mDataSize = size;
1577    mBufferSize = allocSize;
1578    return buf;
1579}
1580
1581void* AaptFile::editData(size_t* outSize)
1582{
1583    if (outSize) {
1584        *outSize = mDataSize;
1585    }
1586    return mData;
1587}
1588
1589void* AaptFile::padData(size_t wordSize)
1590{
1591    const size_t extra = mDataSize%wordSize;
1592    if (extra == 0) {
1593        return mData;
1594    }
1595
1596    size_t initial = mDataSize;
1597    void* data = editData(initial+(wordSize-extra));
1598    if (data != NULL) {
1599        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1600    }
1601    return data;
1602}
1603
1604status_t AaptFile::writeData(const void* data, size_t size)
1605{
1606    size_t end = mDataSize;
1607    size_t total = size + end;
1608    void* buf = editData(total);
1609    if (buf == NULL) {
1610        return UNKNOWN_ERROR;
1611    }
1612    memcpy(((char*)buf)+end, data, size);
1613    return NO_ERROR;
1614}
1615
1616void AaptFile::clearData()
1617{
1618    if (mData != NULL) free(mData);
1619    mData = NULL;
1620    mDataSize = 0;
1621    mBufferSize = 0;
1622}
1623
1624String8 AaptFile::getPrintableSource() const
1625{
1626    if (hasData()) {
1627        String8 name(mGroupEntry.toDirName(String8()));
1628        name.appendPath(mPath);
1629        name.append(" #generated");
1630        return name;
1631    }
1632    return mSourceFile;
1633}
1634
1635// =========================================================================
1636// =========================================================================
1637// =========================================================================
1638
1639status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
1640{
1641    ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
1642    if (index >= 0 && overwriteDuplicate) {
1643        fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
1644                mFiles[index]->getSourceFile().string(),
1645                file->getSourceFile().string());
1646        removeFile(index);
1647        index = -1;
1648    }
1649
1650    if (index < 0) {
1651        file->mPath = mPath;
1652        mFiles.add(file->getGroupEntry(), file);
1653        return NO_ERROR;
1654    }
1655
1656#if 0
1657    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1658            file->getSourceFile().string(),
1659            file->getGroupEntry().toDirName(String8()).string(),
1660            mLeaf.string(), mPath.string());
1661#endif
1662
1663    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1664                                               getPrintableSource().string());
1665    return UNKNOWN_ERROR;
1666}
1667
1668void AaptGroup::removeFile(size_t index)
1669{
1670	mFiles.removeItemsAt(index);
1671}
1672
1673void AaptGroup::print(const String8& prefix) const
1674{
1675    printf("%s%s\n", prefix.string(), getPath().string());
1676    const size_t N=mFiles.size();
1677    size_t i;
1678    for (i=0; i<N; i++) {
1679        sp<AaptFile> file = mFiles.valueAt(i);
1680        const AaptGroupEntry& e = file->getGroupEntry();
1681        if (file->hasData()) {
1682            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1683                    (int)file->getSize());
1684        } else {
1685            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1686                    file->getPrintableSource().string());
1687        }
1688        //printf("%s  File Group Entry: %s\n", prefix.string(),
1689        //        file->getGroupEntry().toDirName(String8()).string());
1690    }
1691}
1692
1693String8 AaptGroup::getPrintableSource() const
1694{
1695    if (mFiles.size() > 0) {
1696        // Arbitrarily pull the first source file out of the list.
1697        return mFiles.valueAt(0)->getPrintableSource();
1698    }
1699
1700    // Should never hit this case, but to be safe...
1701    return getPath();
1702
1703}
1704
1705// =========================================================================
1706// =========================================================================
1707// =========================================================================
1708
1709status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1710{
1711    if (mFiles.indexOfKey(name) >= 0) {
1712        return ALREADY_EXISTS;
1713    }
1714    mFiles.add(name, file);
1715    return NO_ERROR;
1716}
1717
1718status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1719{
1720    if (mDirs.indexOfKey(name) >= 0) {
1721        return ALREADY_EXISTS;
1722    }
1723    mDirs.add(name, dir);
1724    return NO_ERROR;
1725}
1726
1727sp<AaptDir> AaptDir::makeDir(const String8& path)
1728{
1729    String8 name;
1730    String8 remain = path;
1731
1732    sp<AaptDir> subdir = this;
1733    while (name = remain.walkPath(&remain), remain != "") {
1734        subdir = subdir->makeDir(name);
1735    }
1736
1737    ssize_t i = subdir->mDirs.indexOfKey(name);
1738    if (i >= 0) {
1739        return subdir->mDirs.valueAt(i);
1740    }
1741    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1742    subdir->mDirs.add(name, dir);
1743    return dir;
1744}
1745
1746void AaptDir::removeFile(const String8& name)
1747{
1748    mFiles.removeItem(name);
1749}
1750
1751void AaptDir::removeDir(const String8& name)
1752{
1753    mDirs.removeItem(name);
1754}
1755
1756status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
1757        const bool overwrite)
1758{
1759    sp<AaptGroup> group;
1760    if (mFiles.indexOfKey(leafName) >= 0) {
1761        group = mFiles.valueFor(leafName);
1762    } else {
1763        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1764        mFiles.add(leafName, group);
1765    }
1766
1767    return group->addFile(file, overwrite);
1768}
1769
1770ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1771                            const AaptGroupEntry& kind, const String8& resType,
1772                            sp<FilePathStore>& fullResPaths, const bool overwrite)
1773{
1774    Vector<String8> fileNames;
1775    {
1776        DIR* dir = NULL;
1777
1778        dir = opendir(srcDir.string());
1779        if (dir == NULL) {
1780            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1781            return UNKNOWN_ERROR;
1782        }
1783
1784        /*
1785         * Slurp the filenames out of the directory.
1786         */
1787        while (1) {
1788            struct dirent* entry;
1789
1790            entry = readdir(dir);
1791            if (entry == NULL)
1792                break;
1793
1794            if (isHidden(srcDir.string(), entry->d_name))
1795                continue;
1796
1797            String8 name(entry->d_name);
1798            fileNames.add(name);
1799            // Add fully qualified path for dependency purposes
1800            // if we're collecting them
1801            if (fullResPaths != NULL) {
1802                fullResPaths->add(srcDir.appendPathCopy(name));
1803            }
1804        }
1805        closedir(dir);
1806    }
1807
1808    ssize_t count = 0;
1809
1810    /*
1811     * Stash away the files and recursively descend into subdirectories.
1812     */
1813    const size_t N = fileNames.size();
1814    size_t i;
1815    for (i = 0; i < N; i++) {
1816        String8 pathName(srcDir);
1817        FileType type;
1818
1819        pathName.appendPath(fileNames[i].string());
1820        type = getFileType(pathName.string());
1821        if (type == kFileTypeDirectory) {
1822            sp<AaptDir> subdir;
1823            bool notAdded = false;
1824            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1825                subdir = mDirs.valueFor(fileNames[i]);
1826            } else {
1827                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1828                notAdded = true;
1829            }
1830            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1831                                                resType, fullResPaths, overwrite);
1832            if (res < NO_ERROR) {
1833                return res;
1834            }
1835            if (res > 0 && notAdded) {
1836                mDirs.add(fileNames[i], subdir);
1837            }
1838            count += res;
1839        } else if (type == kFileTypeRegular) {
1840            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1841            status_t err = addLeafFile(fileNames[i], file, overwrite);
1842            if (err != NO_ERROR) {
1843                return err;
1844            }
1845
1846            count++;
1847
1848        } else {
1849            if (bundle->getVerbose())
1850                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1851        }
1852    }
1853
1854    return count;
1855}
1856
1857status_t AaptDir::validate() const
1858{
1859    const size_t NF = mFiles.size();
1860    const size_t ND = mDirs.size();
1861    size_t i;
1862    for (i = 0; i < NF; i++) {
1863        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1864            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1865                    "Invalid filename.  Unable to add.");
1866            return UNKNOWN_ERROR;
1867        }
1868
1869        size_t j;
1870        for (j = i+1; j < NF; j++) {
1871            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1872                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1873                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1874                        "File is case-insensitive equivalent to: %s",
1875                        mFiles.valueAt(j)->getPrintableSource().string());
1876                return UNKNOWN_ERROR;
1877            }
1878
1879            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1880            // (this is mostly caught by the "marked" stuff, below)
1881        }
1882
1883        for (j = 0; j < ND; j++) {
1884            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1885                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1886                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1887                        "File conflicts with dir from: %s",
1888                        mDirs.valueAt(j)->getPrintableSource().string());
1889                return UNKNOWN_ERROR;
1890            }
1891        }
1892    }
1893
1894    for (i = 0; i < ND; i++) {
1895        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1896            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1897                    "Invalid directory name, unable to add.");
1898            return UNKNOWN_ERROR;
1899        }
1900
1901        size_t j;
1902        for (j = i+1; j < ND; j++) {
1903            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1904                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1905                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1906                        "Directory is case-insensitive equivalent to: %s",
1907                        mDirs.valueAt(j)->getPrintableSource().string());
1908                return UNKNOWN_ERROR;
1909            }
1910        }
1911
1912        status_t err = mDirs.valueAt(i)->validate();
1913        if (err != NO_ERROR) {
1914            return err;
1915        }
1916    }
1917
1918    return NO_ERROR;
1919}
1920
1921void AaptDir::print(const String8& prefix) const
1922{
1923    const size_t ND=getDirs().size();
1924    size_t i;
1925    for (i=0; i<ND; i++) {
1926        getDirs().valueAt(i)->print(prefix);
1927    }
1928
1929    const size_t NF=getFiles().size();
1930    for (i=0; i<NF; i++) {
1931        getFiles().valueAt(i)->print(prefix);
1932    }
1933}
1934
1935String8 AaptDir::getPrintableSource() const
1936{
1937    if (mFiles.size() > 0) {
1938        // Arbitrarily pull the first file out of the list as the source dir.
1939        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1940    }
1941    if (mDirs.size() > 0) {
1942        // Or arbitrarily pull the first dir out of the list as the source dir.
1943        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1944    }
1945
1946    // Should never hit this case, but to be safe...
1947    return mPath;
1948
1949}
1950
1951// =========================================================================
1952// =========================================================================
1953// =========================================================================
1954
1955status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
1956{
1957    status_t err = NO_ERROR;
1958    size_t N = javaSymbols->mSymbols.size();
1959    for (size_t i=0; i<N; i++) {
1960        const String8& name = javaSymbols->mSymbols.keyAt(i);
1961        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
1962        ssize_t pos = mSymbols.indexOfKey(name);
1963        if (pos < 0) {
1964            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
1965            err = UNKNOWN_ERROR;
1966            continue;
1967        }
1968        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
1969        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
1970        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
1971    }
1972
1973    N = javaSymbols->mNestedSymbols.size();
1974    for (size_t i=0; i<N; i++) {
1975        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
1976        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
1977        ssize_t pos = mNestedSymbols.indexOfKey(name);
1978        if (pos < 0) {
1979            SourcePos pos;
1980            pos.error("Java symbol dir %s not defined\n", name.string());
1981            err = UNKNOWN_ERROR;
1982            continue;
1983        }
1984        //printf("**** applying java symbols in dir %s\n", name.string());
1985        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1986        if (myerr != NO_ERROR) {
1987            err = myerr;
1988        }
1989    }
1990
1991    return err;
1992}
1993
1994// =========================================================================
1995// =========================================================================
1996// =========================================================================
1997
1998AaptAssets::AaptAssets()
1999    : AaptDir(String8(), String8()),
2000      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
2001{
2002}
2003
2004const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2005    if (mChanged) {
2006    }
2007    return mGroupEntries;
2008}
2009
2010status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2011{
2012    mChanged = true;
2013    return AaptDir::addFile(name, file);
2014}
2015
2016sp<AaptFile> AaptAssets::addFile(
2017        const String8& filePath, const AaptGroupEntry& entry,
2018        const String8& srcDir, sp<AaptGroup>* outGroup,
2019        const String8& resType)
2020{
2021    sp<AaptDir> dir = this;
2022    sp<AaptGroup> group;
2023    sp<AaptFile> file;
2024    String8 root, remain(filePath), partialPath;
2025    while (remain.length() > 0) {
2026        root = remain.walkPath(&remain);
2027        partialPath.appendPath(root);
2028
2029        const String8 rootStr(root);
2030
2031        if (remain.length() == 0) {
2032            ssize_t i = dir->getFiles().indexOfKey(rootStr);
2033            if (i >= 0) {
2034                group = dir->getFiles().valueAt(i);
2035            } else {
2036                group = new AaptGroup(rootStr, filePath);
2037                status_t res = dir->addFile(rootStr, group);
2038                if (res != NO_ERROR) {
2039                    return NULL;
2040                }
2041            }
2042            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2043            status_t res = group->addFile(file);
2044            if (res != NO_ERROR) {
2045                return NULL;
2046            }
2047            break;
2048
2049        } else {
2050            ssize_t i = dir->getDirs().indexOfKey(rootStr);
2051            if (i >= 0) {
2052                dir = dir->getDirs().valueAt(i);
2053            } else {
2054                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2055                status_t res = dir->addDir(rootStr, subdir);
2056                if (res != NO_ERROR) {
2057                    return NULL;
2058                }
2059                dir = subdir;
2060            }
2061        }
2062    }
2063
2064    mGroupEntries.add(entry);
2065    if (outGroup) *outGroup = group;
2066    return file;
2067}
2068
2069void AaptAssets::addResource(const String8& leafName, const String8& path,
2070                const sp<AaptFile>& file, const String8& resType)
2071{
2072    sp<AaptDir> res = AaptDir::makeDir(kResString);
2073    String8 dirname = file->getGroupEntry().toDirName(resType);
2074    sp<AaptDir> subdir = res->makeDir(dirname);
2075    sp<AaptGroup> grr = new AaptGroup(leafName, path);
2076    grr->addFile(file);
2077
2078    subdir->addFile(leafName, grr);
2079}
2080
2081
2082ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2083{
2084    int count;
2085    int totalCount = 0;
2086    FileType type;
2087    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2088    const size_t dirCount =resDirs.size();
2089    sp<AaptAssets> current = this;
2090
2091    const int N = bundle->getFileSpecCount();
2092
2093    /*
2094     * If a package manifest was specified, include that first.
2095     */
2096    if (bundle->getAndroidManifestFile() != NULL) {
2097        // place at root of zip.
2098        String8 srcFile(bundle->getAndroidManifestFile());
2099        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2100                NULL, String8());
2101        totalCount++;
2102    }
2103
2104    /*
2105     * If a directory of custom assets was supplied, slurp 'em up.
2106     */
2107    const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
2108    const int AN = assetDirs.size();
2109    for (int i = 0; i < AN; i++) {
2110        FileType type = getFileType(assetDirs[i]);
2111        if (type == kFileTypeNonexistent) {
2112            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
2113            return UNKNOWN_ERROR;
2114        }
2115        if (type != kFileTypeDirectory) {
2116            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
2117            return UNKNOWN_ERROR;
2118        }
2119
2120        String8 assetRoot(assetDirs[i]);
2121        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2122        AaptGroupEntry group;
2123        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2124                                            String8(), mFullAssetPaths, true);
2125        if (count < 0) {
2126            totalCount = count;
2127            goto bail;
2128        }
2129        if (count > 0) {
2130            mGroupEntries.add(group);
2131        }
2132        totalCount += count;
2133
2134        if (bundle->getVerbose()) {
2135            printf("Found %d custom asset file%s in %s\n",
2136                   count, (count==1) ? "" : "s", assetDirs[i]);
2137        }
2138    }
2139
2140    /*
2141     * If a directory of resource-specific assets was supplied, slurp 'em up.
2142     */
2143    for (size_t i=0; i<dirCount; i++) {
2144        const char *res = resDirs[i];
2145        if (res) {
2146            type = getFileType(res);
2147            if (type == kFileTypeNonexistent) {
2148                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2149                return UNKNOWN_ERROR;
2150            }
2151            if (type == kFileTypeDirectory) {
2152                if (i>0) {
2153                    sp<AaptAssets> nextOverlay = new AaptAssets();
2154                    current->setOverlay(nextOverlay);
2155                    current = nextOverlay;
2156                    current->setFullResPaths(mFullResPaths);
2157                }
2158                count = current->slurpResourceTree(bundle, String8(res));
2159
2160                if (count < 0) {
2161                    totalCount = count;
2162                    goto bail;
2163                }
2164                totalCount += count;
2165            }
2166            else {
2167                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2168                return UNKNOWN_ERROR;
2169            }
2170        }
2171
2172    }
2173    /*
2174     * Now do any additional raw files.
2175     */
2176    for (int arg=0; arg<N; arg++) {
2177        const char* assetDir = bundle->getFileSpecEntry(arg);
2178
2179        FileType type = getFileType(assetDir);
2180        if (type == kFileTypeNonexistent) {
2181            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2182            return UNKNOWN_ERROR;
2183        }
2184        if (type != kFileTypeDirectory) {
2185            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2186            return UNKNOWN_ERROR;
2187        }
2188
2189        String8 assetRoot(assetDir);
2190
2191        if (bundle->getVerbose())
2192            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2193
2194        /*
2195         * Do a recursive traversal of subdir tree.  We don't make any
2196         * guarantees about ordering, so we're okay with an inorder search
2197         * using whatever order the OS happens to hand back to us.
2198         */
2199        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2200        if (count < 0) {
2201            /* failure; report error and remove archive */
2202            totalCount = count;
2203            goto bail;
2204        }
2205        totalCount += count;
2206
2207        if (bundle->getVerbose())
2208            printf("Found %d asset file%s in %s\n",
2209                   count, (count==1) ? "" : "s", assetDir);
2210    }
2211
2212    count = validate();
2213    if (count != NO_ERROR) {
2214        totalCount = count;
2215        goto bail;
2216    }
2217
2218    count = filter(bundle);
2219    if (count != NO_ERROR) {
2220        totalCount = count;
2221        goto bail;
2222    }
2223
2224bail:
2225    return totalCount;
2226}
2227
2228ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2229                                    const AaptGroupEntry& kind,
2230                                    const String8& resType,
2231                                    sp<FilePathStore>& fullResPaths)
2232{
2233    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2234    if (res > 0) {
2235        mGroupEntries.add(kind);
2236    }
2237
2238    return res;
2239}
2240
2241ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2242{
2243    ssize_t err = 0;
2244
2245    DIR* dir = opendir(srcDir.string());
2246    if (dir == NULL) {
2247        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2248        return UNKNOWN_ERROR;
2249    }
2250
2251    status_t count = 0;
2252
2253    /*
2254     * Run through the directory, looking for dirs that match the
2255     * expected pattern.
2256     */
2257    while (1) {
2258        struct dirent* entry = readdir(dir);
2259        if (entry == NULL) {
2260            break;
2261        }
2262
2263        if (isHidden(srcDir.string(), entry->d_name)) {
2264            continue;
2265        }
2266
2267        String8 subdirName(srcDir);
2268        subdirName.appendPath(entry->d_name);
2269
2270        AaptGroupEntry group;
2271        String8 resType;
2272        bool b = group.initFromDirName(entry->d_name, &resType);
2273        if (!b) {
2274            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2275                    entry->d_name);
2276            err = -1;
2277            continue;
2278        }
2279
2280        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2281            int maxResInt = atoi(bundle->getMaxResVersion());
2282            const char *verString = group.getVersionString().string();
2283            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2284            if (dirVersionInt > maxResInt) {
2285              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2286              continue;
2287            }
2288        }
2289
2290        FileType type = getFileType(subdirName.string());
2291
2292        if (type == kFileTypeDirectory) {
2293            sp<AaptDir> dir = makeDir(resType);
2294            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2295                                                resType, mFullResPaths);
2296            if (res < 0) {
2297                count = res;
2298                goto bail;
2299            }
2300            if (res > 0) {
2301                mGroupEntries.add(group);
2302                count += res;
2303            }
2304
2305            // Only add this directory if we don't already have a resource dir
2306            // for the current type.  This ensures that we only add the dir once
2307            // for all configs.
2308            sp<AaptDir> rdir = resDir(resType);
2309            if (rdir == NULL) {
2310                mResDirs.add(dir);
2311            }
2312        } else {
2313            if (bundle->getVerbose()) {
2314                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2315            }
2316        }
2317    }
2318
2319bail:
2320    closedir(dir);
2321    dir = NULL;
2322
2323    if (err != 0) {
2324        return err;
2325    }
2326    return count;
2327}
2328
2329ssize_t
2330AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2331{
2332    int count = 0;
2333    SortedVector<AaptGroupEntry> entries;
2334
2335    ZipFile* zip = new ZipFile;
2336    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2337    if (err != NO_ERROR) {
2338        fprintf(stderr, "error opening zip file %s\n", filename);
2339        count = err;
2340        delete zip;
2341        return -1;
2342    }
2343
2344    const int N = zip->getNumEntries();
2345    for (int i=0; i<N; i++) {
2346        ZipEntry* entry = zip->getEntryByIndex(i);
2347        if (entry->getDeleted()) {
2348            continue;
2349        }
2350
2351        String8 entryName(entry->getFileName());
2352
2353        String8 dirName = entryName.getPathDir();
2354        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2355
2356        String8 resType;
2357        AaptGroupEntry kind;
2358
2359        String8 remain;
2360        if (entryName.walkPath(&remain) == kResourceDir) {
2361            // these are the resources, pull their type out of the directory name
2362            kind.initFromDirName(remain.walkPath().string(), &resType);
2363        } else {
2364            // these are untyped and don't have an AaptGroupEntry
2365        }
2366        if (entries.indexOf(kind) < 0) {
2367            entries.add(kind);
2368            mGroupEntries.add(kind);
2369        }
2370
2371        // use the one from the zip file if they both exist.
2372        dir->removeFile(entryName.getPathLeaf());
2373
2374        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2375        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2376        if (err != NO_ERROR) {
2377            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2378            count = err;
2379            goto bail;
2380        }
2381        file->setCompressionMethod(entry->getCompressionMethod());
2382
2383#if 0
2384        if (entryName == "AndroidManifest.xml") {
2385            printf("AndroidManifest.xml\n");
2386        }
2387        printf("\n\nfile: %s\n", entryName.string());
2388#endif
2389
2390        size_t len = entry->getUncompressedLen();
2391        void* data = zip->uncompress(entry);
2392        void* buf = file->editData(len);
2393        memcpy(buf, data, len);
2394
2395#if 0
2396        const int OFF = 0;
2397        const unsigned char* p = (unsigned char*)data;
2398        const unsigned char* end = p+len;
2399        p += OFF;
2400        for (int i=0; i<32 && p < end; i++) {
2401            printf("0x%03x ", i*0x10 + OFF);
2402            for (int j=0; j<0x10 && p < end; j++) {
2403                printf(" %02x", *p);
2404                p++;
2405            }
2406            printf("\n");
2407        }
2408#endif
2409
2410        free(data);
2411
2412        count++;
2413    }
2414
2415bail:
2416    delete zip;
2417    return count;
2418}
2419
2420status_t AaptAssets::filter(Bundle* bundle)
2421{
2422    ResourceFilter reqFilter;
2423    status_t err = reqFilter.parse(bundle->getConfigurations());
2424    if (err != NO_ERROR) {
2425        return err;
2426    }
2427
2428    ResourceFilter prefFilter;
2429    err = prefFilter.parse(bundle->getPreferredConfigurations());
2430    if (err != NO_ERROR) {
2431        return err;
2432    }
2433
2434    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2435        return NO_ERROR;
2436    }
2437
2438    if (bundle->getVerbose()) {
2439        if (!reqFilter.isEmpty()) {
2440            printf("Applying required filter: %s\n",
2441                    bundle->getConfigurations());
2442        }
2443        if (!prefFilter.isEmpty()) {
2444            printf("Applying preferred filter: %s\n",
2445                    bundle->getPreferredConfigurations());
2446        }
2447    }
2448
2449    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2450    const size_t ND = resdirs.size();
2451    for (size_t i=0; i<ND; i++) {
2452        const sp<AaptDir>& dir = resdirs.itemAt(i);
2453        if (dir->getLeaf() == kValuesDir) {
2454            // The "value" dir is special since a single file defines
2455            // multiple resources, so we can not do filtering on the
2456            // files themselves.
2457            continue;
2458        }
2459        if (dir->getLeaf() == kMipmapDir) {
2460            // We also skip the "mipmap" directory, since the point of this
2461            // is to include all densities without stripping.  If you put
2462            // other configurations in here as well they won't be stripped
2463            // either...  So don't do that.  Seriously.  What is wrong with you?
2464            continue;
2465        }
2466
2467        const size_t NG = dir->getFiles().size();
2468        for (size_t j=0; j<NG; j++) {
2469            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2470
2471            // First remove any configurations we know we don't need.
2472            for (size_t k=0; k<grp->getFiles().size(); k++) {
2473                sp<AaptFile> file = grp->getFiles().valueAt(k);
2474                if (k == 0 && grp->getFiles().size() == 1) {
2475                    // If this is the only file left, we need to keep it.
2476                    // Otherwise the resource IDs we are using will be inconsistent
2477                    // with what we get when not stripping.  Sucky, but at least
2478                    // for now we can rely on the back-end doing another filtering
2479                    // pass to take this out and leave us with this resource name
2480                    // containing no entries.
2481                    continue;
2482                }
2483                if (file->getPath().getPathExtension() == ".xml") {
2484                    // We can't remove .xml files at this point, because when
2485                    // we parse them they may add identifier resources, so
2486                    // removing them can cause our resource identifiers to
2487                    // become inconsistent.
2488                    continue;
2489                }
2490                const ResTable_config& config(file->getGroupEntry().toParams());
2491                if (!reqFilter.match(config)) {
2492                    if (bundle->getVerbose()) {
2493                        printf("Pruning unneeded resource: %s\n",
2494                                file->getPrintableSource().string());
2495                    }
2496                    grp->removeFile(k);
2497                    k--;
2498                }
2499            }
2500
2501            // Quick check: no preferred filters, nothing more to do.
2502            if (prefFilter.isEmpty()) {
2503                continue;
2504            }
2505
2506            // Get the preferred density if there is one. We do not match exactly for density.
2507            // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2508            // pick xhdpi.
2509            uint32_t preferredDensity = 0;
2510            const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
2511            if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
2512                preferredDensity = (*preferredConfigs)[0];
2513            }
2514
2515            // Now deal with preferred configurations.
2516            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2517                for (size_t k=0; k<grp->getFiles().size(); k++) {
2518                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2519                    if (k == 0 && grp->getFiles().size() == 1) {
2520                        // If this is the only file left, we need to keep it.
2521                        // Otherwise the resource IDs we are using will be inconsistent
2522                        // with what we get when not stripping.  Sucky, but at least
2523                        // for now we can rely on the back-end doing another filtering
2524                        // pass to take this out and leave us with this resource name
2525                        // containing no entries.
2526                        continue;
2527                    }
2528                    if (file->getPath().getPathExtension() == ".xml") {
2529                        // We can't remove .xml files at this point, because when
2530                        // we parse them they may add identifier resources, so
2531                        // removing them can cause our resource identifiers to
2532                        // become inconsistent.
2533                        continue;
2534                    }
2535                    const ResTable_config& config(file->getGroupEntry().toParams());
2536                    if (!prefFilter.match(axis, config)) {
2537                        // This is a resource we would prefer not to have.  Check
2538                        // to see if have a similar variation that we would like
2539                        // to have and, if so, we can drop it.
2540
2541                        uint32_t bestDensity = config.density;
2542
2543                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2544                            if (m == k) continue;
2545                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2546                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2547                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2548                                if (axis == AXIS_DENSITY && preferredDensity > 0) {
2549                                    // See if there is a better density resource
2550                                    if (mconfig.density < bestDensity &&
2551                                            mconfig.density > preferredDensity &&
2552                                            bestDensity > preferredDensity) {
2553                                        // This density is between our best density and
2554                                        // the preferred density, therefore it is better.
2555                                        bestDensity = mconfig.density;
2556                                    } else if (mconfig.density > bestDensity &&
2557                                            bestDensity < preferredDensity) {
2558                                        // This density is better than our best density and
2559                                        // our best density was smaller than our preferred
2560                                        // density, so it is better.
2561                                        bestDensity = mconfig.density;
2562                                    }
2563                                } else if (prefFilter.match(axis, mconfig)) {
2564                                    if (bundle->getVerbose()) {
2565                                        printf("Pruning unneeded resource: %s\n",
2566                                                file->getPrintableSource().string());
2567                                    }
2568                                    grp->removeFile(k);
2569                                    k--;
2570                                    break;
2571                                }
2572                            }
2573                        }
2574
2575                        if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2576                                bestDensity != config.density) {
2577                            if (bundle->getVerbose()) {
2578                                printf("Pruning unneeded resource: %s\n",
2579                                        file->getPrintableSource().string());
2580                            }
2581                            grp->removeFile(k);
2582                            k--;
2583                        }
2584                    }
2585                }
2586            }
2587        }
2588    }
2589
2590    return NO_ERROR;
2591}
2592
2593sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2594{
2595    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2596    if (sym == NULL) {
2597        sym = new AaptSymbols();
2598        mSymbols.add(name, sym);
2599    }
2600    return sym;
2601}
2602
2603sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2604{
2605    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2606    if (sym == NULL) {
2607        sym = new AaptSymbols();
2608        mJavaSymbols.add(name, sym);
2609    }
2610    return sym;
2611}
2612
2613status_t AaptAssets::applyJavaSymbols()
2614{
2615    size_t N = mJavaSymbols.size();
2616    for (size_t i=0; i<N; i++) {
2617        const String8& name = mJavaSymbols.keyAt(i);
2618        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2619        ssize_t pos = mSymbols.indexOfKey(name);
2620        if (pos < 0) {
2621            SourcePos pos;
2622            pos.error("Java symbol dir %s not defined\n", name.string());
2623            return UNKNOWN_ERROR;
2624        }
2625        //printf("**** applying java symbols in dir %s\n", name.string());
2626        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2627        if (err != NO_ERROR) {
2628            return err;
2629        }
2630    }
2631
2632    return NO_ERROR;
2633}
2634
2635bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2636    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2637    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2638    //        sym.isJavaSymbol ? 1 : 0);
2639    if (!mHavePrivateSymbols) return true;
2640    if (sym.isPublic) return true;
2641    if (includePrivate && sym.isJavaSymbol) return true;
2642    return false;
2643}
2644
2645status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2646{
2647    if (!mHaveIncludedAssets) {
2648        // Add in all includes.
2649        const Vector<const char*>& incl = bundle->getPackageIncludes();
2650        const size_t N=incl.size();
2651        for (size_t i=0; i<N; i++) {
2652            if (bundle->getVerbose())
2653                printf("Including resources from package: %s\n", incl[i]);
2654            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2655                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2656                        incl[i]);
2657                return UNKNOWN_ERROR;
2658            }
2659        }
2660        mHaveIncludedAssets = true;
2661    }
2662
2663    return NO_ERROR;
2664}
2665
2666status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2667{
2668    const ResTable& res = getIncludedResources();
2669    // XXX dirty!
2670    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2671}
2672
2673const ResTable& AaptAssets::getIncludedResources() const
2674{
2675    return mIncludedAssets.getResources(false);
2676}
2677
2678void AaptAssets::print(const String8& prefix) const
2679{
2680    String8 innerPrefix(prefix);
2681    innerPrefix.append("  ");
2682    String8 innerInnerPrefix(innerPrefix);
2683    innerInnerPrefix.append("  ");
2684    printf("%sConfigurations:\n", prefix.string());
2685    const size_t N=mGroupEntries.size();
2686    for (size_t i=0; i<N; i++) {
2687        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2688        printf("%s %s\n", prefix.string(),
2689                cname != "" ? cname.string() : "(default)");
2690    }
2691
2692    printf("\n%sFiles:\n", prefix.string());
2693    AaptDir::print(innerPrefix);
2694
2695    printf("\n%sResource Dirs:\n", prefix.string());
2696    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2697    const size_t NR = resdirs.size();
2698    for (size_t i=0; i<NR; i++) {
2699        const sp<AaptDir>& d = resdirs.itemAt(i);
2700        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2701        d->print(innerInnerPrefix);
2702    }
2703}
2704
2705sp<AaptDir> AaptAssets::resDir(const String8& name) const
2706{
2707    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2708    const size_t N = resdirs.size();
2709    for (size_t i=0; i<N; i++) {
2710        const sp<AaptDir>& d = resdirs.itemAt(i);
2711        if (d->getLeaf() == name) {
2712            return d;
2713        }
2714    }
2715    return NULL;
2716}
2717
2718bool
2719valid_symbol_name(const String8& symbol)
2720{
2721    static char const * const KEYWORDS[] = {
2722        "abstract", "assert", "boolean", "break",
2723        "byte", "case", "catch", "char", "class", "const", "continue",
2724        "default", "do", "double", "else", "enum", "extends", "final",
2725        "finally", "float", "for", "goto", "if", "implements", "import",
2726        "instanceof", "int", "interface", "long", "native", "new", "package",
2727        "private", "protected", "public", "return", "short", "static",
2728        "strictfp", "super", "switch", "synchronized", "this", "throw",
2729        "throws", "transient", "try", "void", "volatile", "while",
2730        "true", "false", "null",
2731        NULL
2732    };
2733    const char*const* k = KEYWORDS;
2734    const char*const s = symbol.string();
2735    while (*k) {
2736        if (0 == strcmp(s, *k)) {
2737            return false;
2738        }
2739        k++;
2740    }
2741    return true;
2742}
2743