AaptAssets.cpp revision 09384303dea4f3f01d5682918d7bab9bf83a02b1
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    }
1121
1122    return false;
1123}
1124
1125bool AaptGroupEntry::getUiModeNightName(const char* name,
1126                                          ResTable_config* out)
1127{
1128    if (strcmp(name, kWildcardName) == 0) {
1129        if (out) out->uiMode =
1130                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1131                | ResTable_config::UI_MODE_NIGHT_ANY;
1132        return true;
1133    } else if (strcmp(name, "night") == 0) {
1134        if (out) out->uiMode =
1135                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1136                | ResTable_config::UI_MODE_NIGHT_YES;
1137        return true;
1138    } else if (strcmp(name, "notnight") == 0) {
1139      if (out) out->uiMode =
1140              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1141              | ResTable_config::UI_MODE_NIGHT_NO;
1142        return true;
1143    }
1144
1145    return false;
1146}
1147
1148bool AaptGroupEntry::getDensityName(const char* name,
1149                                    ResTable_config* out)
1150{
1151    if (strcmp(name, kWildcardName) == 0) {
1152        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1153        return true;
1154    }
1155
1156    if (strcmp(name, "nodpi") == 0) {
1157        if (out) out->density = ResTable_config::DENSITY_NONE;
1158        return true;
1159    }
1160
1161    if (strcmp(name, "ldpi") == 0) {
1162        if (out) out->density = ResTable_config::DENSITY_LOW;
1163        return true;
1164    }
1165
1166    if (strcmp(name, "mdpi") == 0) {
1167        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1168        return true;
1169    }
1170
1171    if (strcmp(name, "tvdpi") == 0) {
1172        if (out) out->density = ResTable_config::DENSITY_TV;
1173        return true;
1174    }
1175
1176    if (strcmp(name, "hdpi") == 0) {
1177        if (out) out->density = ResTable_config::DENSITY_HIGH;
1178        return true;
1179    }
1180
1181    if (strcmp(name, "xhdpi") == 0) {
1182        if (out) out->density = ResTable_config::DENSITY_XHIGH;
1183        return true;
1184    }
1185
1186    if (strcmp(name, "xxhdpi") == 0) {
1187        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1188        return true;
1189    }
1190
1191    if (strcmp(name, "xxxhdpi") == 0) {
1192        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1193        return true;
1194    }
1195
1196    char* c = (char*)name;
1197    while (*c >= '0' && *c <= '9') {
1198        c++;
1199    }
1200
1201    // check that we have 'dpi' after the last digit.
1202    if (toupper(c[0]) != 'D' ||
1203            toupper(c[1]) != 'P' ||
1204            toupper(c[2]) != 'I' ||
1205            c[3] != 0) {
1206        return false;
1207    }
1208
1209    // temporarily replace the first letter with \0 to
1210    // use atoi.
1211    char tmp = c[0];
1212    c[0] = '\0';
1213
1214    int d = atoi(name);
1215    c[0] = tmp;
1216
1217    if (d != 0) {
1218        if (out) out->density = d;
1219        return true;
1220    }
1221
1222    return false;
1223}
1224
1225bool AaptGroupEntry::getTouchscreenName(const char* name,
1226                                        ResTable_config* out)
1227{
1228    if (strcmp(name, kWildcardName) == 0) {
1229        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1230        return true;
1231    } else if (strcmp(name, "notouch") == 0) {
1232        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1233        return true;
1234    } else if (strcmp(name, "stylus") == 0) {
1235        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1236        return true;
1237    } else if (strcmp(name, "finger") == 0) {
1238        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1239        return true;
1240    }
1241
1242    return false;
1243}
1244
1245bool AaptGroupEntry::getKeysHiddenName(const char* name,
1246                                       ResTable_config* out)
1247{
1248    uint8_t mask = 0;
1249    uint8_t value = 0;
1250    if (strcmp(name, kWildcardName) == 0) {
1251        mask = ResTable_config::MASK_KEYSHIDDEN;
1252        value = ResTable_config::KEYSHIDDEN_ANY;
1253    } else if (strcmp(name, "keysexposed") == 0) {
1254        mask = ResTable_config::MASK_KEYSHIDDEN;
1255        value = ResTable_config::KEYSHIDDEN_NO;
1256    } else if (strcmp(name, "keyshidden") == 0) {
1257        mask = ResTable_config::MASK_KEYSHIDDEN;
1258        value = ResTable_config::KEYSHIDDEN_YES;
1259    } else if (strcmp(name, "keyssoft") == 0) {
1260        mask = ResTable_config::MASK_KEYSHIDDEN;
1261        value = ResTable_config::KEYSHIDDEN_SOFT;
1262    }
1263
1264    if (mask != 0) {
1265        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1266        return true;
1267    }
1268
1269    return false;
1270}
1271
1272bool AaptGroupEntry::getKeyboardName(const char* name,
1273                                        ResTable_config* out)
1274{
1275    if (strcmp(name, kWildcardName) == 0) {
1276        if (out) out->keyboard = out->KEYBOARD_ANY;
1277        return true;
1278    } else if (strcmp(name, "nokeys") == 0) {
1279        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1280        return true;
1281    } else if (strcmp(name, "qwerty") == 0) {
1282        if (out) out->keyboard = out->KEYBOARD_QWERTY;
1283        return true;
1284    } else if (strcmp(name, "12key") == 0) {
1285        if (out) out->keyboard = out->KEYBOARD_12KEY;
1286        return true;
1287    }
1288
1289    return false;
1290}
1291
1292bool AaptGroupEntry::getNavHiddenName(const char* name,
1293                                       ResTable_config* out)
1294{
1295    uint8_t mask = 0;
1296    uint8_t value = 0;
1297    if (strcmp(name, kWildcardName) == 0) {
1298        mask = ResTable_config::MASK_NAVHIDDEN;
1299        value = ResTable_config::NAVHIDDEN_ANY;
1300    } else if (strcmp(name, "navexposed") == 0) {
1301        mask = ResTable_config::MASK_NAVHIDDEN;
1302        value = ResTable_config::NAVHIDDEN_NO;
1303    } else if (strcmp(name, "navhidden") == 0) {
1304        mask = ResTable_config::MASK_NAVHIDDEN;
1305        value = ResTable_config::NAVHIDDEN_YES;
1306    }
1307
1308    if (mask != 0) {
1309        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1310        return true;
1311    }
1312
1313    return false;
1314}
1315
1316bool AaptGroupEntry::getNavigationName(const char* name,
1317                                     ResTable_config* out)
1318{
1319    if (strcmp(name, kWildcardName) == 0) {
1320        if (out) out->navigation = out->NAVIGATION_ANY;
1321        return true;
1322    } else if (strcmp(name, "nonav") == 0) {
1323        if (out) out->navigation = out->NAVIGATION_NONAV;
1324        return true;
1325    } else if (strcmp(name, "dpad") == 0) {
1326        if (out) out->navigation = out->NAVIGATION_DPAD;
1327        return true;
1328    } else if (strcmp(name, "trackball") == 0) {
1329        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1330        return true;
1331    } else if (strcmp(name, "wheel") == 0) {
1332        if (out) out->navigation = out->NAVIGATION_WHEEL;
1333        return true;
1334    }
1335
1336    return false;
1337}
1338
1339bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1340{
1341    if (strcmp(name, kWildcardName) == 0) {
1342        if (out) {
1343            out->screenWidth = out->SCREENWIDTH_ANY;
1344            out->screenHeight = out->SCREENHEIGHT_ANY;
1345        }
1346        return true;
1347    }
1348
1349    const char* x = name;
1350    while (*x >= '0' && *x <= '9') x++;
1351    if (x == name || *x != 'x') return false;
1352    String8 xName(name, x-name);
1353    x++;
1354
1355    const char* y = x;
1356    while (*y >= '0' && *y <= '9') y++;
1357    if (y == name || *y != 0) return false;
1358    String8 yName(x, y-x);
1359
1360    uint16_t w = (uint16_t)atoi(xName.string());
1361    uint16_t h = (uint16_t)atoi(yName.string());
1362    if (w < h) {
1363        return false;
1364    }
1365
1366    if (out) {
1367        out->screenWidth = w;
1368        out->screenHeight = h;
1369    }
1370
1371    return true;
1372}
1373
1374bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1375{
1376    if (strcmp(name, kWildcardName) == 0) {
1377        if (out) {
1378            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1379        }
1380        return true;
1381    }
1382
1383    if (*name != 's') return false;
1384    name++;
1385    if (*name != 'w') return false;
1386    name++;
1387    const char* x = name;
1388    while (*x >= '0' && *x <= '9') x++;
1389    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1390    String8 xName(name, x-name);
1391
1392    if (out) {
1393        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1394    }
1395
1396    return true;
1397}
1398
1399bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1400{
1401    if (strcmp(name, kWildcardName) == 0) {
1402        if (out) {
1403            out->screenWidthDp = out->SCREENWIDTH_ANY;
1404        }
1405        return true;
1406    }
1407
1408    if (*name != 'w') return false;
1409    name++;
1410    const char* x = name;
1411    while (*x >= '0' && *x <= '9') x++;
1412    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1413    String8 xName(name, x-name);
1414
1415    if (out) {
1416        out->screenWidthDp = (uint16_t)atoi(xName.string());
1417    }
1418
1419    return true;
1420}
1421
1422bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1423{
1424    if (strcmp(name, kWildcardName) == 0) {
1425        if (out) {
1426            out->screenHeightDp = out->SCREENWIDTH_ANY;
1427        }
1428        return true;
1429    }
1430
1431    if (*name != 'h') return false;
1432    name++;
1433    const char* x = name;
1434    while (*x >= '0' && *x <= '9') x++;
1435    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1436    String8 xName(name, x-name);
1437
1438    if (out) {
1439        out->screenHeightDp = (uint16_t)atoi(xName.string());
1440    }
1441
1442    return true;
1443}
1444
1445bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1446{
1447    if (strcmp(name, kWildcardName) == 0) {
1448        if (out) {
1449            out->sdkVersion = out->SDKVERSION_ANY;
1450            out->minorVersion = out->MINORVERSION_ANY;
1451        }
1452        return true;
1453    }
1454
1455    if (*name != 'v') {
1456        return false;
1457    }
1458
1459    name++;
1460    const char* s = name;
1461    while (*s >= '0' && *s <= '9') s++;
1462    if (s == name || *s != 0) return false;
1463    String8 sdkName(name, s-name);
1464
1465    if (out) {
1466        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1467        out->minorVersion = 0;
1468    }
1469
1470    return true;
1471}
1472
1473int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1474{
1475    int v = mcc.compare(o.mcc);
1476    if (v == 0) v = mnc.compare(o.mnc);
1477    if (v == 0) v = locale.compare(o.locale);
1478    if (v == 0) v = layoutDirection.compare(o.layoutDirection);
1479    if (v == 0) v = vendor.compare(o.vendor);
1480    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1481    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1482    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1483    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1484    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1485    if (v == 0) v = orientation.compare(o.orientation);
1486    if (v == 0) v = uiModeType.compare(o.uiModeType);
1487    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1488    if (v == 0) v = density.compare(o.density);
1489    if (v == 0) v = touchscreen.compare(o.touchscreen);
1490    if (v == 0) v = keysHidden.compare(o.keysHidden);
1491    if (v == 0) v = keyboard.compare(o.keyboard);
1492    if (v == 0) v = navHidden.compare(o.navHidden);
1493    if (v == 0) v = navigation.compare(o.navigation);
1494    if (v == 0) v = screenSize.compare(o.screenSize);
1495    if (v == 0) v = version.compare(o.version);
1496    return v;
1497}
1498
1499const ResTable_config& AaptGroupEntry::toParams() const
1500{
1501    if (!mParamsChanged) {
1502        return mParams;
1503    }
1504
1505    mParamsChanged = false;
1506    ResTable_config& params(mParams);
1507    memset(&params, 0, sizeof(params));
1508    getMccName(mcc.string(), &params);
1509    getMncName(mnc.string(), &params);
1510    getLocaleName(locale.string(), &params);
1511    getLayoutDirectionName(layoutDirection.string(), &params);
1512    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1513    getScreenWidthDpName(screenWidthDp.string(), &params);
1514    getScreenHeightDpName(screenHeightDp.string(), &params);
1515    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1516    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1517    getOrientationName(orientation.string(), &params);
1518    getUiModeTypeName(uiModeType.string(), &params);
1519    getUiModeNightName(uiModeNight.string(), &params);
1520    getDensityName(density.string(), &params);
1521    getTouchscreenName(touchscreen.string(), &params);
1522    getKeysHiddenName(keysHidden.string(), &params);
1523    getKeyboardName(keyboard.string(), &params);
1524    getNavHiddenName(navHidden.string(), &params);
1525    getNavigationName(navigation.string(), &params);
1526    getScreenSizeName(screenSize.string(), &params);
1527    getVersionName(version.string(), &params);
1528
1529    // Fix up version number based on specified parameters.
1530    int minSdk = 0;
1531    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1532            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1533            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1534        minSdk = SDK_HONEYCOMB_MR2;
1535    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1536                != ResTable_config::UI_MODE_TYPE_ANY
1537            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1538                != ResTable_config::UI_MODE_NIGHT_ANY) {
1539        minSdk = SDK_FROYO;
1540    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1541                != ResTable_config::SCREENSIZE_ANY
1542            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1543                != ResTable_config::SCREENLONG_ANY
1544            || params.density != ResTable_config::DENSITY_DEFAULT) {
1545        minSdk = SDK_DONUT;
1546    }
1547
1548    if (minSdk > params.sdkVersion) {
1549        params.sdkVersion = minSdk;
1550    }
1551
1552    return params;
1553}
1554
1555// =========================================================================
1556// =========================================================================
1557// =========================================================================
1558
1559void* AaptFile::editData(size_t size)
1560{
1561    if (size <= mBufferSize) {
1562        mDataSize = size;
1563        return mData;
1564    }
1565    size_t allocSize = (size*3)/2;
1566    void* buf = realloc(mData, allocSize);
1567    if (buf == NULL) {
1568        return NULL;
1569    }
1570    mData = buf;
1571    mDataSize = size;
1572    mBufferSize = allocSize;
1573    return buf;
1574}
1575
1576void* AaptFile::editData(size_t* outSize)
1577{
1578    if (outSize) {
1579        *outSize = mDataSize;
1580    }
1581    return mData;
1582}
1583
1584void* AaptFile::padData(size_t wordSize)
1585{
1586    const size_t extra = mDataSize%wordSize;
1587    if (extra == 0) {
1588        return mData;
1589    }
1590
1591    size_t initial = mDataSize;
1592    void* data = editData(initial+(wordSize-extra));
1593    if (data != NULL) {
1594        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1595    }
1596    return data;
1597}
1598
1599status_t AaptFile::writeData(const void* data, size_t size)
1600{
1601    size_t end = mDataSize;
1602    size_t total = size + end;
1603    void* buf = editData(total);
1604    if (buf == NULL) {
1605        return UNKNOWN_ERROR;
1606    }
1607    memcpy(((char*)buf)+end, data, size);
1608    return NO_ERROR;
1609}
1610
1611void AaptFile::clearData()
1612{
1613    if (mData != NULL) free(mData);
1614    mData = NULL;
1615    mDataSize = 0;
1616    mBufferSize = 0;
1617}
1618
1619String8 AaptFile::getPrintableSource() const
1620{
1621    if (hasData()) {
1622        String8 name(mGroupEntry.toDirName(String8()));
1623        name.appendPath(mPath);
1624        name.append(" #generated");
1625        return name;
1626    }
1627    return mSourceFile;
1628}
1629
1630// =========================================================================
1631// =========================================================================
1632// =========================================================================
1633
1634status_t AaptGroup::addFile(const sp<AaptFile>& file, const bool overwriteDuplicate)
1635{
1636    ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
1637    if (index >= 0 && overwriteDuplicate) {
1638        fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
1639                mFiles[index]->getSourceFile().string(),
1640                file->getSourceFile().string());
1641        removeFile(index);
1642        index = -1;
1643    }
1644
1645    if (index < 0) {
1646        file->mPath = mPath;
1647        mFiles.add(file->getGroupEntry(), file);
1648        return NO_ERROR;
1649    }
1650
1651#if 0
1652    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1653            file->getSourceFile().string(),
1654            file->getGroupEntry().toDirName(String8()).string(),
1655            mLeaf.string(), mPath.string());
1656#endif
1657
1658    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1659                                               getPrintableSource().string());
1660    return UNKNOWN_ERROR;
1661}
1662
1663void AaptGroup::removeFile(size_t index)
1664{
1665	mFiles.removeItemsAt(index);
1666}
1667
1668void AaptGroup::print(const String8& prefix) const
1669{
1670    printf("%s%s\n", prefix.string(), getPath().string());
1671    const size_t N=mFiles.size();
1672    size_t i;
1673    for (i=0; i<N; i++) {
1674        sp<AaptFile> file = mFiles.valueAt(i);
1675        const AaptGroupEntry& e = file->getGroupEntry();
1676        if (file->hasData()) {
1677            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1678                    (int)file->getSize());
1679        } else {
1680            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1681                    file->getPrintableSource().string());
1682        }
1683        //printf("%s  File Group Entry: %s\n", prefix.string(),
1684        //        file->getGroupEntry().toDirName(String8()).string());
1685    }
1686}
1687
1688String8 AaptGroup::getPrintableSource() const
1689{
1690    if (mFiles.size() > 0) {
1691        // Arbitrarily pull the first source file out of the list.
1692        return mFiles.valueAt(0)->getPrintableSource();
1693    }
1694
1695    // Should never hit this case, but to be safe...
1696    return getPath();
1697
1698}
1699
1700// =========================================================================
1701// =========================================================================
1702// =========================================================================
1703
1704status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1705{
1706    if (mFiles.indexOfKey(name) >= 0) {
1707        return ALREADY_EXISTS;
1708    }
1709    mFiles.add(name, file);
1710    return NO_ERROR;
1711}
1712
1713status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1714{
1715    if (mDirs.indexOfKey(name) >= 0) {
1716        return ALREADY_EXISTS;
1717    }
1718    mDirs.add(name, dir);
1719    return NO_ERROR;
1720}
1721
1722sp<AaptDir> AaptDir::makeDir(const String8& path)
1723{
1724    String8 name;
1725    String8 remain = path;
1726
1727    sp<AaptDir> subdir = this;
1728    while (name = remain.walkPath(&remain), remain != "") {
1729        subdir = subdir->makeDir(name);
1730    }
1731
1732    ssize_t i = subdir->mDirs.indexOfKey(name);
1733    if (i >= 0) {
1734        return subdir->mDirs.valueAt(i);
1735    }
1736    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1737    subdir->mDirs.add(name, dir);
1738    return dir;
1739}
1740
1741void AaptDir::removeFile(const String8& name)
1742{
1743    mFiles.removeItem(name);
1744}
1745
1746void AaptDir::removeDir(const String8& name)
1747{
1748    mDirs.removeItem(name);
1749}
1750
1751status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file,
1752        const bool overwrite)
1753{
1754    sp<AaptGroup> group;
1755    if (mFiles.indexOfKey(leafName) >= 0) {
1756        group = mFiles.valueFor(leafName);
1757    } else {
1758        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1759        mFiles.add(leafName, group);
1760    }
1761
1762    return group->addFile(file, overwrite);
1763}
1764
1765ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1766                            const AaptGroupEntry& kind, const String8& resType,
1767                            sp<FilePathStore>& fullResPaths, const bool overwrite)
1768{
1769    Vector<String8> fileNames;
1770    {
1771        DIR* dir = NULL;
1772
1773        dir = opendir(srcDir.string());
1774        if (dir == NULL) {
1775            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1776            return UNKNOWN_ERROR;
1777        }
1778
1779        /*
1780         * Slurp the filenames out of the directory.
1781         */
1782        while (1) {
1783            struct dirent* entry;
1784
1785            entry = readdir(dir);
1786            if (entry == NULL)
1787                break;
1788
1789            if (isHidden(srcDir.string(), entry->d_name))
1790                continue;
1791
1792            String8 name(entry->d_name);
1793            fileNames.add(name);
1794            // Add fully qualified path for dependency purposes
1795            // if we're collecting them
1796            if (fullResPaths != NULL) {
1797                fullResPaths->add(srcDir.appendPathCopy(name));
1798            }
1799        }
1800        closedir(dir);
1801    }
1802
1803    ssize_t count = 0;
1804
1805    /*
1806     * Stash away the files and recursively descend into subdirectories.
1807     */
1808    const size_t N = fileNames.size();
1809    size_t i;
1810    for (i = 0; i < N; i++) {
1811        String8 pathName(srcDir);
1812        FileType type;
1813
1814        pathName.appendPath(fileNames[i].string());
1815        type = getFileType(pathName.string());
1816        if (type == kFileTypeDirectory) {
1817            sp<AaptDir> subdir;
1818            bool notAdded = false;
1819            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1820                subdir = mDirs.valueFor(fileNames[i]);
1821            } else {
1822                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1823                notAdded = true;
1824            }
1825            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1826                                                resType, fullResPaths, overwrite);
1827            if (res < NO_ERROR) {
1828                return res;
1829            }
1830            if (res > 0 && notAdded) {
1831                mDirs.add(fileNames[i], subdir);
1832            }
1833            count += res;
1834        } else if (type == kFileTypeRegular) {
1835            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1836            status_t err = addLeafFile(fileNames[i], file, overwrite);
1837            if (err != NO_ERROR) {
1838                return err;
1839            }
1840
1841            count++;
1842
1843        } else {
1844            if (bundle->getVerbose())
1845                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1846        }
1847    }
1848
1849    return count;
1850}
1851
1852status_t AaptDir::validate() const
1853{
1854    const size_t NF = mFiles.size();
1855    const size_t ND = mDirs.size();
1856    size_t i;
1857    for (i = 0; i < NF; i++) {
1858        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1859            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1860                    "Invalid filename.  Unable to add.");
1861            return UNKNOWN_ERROR;
1862        }
1863
1864        size_t j;
1865        for (j = i+1; j < NF; j++) {
1866            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1867                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1868                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1869                        "File is case-insensitive equivalent to: %s",
1870                        mFiles.valueAt(j)->getPrintableSource().string());
1871                return UNKNOWN_ERROR;
1872            }
1873
1874            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1875            // (this is mostly caught by the "marked" stuff, below)
1876        }
1877
1878        for (j = 0; j < ND; j++) {
1879            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1880                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1881                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1882                        "File conflicts with dir from: %s",
1883                        mDirs.valueAt(j)->getPrintableSource().string());
1884                return UNKNOWN_ERROR;
1885            }
1886        }
1887    }
1888
1889    for (i = 0; i < ND; i++) {
1890        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1891            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1892                    "Invalid directory name, unable to add.");
1893            return UNKNOWN_ERROR;
1894        }
1895
1896        size_t j;
1897        for (j = i+1; j < ND; j++) {
1898            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1899                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1900                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1901                        "Directory is case-insensitive equivalent to: %s",
1902                        mDirs.valueAt(j)->getPrintableSource().string());
1903                return UNKNOWN_ERROR;
1904            }
1905        }
1906
1907        status_t err = mDirs.valueAt(i)->validate();
1908        if (err != NO_ERROR) {
1909            return err;
1910        }
1911    }
1912
1913    return NO_ERROR;
1914}
1915
1916void AaptDir::print(const String8& prefix) const
1917{
1918    const size_t ND=getDirs().size();
1919    size_t i;
1920    for (i=0; i<ND; i++) {
1921        getDirs().valueAt(i)->print(prefix);
1922    }
1923
1924    const size_t NF=getFiles().size();
1925    for (i=0; i<NF; i++) {
1926        getFiles().valueAt(i)->print(prefix);
1927    }
1928}
1929
1930String8 AaptDir::getPrintableSource() const
1931{
1932    if (mFiles.size() > 0) {
1933        // Arbitrarily pull the first file out of the list as the source dir.
1934        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1935    }
1936    if (mDirs.size() > 0) {
1937        // Or arbitrarily pull the first dir out of the list as the source dir.
1938        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1939    }
1940
1941    // Should never hit this case, but to be safe...
1942    return mPath;
1943
1944}
1945
1946// =========================================================================
1947// =========================================================================
1948// =========================================================================
1949
1950status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
1951{
1952    status_t err = NO_ERROR;
1953    size_t N = javaSymbols->mSymbols.size();
1954    for (size_t i=0; i<N; i++) {
1955        const String8& name = javaSymbols->mSymbols.keyAt(i);
1956        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
1957        ssize_t pos = mSymbols.indexOfKey(name);
1958        if (pos < 0) {
1959            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
1960            err = UNKNOWN_ERROR;
1961            continue;
1962        }
1963        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
1964        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
1965        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
1966    }
1967
1968    N = javaSymbols->mNestedSymbols.size();
1969    for (size_t i=0; i<N; i++) {
1970        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
1971        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
1972        ssize_t pos = mNestedSymbols.indexOfKey(name);
1973        if (pos < 0) {
1974            SourcePos pos;
1975            pos.error("Java symbol dir %s not defined\n", name.string());
1976            err = UNKNOWN_ERROR;
1977            continue;
1978        }
1979        //printf("**** applying java symbols in dir %s\n", name.string());
1980        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1981        if (myerr != NO_ERROR) {
1982            err = myerr;
1983        }
1984    }
1985
1986    return err;
1987}
1988
1989// =========================================================================
1990// =========================================================================
1991// =========================================================================
1992
1993AaptAssets::AaptAssets()
1994    : AaptDir(String8(), String8()),
1995      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1996{
1997}
1998
1999const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2000    if (mChanged) {
2001    }
2002    return mGroupEntries;
2003}
2004
2005status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2006{
2007    mChanged = true;
2008    return AaptDir::addFile(name, file);
2009}
2010
2011sp<AaptFile> AaptAssets::addFile(
2012        const String8& filePath, const AaptGroupEntry& entry,
2013        const String8& srcDir, sp<AaptGroup>* outGroup,
2014        const String8& resType)
2015{
2016    sp<AaptDir> dir = this;
2017    sp<AaptGroup> group;
2018    sp<AaptFile> file;
2019    String8 root, remain(filePath), partialPath;
2020    while (remain.length() > 0) {
2021        root = remain.walkPath(&remain);
2022        partialPath.appendPath(root);
2023
2024        const String8 rootStr(root);
2025
2026        if (remain.length() == 0) {
2027            ssize_t i = dir->getFiles().indexOfKey(rootStr);
2028            if (i >= 0) {
2029                group = dir->getFiles().valueAt(i);
2030            } else {
2031                group = new AaptGroup(rootStr, filePath);
2032                status_t res = dir->addFile(rootStr, group);
2033                if (res != NO_ERROR) {
2034                    return NULL;
2035                }
2036            }
2037            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2038            status_t res = group->addFile(file);
2039            if (res != NO_ERROR) {
2040                return NULL;
2041            }
2042            break;
2043
2044        } else {
2045            ssize_t i = dir->getDirs().indexOfKey(rootStr);
2046            if (i >= 0) {
2047                dir = dir->getDirs().valueAt(i);
2048            } else {
2049                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2050                status_t res = dir->addDir(rootStr, subdir);
2051                if (res != NO_ERROR) {
2052                    return NULL;
2053                }
2054                dir = subdir;
2055            }
2056        }
2057    }
2058
2059    mGroupEntries.add(entry);
2060    if (outGroup) *outGroup = group;
2061    return file;
2062}
2063
2064void AaptAssets::addResource(const String8& leafName, const String8& path,
2065                const sp<AaptFile>& file, const String8& resType)
2066{
2067    sp<AaptDir> res = AaptDir::makeDir(kResString);
2068    String8 dirname = file->getGroupEntry().toDirName(resType);
2069    sp<AaptDir> subdir = res->makeDir(dirname);
2070    sp<AaptGroup> grr = new AaptGroup(leafName, path);
2071    grr->addFile(file);
2072
2073    subdir->addFile(leafName, grr);
2074}
2075
2076
2077ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2078{
2079    int count;
2080    int totalCount = 0;
2081    FileType type;
2082    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2083    const size_t dirCount =resDirs.size();
2084    sp<AaptAssets> current = this;
2085
2086    const int N = bundle->getFileSpecCount();
2087
2088    /*
2089     * If a package manifest was specified, include that first.
2090     */
2091    if (bundle->getAndroidManifestFile() != NULL) {
2092        // place at root of zip.
2093        String8 srcFile(bundle->getAndroidManifestFile());
2094        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2095                NULL, String8());
2096        totalCount++;
2097    }
2098
2099    /*
2100     * If a directory of custom assets was supplied, slurp 'em up.
2101     */
2102    const Vector<const char*>& assetDirs = bundle->getAssetSourceDirs();
2103    const int AN = assetDirs.size();
2104    for (int i = 0; i < AN; i++) {
2105        FileType type = getFileType(assetDirs[i]);
2106        if (type == kFileTypeNonexistent) {
2107            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDirs[i]);
2108            return UNKNOWN_ERROR;
2109        }
2110        if (type != kFileTypeDirectory) {
2111            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDirs[i]);
2112            return UNKNOWN_ERROR;
2113        }
2114
2115        String8 assetRoot(assetDirs[i]);
2116        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2117        AaptGroupEntry group;
2118        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2119                                            String8(), mFullAssetPaths, true);
2120        if (count < 0) {
2121            totalCount = count;
2122            goto bail;
2123        }
2124        if (count > 0) {
2125            mGroupEntries.add(group);
2126        }
2127        totalCount += count;
2128
2129        if (bundle->getVerbose()) {
2130            printf("Found %d custom asset file%s in %s\n",
2131                   count, (count==1) ? "" : "s", assetDirs[i]);
2132        }
2133    }
2134
2135    /*
2136     * If a directory of resource-specific assets was supplied, slurp 'em up.
2137     */
2138    for (size_t i=0; i<dirCount; i++) {
2139        const char *res = resDirs[i];
2140        if (res) {
2141            type = getFileType(res);
2142            if (type == kFileTypeNonexistent) {
2143                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2144                return UNKNOWN_ERROR;
2145            }
2146            if (type == kFileTypeDirectory) {
2147                if (i>0) {
2148                    sp<AaptAssets> nextOverlay = new AaptAssets();
2149                    current->setOverlay(nextOverlay);
2150                    current = nextOverlay;
2151                    current->setFullResPaths(mFullResPaths);
2152                }
2153                count = current->slurpResourceTree(bundle, String8(res));
2154
2155                if (count < 0) {
2156                    totalCount = count;
2157                    goto bail;
2158                }
2159                totalCount += count;
2160            }
2161            else {
2162                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2163                return UNKNOWN_ERROR;
2164            }
2165        }
2166
2167    }
2168    /*
2169     * Now do any additional raw files.
2170     */
2171    for (int arg=0; arg<N; arg++) {
2172        const char* assetDir = bundle->getFileSpecEntry(arg);
2173
2174        FileType type = getFileType(assetDir);
2175        if (type == kFileTypeNonexistent) {
2176            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2177            return UNKNOWN_ERROR;
2178        }
2179        if (type != kFileTypeDirectory) {
2180            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2181            return UNKNOWN_ERROR;
2182        }
2183
2184        String8 assetRoot(assetDir);
2185
2186        if (bundle->getVerbose())
2187            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2188
2189        /*
2190         * Do a recursive traversal of subdir tree.  We don't make any
2191         * guarantees about ordering, so we're okay with an inorder search
2192         * using whatever order the OS happens to hand back to us.
2193         */
2194        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2195        if (count < 0) {
2196            /* failure; report error and remove archive */
2197            totalCount = count;
2198            goto bail;
2199        }
2200        totalCount += count;
2201
2202        if (bundle->getVerbose())
2203            printf("Found %d asset file%s in %s\n",
2204                   count, (count==1) ? "" : "s", assetDir);
2205    }
2206
2207    count = validate();
2208    if (count != NO_ERROR) {
2209        totalCount = count;
2210        goto bail;
2211    }
2212
2213    count = filter(bundle);
2214    if (count != NO_ERROR) {
2215        totalCount = count;
2216        goto bail;
2217    }
2218
2219bail:
2220    return totalCount;
2221}
2222
2223ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2224                                    const AaptGroupEntry& kind,
2225                                    const String8& resType,
2226                                    sp<FilePathStore>& fullResPaths)
2227{
2228    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2229    if (res > 0) {
2230        mGroupEntries.add(kind);
2231    }
2232
2233    return res;
2234}
2235
2236ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2237{
2238    ssize_t err = 0;
2239
2240    DIR* dir = opendir(srcDir.string());
2241    if (dir == NULL) {
2242        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2243        return UNKNOWN_ERROR;
2244    }
2245
2246    status_t count = 0;
2247
2248    /*
2249     * Run through the directory, looking for dirs that match the
2250     * expected pattern.
2251     */
2252    while (1) {
2253        struct dirent* entry = readdir(dir);
2254        if (entry == NULL) {
2255            break;
2256        }
2257
2258        if (isHidden(srcDir.string(), entry->d_name)) {
2259            continue;
2260        }
2261
2262        String8 subdirName(srcDir);
2263        subdirName.appendPath(entry->d_name);
2264
2265        AaptGroupEntry group;
2266        String8 resType;
2267        bool b = group.initFromDirName(entry->d_name, &resType);
2268        if (!b) {
2269            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2270                    entry->d_name);
2271            err = -1;
2272            continue;
2273        }
2274
2275        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2276            int maxResInt = atoi(bundle->getMaxResVersion());
2277            const char *verString = group.getVersionString().string();
2278            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2279            if (dirVersionInt > maxResInt) {
2280              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2281              continue;
2282            }
2283        }
2284
2285        FileType type = getFileType(subdirName.string());
2286
2287        if (type == kFileTypeDirectory) {
2288            sp<AaptDir> dir = makeDir(resType);
2289            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2290                                                resType, mFullResPaths);
2291            if (res < 0) {
2292                count = res;
2293                goto bail;
2294            }
2295            if (res > 0) {
2296                mGroupEntries.add(group);
2297                count += res;
2298            }
2299
2300            // Only add this directory if we don't already have a resource dir
2301            // for the current type.  This ensures that we only add the dir once
2302            // for all configs.
2303            sp<AaptDir> rdir = resDir(resType);
2304            if (rdir == NULL) {
2305                mResDirs.add(dir);
2306            }
2307        } else {
2308            if (bundle->getVerbose()) {
2309                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2310            }
2311        }
2312    }
2313
2314bail:
2315    closedir(dir);
2316    dir = NULL;
2317
2318    if (err != 0) {
2319        return err;
2320    }
2321    return count;
2322}
2323
2324ssize_t
2325AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2326{
2327    int count = 0;
2328    SortedVector<AaptGroupEntry> entries;
2329
2330    ZipFile* zip = new ZipFile;
2331    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2332    if (err != NO_ERROR) {
2333        fprintf(stderr, "error opening zip file %s\n", filename);
2334        count = err;
2335        delete zip;
2336        return -1;
2337    }
2338
2339    const int N = zip->getNumEntries();
2340    for (int i=0; i<N; i++) {
2341        ZipEntry* entry = zip->getEntryByIndex(i);
2342        if (entry->getDeleted()) {
2343            continue;
2344        }
2345
2346        String8 entryName(entry->getFileName());
2347
2348        String8 dirName = entryName.getPathDir();
2349        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2350
2351        String8 resType;
2352        AaptGroupEntry kind;
2353
2354        String8 remain;
2355        if (entryName.walkPath(&remain) == kResourceDir) {
2356            // these are the resources, pull their type out of the directory name
2357            kind.initFromDirName(remain.walkPath().string(), &resType);
2358        } else {
2359            // these are untyped and don't have an AaptGroupEntry
2360        }
2361        if (entries.indexOf(kind) < 0) {
2362            entries.add(kind);
2363            mGroupEntries.add(kind);
2364        }
2365
2366        // use the one from the zip file if they both exist.
2367        dir->removeFile(entryName.getPathLeaf());
2368
2369        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2370        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2371        if (err != NO_ERROR) {
2372            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2373            count = err;
2374            goto bail;
2375        }
2376        file->setCompressionMethod(entry->getCompressionMethod());
2377
2378#if 0
2379        if (entryName == "AndroidManifest.xml") {
2380            printf("AndroidManifest.xml\n");
2381        }
2382        printf("\n\nfile: %s\n", entryName.string());
2383#endif
2384
2385        size_t len = entry->getUncompressedLen();
2386        void* data = zip->uncompress(entry);
2387        void* buf = file->editData(len);
2388        memcpy(buf, data, len);
2389
2390#if 0
2391        const int OFF = 0;
2392        const unsigned char* p = (unsigned char*)data;
2393        const unsigned char* end = p+len;
2394        p += OFF;
2395        for (int i=0; i<32 && p < end; i++) {
2396            printf("0x%03x ", i*0x10 + OFF);
2397            for (int j=0; j<0x10 && p < end; j++) {
2398                printf(" %02x", *p);
2399                p++;
2400            }
2401            printf("\n");
2402        }
2403#endif
2404
2405        free(data);
2406
2407        count++;
2408    }
2409
2410bail:
2411    delete zip;
2412    return count;
2413}
2414
2415status_t AaptAssets::filter(Bundle* bundle)
2416{
2417    ResourceFilter reqFilter;
2418    status_t err = reqFilter.parse(bundle->getConfigurations());
2419    if (err != NO_ERROR) {
2420        return err;
2421    }
2422
2423    ResourceFilter prefFilter;
2424    err = prefFilter.parse(bundle->getPreferredConfigurations());
2425    if (err != NO_ERROR) {
2426        return err;
2427    }
2428
2429    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2430        return NO_ERROR;
2431    }
2432
2433    if (bundle->getVerbose()) {
2434        if (!reqFilter.isEmpty()) {
2435            printf("Applying required filter: %s\n",
2436                    bundle->getConfigurations());
2437        }
2438        if (!prefFilter.isEmpty()) {
2439            printf("Applying preferred filter: %s\n",
2440                    bundle->getPreferredConfigurations());
2441        }
2442    }
2443
2444    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2445    const size_t ND = resdirs.size();
2446    for (size_t i=0; i<ND; i++) {
2447        const sp<AaptDir>& dir = resdirs.itemAt(i);
2448        if (dir->getLeaf() == kValuesDir) {
2449            // The "value" dir is special since a single file defines
2450            // multiple resources, so we can not do filtering on the
2451            // files themselves.
2452            continue;
2453        }
2454        if (dir->getLeaf() == kMipmapDir) {
2455            // We also skip the "mipmap" directory, since the point of this
2456            // is to include all densities without stripping.  If you put
2457            // other configurations in here as well they won't be stripped
2458            // either...  So don't do that.  Seriously.  What is wrong with you?
2459            continue;
2460        }
2461
2462        const size_t NG = dir->getFiles().size();
2463        for (size_t j=0; j<NG; j++) {
2464            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2465
2466            // First remove any configurations we know we don't need.
2467            for (size_t k=0; k<grp->getFiles().size(); k++) {
2468                sp<AaptFile> file = grp->getFiles().valueAt(k);
2469                if (k == 0 && grp->getFiles().size() == 1) {
2470                    // If this is the only file left, we need to keep it.
2471                    // Otherwise the resource IDs we are using will be inconsistent
2472                    // with what we get when not stripping.  Sucky, but at least
2473                    // for now we can rely on the back-end doing another filtering
2474                    // pass to take this out and leave us with this resource name
2475                    // containing no entries.
2476                    continue;
2477                }
2478                if (file->getPath().getPathExtension() == ".xml") {
2479                    // We can't remove .xml files at this point, because when
2480                    // we parse them they may add identifier resources, so
2481                    // removing them can cause our resource identifiers to
2482                    // become inconsistent.
2483                    continue;
2484                }
2485                const ResTable_config& config(file->getGroupEntry().toParams());
2486                if (!reqFilter.match(config)) {
2487                    if (bundle->getVerbose()) {
2488                        printf("Pruning unneeded resource: %s\n",
2489                                file->getPrintableSource().string());
2490                    }
2491                    grp->removeFile(k);
2492                    k--;
2493                }
2494            }
2495
2496            // Quick check: no preferred filters, nothing more to do.
2497            if (prefFilter.isEmpty()) {
2498                continue;
2499            }
2500
2501            // Get the preferred density if there is one. We do not match exactly for density.
2502            // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2503            // pick xhdpi.
2504            uint32_t preferredDensity = 0;
2505            const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
2506            if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
2507                preferredDensity = (*preferredConfigs)[0];
2508            }
2509
2510            // Now deal with preferred configurations.
2511            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2512                for (size_t k=0; k<grp->getFiles().size(); k++) {
2513                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2514                    if (k == 0 && grp->getFiles().size() == 1) {
2515                        // If this is the only file left, we need to keep it.
2516                        // Otherwise the resource IDs we are using will be inconsistent
2517                        // with what we get when not stripping.  Sucky, but at least
2518                        // for now we can rely on the back-end doing another filtering
2519                        // pass to take this out and leave us with this resource name
2520                        // containing no entries.
2521                        continue;
2522                    }
2523                    if (file->getPath().getPathExtension() == ".xml") {
2524                        // We can't remove .xml files at this point, because when
2525                        // we parse them they may add identifier resources, so
2526                        // removing them can cause our resource identifiers to
2527                        // become inconsistent.
2528                        continue;
2529                    }
2530                    const ResTable_config& config(file->getGroupEntry().toParams());
2531                    if (!prefFilter.match(axis, config)) {
2532                        // This is a resource we would prefer not to have.  Check
2533                        // to see if have a similar variation that we would like
2534                        // to have and, if so, we can drop it.
2535
2536                        uint32_t bestDensity = config.density;
2537
2538                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2539                            if (m == k) continue;
2540                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2541                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2542                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2543                                if (axis == AXIS_DENSITY && preferredDensity > 0) {
2544                                    // See if there is a better density resource
2545                                    if (mconfig.density < bestDensity &&
2546                                            mconfig.density > preferredDensity &&
2547                                            bestDensity > preferredDensity) {
2548                                        // This density is between our best density and
2549                                        // the preferred density, therefore it is better.
2550                                        bestDensity = mconfig.density;
2551                                    } else if (mconfig.density > bestDensity &&
2552                                            bestDensity < preferredDensity) {
2553                                        // This density is better than our best density and
2554                                        // our best density was smaller than our preferred
2555                                        // density, so it is better.
2556                                        bestDensity = mconfig.density;
2557                                    }
2558                                } else if (prefFilter.match(axis, mconfig)) {
2559                                    if (bundle->getVerbose()) {
2560                                        printf("Pruning unneeded resource: %s\n",
2561                                                file->getPrintableSource().string());
2562                                    }
2563                                    grp->removeFile(k);
2564                                    k--;
2565                                    break;
2566                                }
2567                            }
2568                        }
2569
2570                        if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2571                                bestDensity != config.density) {
2572                            if (bundle->getVerbose()) {
2573                                printf("Pruning unneeded resource: %s\n",
2574                                        file->getPrintableSource().string());
2575                            }
2576                            grp->removeFile(k);
2577                            k--;
2578                        }
2579                    }
2580                }
2581            }
2582        }
2583    }
2584
2585    return NO_ERROR;
2586}
2587
2588sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2589{
2590    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2591    if (sym == NULL) {
2592        sym = new AaptSymbols();
2593        mSymbols.add(name, sym);
2594    }
2595    return sym;
2596}
2597
2598sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2599{
2600    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2601    if (sym == NULL) {
2602        sym = new AaptSymbols();
2603        mJavaSymbols.add(name, sym);
2604    }
2605    return sym;
2606}
2607
2608status_t AaptAssets::applyJavaSymbols()
2609{
2610    size_t N = mJavaSymbols.size();
2611    for (size_t i=0; i<N; i++) {
2612        const String8& name = mJavaSymbols.keyAt(i);
2613        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2614        ssize_t pos = mSymbols.indexOfKey(name);
2615        if (pos < 0) {
2616            SourcePos pos;
2617            pos.error("Java symbol dir %s not defined\n", name.string());
2618            return UNKNOWN_ERROR;
2619        }
2620        //printf("**** applying java symbols in dir %s\n", name.string());
2621        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2622        if (err != NO_ERROR) {
2623            return err;
2624        }
2625    }
2626
2627    return NO_ERROR;
2628}
2629
2630bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2631    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2632    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2633    //        sym.isJavaSymbol ? 1 : 0);
2634    if (!mHavePrivateSymbols) return true;
2635    if (sym.isPublic) return true;
2636    if (includePrivate && sym.isJavaSymbol) return true;
2637    return false;
2638}
2639
2640status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2641{
2642    if (!mHaveIncludedAssets) {
2643        // Add in all includes.
2644        const Vector<const char*>& incl = bundle->getPackageIncludes();
2645        const size_t N=incl.size();
2646        for (size_t i=0; i<N; i++) {
2647            if (bundle->getVerbose())
2648                printf("Including resources from package: %s\n", incl[i]);
2649            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2650                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2651                        incl[i]);
2652                return UNKNOWN_ERROR;
2653            }
2654        }
2655        mHaveIncludedAssets = true;
2656    }
2657
2658    return NO_ERROR;
2659}
2660
2661status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2662{
2663    const ResTable& res = getIncludedResources();
2664    // XXX dirty!
2665    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2666}
2667
2668const ResTable& AaptAssets::getIncludedResources() const
2669{
2670    return mIncludedAssets.getResources(false);
2671}
2672
2673void AaptAssets::print(const String8& prefix) const
2674{
2675    String8 innerPrefix(prefix);
2676    innerPrefix.append("  ");
2677    String8 innerInnerPrefix(innerPrefix);
2678    innerInnerPrefix.append("  ");
2679    printf("%sConfigurations:\n", prefix.string());
2680    const size_t N=mGroupEntries.size();
2681    for (size_t i=0; i<N; i++) {
2682        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2683        printf("%s %s\n", prefix.string(),
2684                cname != "" ? cname.string() : "(default)");
2685    }
2686
2687    printf("\n%sFiles:\n", prefix.string());
2688    AaptDir::print(innerPrefix);
2689
2690    printf("\n%sResource Dirs:\n", prefix.string());
2691    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2692    const size_t NR = resdirs.size();
2693    for (size_t i=0; i<NR; i++) {
2694        const sp<AaptDir>& d = resdirs.itemAt(i);
2695        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2696        d->print(innerInnerPrefix);
2697    }
2698}
2699
2700sp<AaptDir> AaptAssets::resDir(const String8& name) const
2701{
2702    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2703    const size_t N = resdirs.size();
2704    for (size_t i=0; i<N; i++) {
2705        const sp<AaptDir>& d = resdirs.itemAt(i);
2706        if (d->getLeaf() == name) {
2707            return d;
2708        }
2709    }
2710    return NULL;
2711}
2712
2713bool
2714valid_symbol_name(const String8& symbol)
2715{
2716    static char const * const KEYWORDS[] = {
2717        "abstract", "assert", "boolean", "break",
2718        "byte", "case", "catch", "char", "class", "const", "continue",
2719        "default", "do", "double", "else", "enum", "extends", "final",
2720        "finally", "float", "for", "goto", "if", "implements", "import",
2721        "instanceof", "int", "interface", "long", "native", "new", "package",
2722        "private", "protected", "public", "return", "short", "static",
2723        "strictfp", "super", "switch", "synchronized", "this", "throw",
2724        "throws", "transient", "try", "void", "volatile", "while",
2725        "true", "false", "null",
2726        NULL
2727    };
2728    const char*const* k = KEYWORDS;
2729    const char*const s = symbol.string();
2730    while (*k) {
2731        if (0 == strcmp(s, *k)) {
2732            return false;
2733        }
2734        k++;
2735    }
2736    return true;
2737}
2738