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