AaptAssets.cpp revision 94c40a4cf90933216e6d656213dfdfa19c68a9c3
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                if (i > 0 && count > 0) {
2155                  count = current->filter(bundle);
2156                }
2157
2158                if (count < 0) {
2159                    totalCount = count;
2160                    goto bail;
2161                }
2162                totalCount += count;
2163            }
2164            else {
2165                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2166                return UNKNOWN_ERROR;
2167            }
2168        }
2169
2170    }
2171    /*
2172     * Now do any additional raw files.
2173     */
2174    for (int arg=0; arg<N; arg++) {
2175        const char* assetDir = bundle->getFileSpecEntry(arg);
2176
2177        FileType type = getFileType(assetDir);
2178        if (type == kFileTypeNonexistent) {
2179            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2180            return UNKNOWN_ERROR;
2181        }
2182        if (type != kFileTypeDirectory) {
2183            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2184            return UNKNOWN_ERROR;
2185        }
2186
2187        String8 assetRoot(assetDir);
2188
2189        if (bundle->getVerbose())
2190            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2191
2192        /*
2193         * Do a recursive traversal of subdir tree.  We don't make any
2194         * guarantees about ordering, so we're okay with an inorder search
2195         * using whatever order the OS happens to hand back to us.
2196         */
2197        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2198        if (count < 0) {
2199            /* failure; report error and remove archive */
2200            totalCount = count;
2201            goto bail;
2202        }
2203        totalCount += count;
2204
2205        if (bundle->getVerbose())
2206            printf("Found %d asset file%s in %s\n",
2207                   count, (count==1) ? "" : "s", assetDir);
2208    }
2209
2210    count = validate();
2211    if (count != NO_ERROR) {
2212        totalCount = count;
2213        goto bail;
2214    }
2215
2216    count = filter(bundle);
2217    if (count != NO_ERROR) {
2218        totalCount = count;
2219        goto bail;
2220    }
2221
2222bail:
2223    return totalCount;
2224}
2225
2226ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2227                                    const AaptGroupEntry& kind,
2228                                    const String8& resType,
2229                                    sp<FilePathStore>& fullResPaths)
2230{
2231    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2232    if (res > 0) {
2233        mGroupEntries.add(kind);
2234    }
2235
2236    return res;
2237}
2238
2239ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2240{
2241    ssize_t err = 0;
2242
2243    DIR* dir = opendir(srcDir.string());
2244    if (dir == NULL) {
2245        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2246        return UNKNOWN_ERROR;
2247    }
2248
2249    status_t count = 0;
2250
2251    /*
2252     * Run through the directory, looking for dirs that match the
2253     * expected pattern.
2254     */
2255    while (1) {
2256        struct dirent* entry = readdir(dir);
2257        if (entry == NULL) {
2258            break;
2259        }
2260
2261        if (isHidden(srcDir.string(), entry->d_name)) {
2262            continue;
2263        }
2264
2265        String8 subdirName(srcDir);
2266        subdirName.appendPath(entry->d_name);
2267
2268        AaptGroupEntry group;
2269        String8 resType;
2270        bool b = group.initFromDirName(entry->d_name, &resType);
2271        if (!b) {
2272            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2273                    entry->d_name);
2274            err = -1;
2275            continue;
2276        }
2277
2278        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2279            int maxResInt = atoi(bundle->getMaxResVersion());
2280            const char *verString = group.getVersionString().string();
2281            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2282            if (dirVersionInt > maxResInt) {
2283              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2284              continue;
2285            }
2286        }
2287
2288        FileType type = getFileType(subdirName.string());
2289
2290        if (type == kFileTypeDirectory) {
2291            sp<AaptDir> dir = makeDir(resType);
2292            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2293                                                resType, mFullResPaths);
2294            if (res < 0) {
2295                count = res;
2296                goto bail;
2297            }
2298            if (res > 0) {
2299                mGroupEntries.add(group);
2300                count += res;
2301            }
2302
2303            // Only add this directory if we don't already have a resource dir
2304            // for the current type.  This ensures that we only add the dir once
2305            // for all configs.
2306            sp<AaptDir> rdir = resDir(resType);
2307            if (rdir == NULL) {
2308                mResDirs.add(dir);
2309            }
2310        } else {
2311            if (bundle->getVerbose()) {
2312                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2313            }
2314        }
2315    }
2316
2317bail:
2318    closedir(dir);
2319    dir = NULL;
2320
2321    if (err != 0) {
2322        return err;
2323    }
2324    return count;
2325}
2326
2327ssize_t
2328AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2329{
2330    int count = 0;
2331    SortedVector<AaptGroupEntry> entries;
2332
2333    ZipFile* zip = new ZipFile;
2334    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2335    if (err != NO_ERROR) {
2336        fprintf(stderr, "error opening zip file %s\n", filename);
2337        count = err;
2338        delete zip;
2339        return -1;
2340    }
2341
2342    const int N = zip->getNumEntries();
2343    for (int i=0; i<N; i++) {
2344        ZipEntry* entry = zip->getEntryByIndex(i);
2345        if (entry->getDeleted()) {
2346            continue;
2347        }
2348
2349        String8 entryName(entry->getFileName());
2350
2351        String8 dirName = entryName.getPathDir();
2352        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2353
2354        String8 resType;
2355        AaptGroupEntry kind;
2356
2357        String8 remain;
2358        if (entryName.walkPath(&remain) == kResourceDir) {
2359            // these are the resources, pull their type out of the directory name
2360            kind.initFromDirName(remain.walkPath().string(), &resType);
2361        } else {
2362            // these are untyped and don't have an AaptGroupEntry
2363        }
2364        if (entries.indexOf(kind) < 0) {
2365            entries.add(kind);
2366            mGroupEntries.add(kind);
2367        }
2368
2369        // use the one from the zip file if they both exist.
2370        dir->removeFile(entryName.getPathLeaf());
2371
2372        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2373        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2374        if (err != NO_ERROR) {
2375            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2376            count = err;
2377            goto bail;
2378        }
2379        file->setCompressionMethod(entry->getCompressionMethod());
2380
2381#if 0
2382        if (entryName == "AndroidManifest.xml") {
2383            printf("AndroidManifest.xml\n");
2384        }
2385        printf("\n\nfile: %s\n", entryName.string());
2386#endif
2387
2388        size_t len = entry->getUncompressedLen();
2389        void* data = zip->uncompress(entry);
2390        void* buf = file->editData(len);
2391        memcpy(buf, data, len);
2392
2393#if 0
2394        const int OFF = 0;
2395        const unsigned char* p = (unsigned char*)data;
2396        const unsigned char* end = p+len;
2397        p += OFF;
2398        for (int i=0; i<32 && p < end; i++) {
2399            printf("0x%03x ", i*0x10 + OFF);
2400            for (int j=0; j<0x10 && p < end; j++) {
2401                printf(" %02x", *p);
2402                p++;
2403            }
2404            printf("\n");
2405        }
2406#endif
2407
2408        free(data);
2409
2410        count++;
2411    }
2412
2413bail:
2414    delete zip;
2415    return count;
2416}
2417
2418status_t AaptAssets::filter(Bundle* bundle)
2419{
2420    ResourceFilter reqFilter;
2421    status_t err = reqFilter.parse(bundle->getConfigurations());
2422    if (err != NO_ERROR) {
2423        return err;
2424    }
2425
2426    ResourceFilter prefFilter;
2427    err = prefFilter.parse(bundle->getPreferredConfigurations());
2428    if (err != NO_ERROR) {
2429        return err;
2430    }
2431
2432    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2433        return NO_ERROR;
2434    }
2435
2436    if (bundle->getVerbose()) {
2437        if (!reqFilter.isEmpty()) {
2438            printf("Applying required filter: %s\n",
2439                    bundle->getConfigurations());
2440        }
2441        if (!prefFilter.isEmpty()) {
2442            printf("Applying preferred filter: %s\n",
2443                    bundle->getPreferredConfigurations());
2444        }
2445    }
2446
2447    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2448    const size_t ND = resdirs.size();
2449    for (size_t i=0; i<ND; i++) {
2450        const sp<AaptDir>& dir = resdirs.itemAt(i);
2451        if (dir->getLeaf() == kValuesDir) {
2452            // The "value" dir is special since a single file defines
2453            // multiple resources, so we can not do filtering on the
2454            // files themselves.
2455            continue;
2456        }
2457        if (dir->getLeaf() == kMipmapDir) {
2458            // We also skip the "mipmap" directory, since the point of this
2459            // is to include all densities without stripping.  If you put
2460            // other configurations in here as well they won't be stripped
2461            // either...  So don't do that.  Seriously.  What is wrong with you?
2462            continue;
2463        }
2464
2465        const size_t NG = dir->getFiles().size();
2466        for (size_t j=0; j<NG; j++) {
2467            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2468
2469            // First remove any configurations we know we don't need.
2470            for (size_t k=0; k<grp->getFiles().size(); k++) {
2471                sp<AaptFile> file = grp->getFiles().valueAt(k);
2472                if (k == 0 && grp->getFiles().size() == 1) {
2473                    // If this is the only file left, we need to keep it.
2474                    // Otherwise the resource IDs we are using will be inconsistent
2475                    // with what we get when not stripping.  Sucky, but at least
2476                    // for now we can rely on the back-end doing another filtering
2477                    // pass to take this out and leave us with this resource name
2478                    // containing no entries.
2479                    continue;
2480                }
2481                if (file->getPath().getPathExtension() == ".xml") {
2482                    // We can't remove .xml files at this point, because when
2483                    // we parse them they may add identifier resources, so
2484                    // removing them can cause our resource identifiers to
2485                    // become inconsistent.
2486                    continue;
2487                }
2488                const ResTable_config& config(file->getGroupEntry().toParams());
2489                if (!reqFilter.match(config)) {
2490                    if (bundle->getVerbose()) {
2491                        printf("Pruning unneeded resource: %s\n",
2492                                file->getPrintableSource().string());
2493                    }
2494                    grp->removeFile(k);
2495                    k--;
2496                }
2497            }
2498
2499            // Quick check: no preferred filters, nothing more to do.
2500            if (prefFilter.isEmpty()) {
2501                continue;
2502            }
2503
2504            // Get the preferred density if there is one. We do not match exactly for density.
2505            // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2506            // pick xhdpi.
2507            uint32_t preferredDensity = 0;
2508            const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
2509            if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
2510                preferredDensity = (*preferredConfigs)[0];
2511            }
2512
2513            // Now deal with preferred configurations.
2514            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2515                for (size_t k=0; k<grp->getFiles().size(); k++) {
2516                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2517                    if (k == 0 && grp->getFiles().size() == 1) {
2518                        // If this is the only file left, we need to keep it.
2519                        // Otherwise the resource IDs we are using will be inconsistent
2520                        // with what we get when not stripping.  Sucky, but at least
2521                        // for now we can rely on the back-end doing another filtering
2522                        // pass to take this out and leave us with this resource name
2523                        // containing no entries.
2524                        continue;
2525                    }
2526                    if (file->getPath().getPathExtension() == ".xml") {
2527                        // We can't remove .xml files at this point, because when
2528                        // we parse them they may add identifier resources, so
2529                        // removing them can cause our resource identifiers to
2530                        // become inconsistent.
2531                        continue;
2532                    }
2533                    const ResTable_config& config(file->getGroupEntry().toParams());
2534                    if (!prefFilter.match(axis, config)) {
2535                        // This is a resource we would prefer not to have.  Check
2536                        // to see if have a similar variation that we would like
2537                        // to have and, if so, we can drop it.
2538
2539                        uint32_t bestDensity = config.density;
2540
2541                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2542                            if (m == k) continue;
2543                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2544                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2545                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2546                                if (axis == AXIS_DENSITY && preferredDensity > 0) {
2547                                    // See if there is a better density resource
2548                                    if (mconfig.density < bestDensity &&
2549                                            mconfig.density > preferredDensity &&
2550                                            bestDensity > preferredDensity) {
2551                                        // This density is between our best density and
2552                                        // the preferred density, therefore it is better.
2553                                        bestDensity = mconfig.density;
2554                                    } else if (mconfig.density > bestDensity &&
2555                                            bestDensity < preferredDensity) {
2556                                        // This density is better than our best density and
2557                                        // our best density was smaller than our preferred
2558                                        // density, so it is better.
2559                                        bestDensity = mconfig.density;
2560                                    }
2561                                } else if (prefFilter.match(axis, mconfig)) {
2562                                    if (bundle->getVerbose()) {
2563                                        printf("Pruning unneeded resource: %s\n",
2564                                                file->getPrintableSource().string());
2565                                    }
2566                                    grp->removeFile(k);
2567                                    k--;
2568                                    break;
2569                                }
2570                            }
2571                        }
2572
2573                        if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2574                                bestDensity != config.density) {
2575                            if (bundle->getVerbose()) {
2576                                printf("Pruning unneeded resource: %s\n",
2577                                        file->getPrintableSource().string());
2578                            }
2579                            grp->removeFile(k);
2580                            k--;
2581                        }
2582                    }
2583                }
2584            }
2585        }
2586    }
2587
2588    return NO_ERROR;
2589}
2590
2591sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2592{
2593    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2594    if (sym == NULL) {
2595        sym = new AaptSymbols();
2596        mSymbols.add(name, sym);
2597    }
2598    return sym;
2599}
2600
2601sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2602{
2603    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2604    if (sym == NULL) {
2605        sym = new AaptSymbols();
2606        mJavaSymbols.add(name, sym);
2607    }
2608    return sym;
2609}
2610
2611status_t AaptAssets::applyJavaSymbols()
2612{
2613    size_t N = mJavaSymbols.size();
2614    for (size_t i=0; i<N; i++) {
2615        const String8& name = mJavaSymbols.keyAt(i);
2616        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2617        ssize_t pos = mSymbols.indexOfKey(name);
2618        if (pos < 0) {
2619            SourcePos pos;
2620            pos.error("Java symbol dir %s not defined\n", name.string());
2621            return UNKNOWN_ERROR;
2622        }
2623        //printf("**** applying java symbols in dir %s\n", name.string());
2624        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2625        if (err != NO_ERROR) {
2626            return err;
2627        }
2628    }
2629
2630    return NO_ERROR;
2631}
2632
2633bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2634    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2635    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2636    //        sym.isJavaSymbol ? 1 : 0);
2637    if (!mHavePrivateSymbols) return true;
2638    if (sym.isPublic) return true;
2639    if (includePrivate && sym.isJavaSymbol) return true;
2640    return false;
2641}
2642
2643status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2644{
2645    if (!mHaveIncludedAssets) {
2646        // Add in all includes.
2647        const Vector<const char*>& incl = bundle->getPackageIncludes();
2648        const size_t N=incl.size();
2649        for (size_t i=0; i<N; i++) {
2650            if (bundle->getVerbose())
2651                printf("Including resources from package: %s\n", incl[i]);
2652            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2653                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2654                        incl[i]);
2655                return UNKNOWN_ERROR;
2656            }
2657        }
2658        mHaveIncludedAssets = true;
2659    }
2660
2661    return NO_ERROR;
2662}
2663
2664status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2665{
2666    const ResTable& res = getIncludedResources();
2667    // XXX dirty!
2668    return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
2669}
2670
2671const ResTable& AaptAssets::getIncludedResources() const
2672{
2673    return mIncludedAssets.getResources(false);
2674}
2675
2676void AaptAssets::print(const String8& prefix) const
2677{
2678    String8 innerPrefix(prefix);
2679    innerPrefix.append("  ");
2680    String8 innerInnerPrefix(innerPrefix);
2681    innerInnerPrefix.append("  ");
2682    printf("%sConfigurations:\n", prefix.string());
2683    const size_t N=mGroupEntries.size();
2684    for (size_t i=0; i<N; i++) {
2685        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2686        printf("%s %s\n", prefix.string(),
2687                cname != "" ? cname.string() : "(default)");
2688    }
2689
2690    printf("\n%sFiles:\n", prefix.string());
2691    AaptDir::print(innerPrefix);
2692
2693    printf("\n%sResource Dirs:\n", prefix.string());
2694    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2695    const size_t NR = resdirs.size();
2696    for (size_t i=0; i<NR; i++) {
2697        const sp<AaptDir>& d = resdirs.itemAt(i);
2698        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2699        d->print(innerInnerPrefix);
2700    }
2701}
2702
2703sp<AaptDir> AaptAssets::resDir(const String8& name) const
2704{
2705    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2706    const size_t N = resdirs.size();
2707    for (size_t i=0; i<N; i++) {
2708        const sp<AaptDir>& d = resdirs.itemAt(i);
2709        if (d->getLeaf() == name) {
2710            return d;
2711        }
2712    }
2713    return NULL;
2714}
2715
2716bool
2717valid_symbol_name(const String8& symbol)
2718{
2719    static char const * const KEYWORDS[] = {
2720        "abstract", "assert", "boolean", "break",
2721        "byte", "case", "catch", "char", "class", "const", "continue",
2722        "default", "do", "double", "else", "enum", "extends", "final",
2723        "finally", "float", "for", "goto", "if", "implements", "import",
2724        "instanceof", "int", "interface", "long", "native", "new", "package",
2725        "private", "protected", "public", "return", "short", "static",
2726        "strictfp", "super", "switch", "synchronized", "this", "throw",
2727        "throws", "transient", "try", "void", "volatile", "while",
2728        "true", "false", "null",
2729        NULL
2730    };
2731    const char*const* k = KEYWORDS;
2732    const char*const s = symbol.string();
2733    while (*k) {
2734        if (0 == strcmp(s, *k)) {
2735            return false;
2736        }
2737        k++;
2738    }
2739    return true;
2740}
2741