AaptAssets.cpp revision 1d766b53217f9e45472a01e84d2f1b02af36dec7
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)
1635{
1636    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1637        file->mPath = mPath;
1638        mFiles.add(file->getGroupEntry(), file);
1639        return NO_ERROR;
1640    }
1641
1642#if 0
1643    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1644            file->getSourceFile().string(),
1645            file->getGroupEntry().toDirName(String8()).string(),
1646            mLeaf.string(), mPath.string());
1647#endif
1648
1649    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1650                                               getPrintableSource().string());
1651    return UNKNOWN_ERROR;
1652}
1653
1654void AaptGroup::removeFile(size_t index)
1655{
1656	mFiles.removeItemsAt(index);
1657}
1658
1659void AaptGroup::print(const String8& prefix) const
1660{
1661    printf("%s%s\n", prefix.string(), getPath().string());
1662    const size_t N=mFiles.size();
1663    size_t i;
1664    for (i=0; i<N; i++) {
1665        sp<AaptFile> file = mFiles.valueAt(i);
1666        const AaptGroupEntry& e = file->getGroupEntry();
1667        if (file->hasData()) {
1668            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1669                    (int)file->getSize());
1670        } else {
1671            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1672                    file->getPrintableSource().string());
1673        }
1674        //printf("%s  File Group Entry: %s\n", prefix.string(),
1675        //        file->getGroupEntry().toDirName(String8()).string());
1676    }
1677}
1678
1679String8 AaptGroup::getPrintableSource() const
1680{
1681    if (mFiles.size() > 0) {
1682        // Arbitrarily pull the first source file out of the list.
1683        return mFiles.valueAt(0)->getPrintableSource();
1684    }
1685
1686    // Should never hit this case, but to be safe...
1687    return getPath();
1688
1689}
1690
1691// =========================================================================
1692// =========================================================================
1693// =========================================================================
1694
1695status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1696{
1697    if (mFiles.indexOfKey(name) >= 0) {
1698        return ALREADY_EXISTS;
1699    }
1700    mFiles.add(name, file);
1701    return NO_ERROR;
1702}
1703
1704status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1705{
1706    if (mDirs.indexOfKey(name) >= 0) {
1707        return ALREADY_EXISTS;
1708    }
1709    mDirs.add(name, dir);
1710    return NO_ERROR;
1711}
1712
1713sp<AaptDir> AaptDir::makeDir(const String8& path)
1714{
1715    String8 name;
1716    String8 remain = path;
1717
1718    sp<AaptDir> subdir = this;
1719    while (name = remain.walkPath(&remain), remain != "") {
1720        subdir = subdir->makeDir(name);
1721    }
1722
1723    ssize_t i = subdir->mDirs.indexOfKey(name);
1724    if (i >= 0) {
1725        return subdir->mDirs.valueAt(i);
1726    }
1727    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1728    subdir->mDirs.add(name, dir);
1729    return dir;
1730}
1731
1732void AaptDir::removeFile(const String8& name)
1733{
1734    mFiles.removeItem(name);
1735}
1736
1737void AaptDir::removeDir(const String8& name)
1738{
1739    mDirs.removeItem(name);
1740}
1741
1742status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1743{
1744    sp<AaptGroup> group;
1745    if (mFiles.indexOfKey(leafName) >= 0) {
1746        group = mFiles.valueFor(leafName);
1747    } else {
1748        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1749        mFiles.add(leafName, group);
1750    }
1751
1752    return group->addFile(file);
1753}
1754
1755ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1756                            const AaptGroupEntry& kind, const String8& resType,
1757                            sp<FilePathStore>& fullResPaths)
1758{
1759    Vector<String8> fileNames;
1760    {
1761        DIR* dir = NULL;
1762
1763        dir = opendir(srcDir.string());
1764        if (dir == NULL) {
1765            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1766            return UNKNOWN_ERROR;
1767        }
1768
1769        /*
1770         * Slurp the filenames out of the directory.
1771         */
1772        while (1) {
1773            struct dirent* entry;
1774
1775            entry = readdir(dir);
1776            if (entry == NULL)
1777                break;
1778
1779            if (isHidden(srcDir.string(), entry->d_name))
1780                continue;
1781
1782            String8 name(entry->d_name);
1783            fileNames.add(name);
1784            // Add fully qualified path for dependency purposes
1785            // if we're collecting them
1786            if (fullResPaths != NULL) {
1787                fullResPaths->add(srcDir.appendPathCopy(name));
1788            }
1789        }
1790        closedir(dir);
1791    }
1792
1793    ssize_t count = 0;
1794
1795    /*
1796     * Stash away the files and recursively descend into subdirectories.
1797     */
1798    const size_t N = fileNames.size();
1799    size_t i;
1800    for (i = 0; i < N; i++) {
1801        String8 pathName(srcDir);
1802        FileType type;
1803
1804        pathName.appendPath(fileNames[i].string());
1805        type = getFileType(pathName.string());
1806        if (type == kFileTypeDirectory) {
1807            sp<AaptDir> subdir;
1808            bool notAdded = false;
1809            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1810                subdir = mDirs.valueFor(fileNames[i]);
1811            } else {
1812                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1813                notAdded = true;
1814            }
1815            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1816                                                resType, fullResPaths);
1817            if (res < NO_ERROR) {
1818                return res;
1819            }
1820            if (res > 0 && notAdded) {
1821                mDirs.add(fileNames[i], subdir);
1822            }
1823            count += res;
1824        } else if (type == kFileTypeRegular) {
1825            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1826            status_t err = addLeafFile(fileNames[i], file);
1827            if (err != NO_ERROR) {
1828                return err;
1829            }
1830
1831            count++;
1832
1833        } else {
1834            if (bundle->getVerbose())
1835                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1836        }
1837    }
1838
1839    return count;
1840}
1841
1842status_t AaptDir::validate() const
1843{
1844    const size_t NF = mFiles.size();
1845    const size_t ND = mDirs.size();
1846    size_t i;
1847    for (i = 0; i < NF; i++) {
1848        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1849            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1850                    "Invalid filename.  Unable to add.");
1851            return UNKNOWN_ERROR;
1852        }
1853
1854        size_t j;
1855        for (j = i+1; j < NF; j++) {
1856            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1857                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1858                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1859                        "File is case-insensitive equivalent to: %s",
1860                        mFiles.valueAt(j)->getPrintableSource().string());
1861                return UNKNOWN_ERROR;
1862            }
1863
1864            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1865            // (this is mostly caught by the "marked" stuff, below)
1866        }
1867
1868        for (j = 0; j < ND; j++) {
1869            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1870                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1871                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1872                        "File conflicts with dir from: %s",
1873                        mDirs.valueAt(j)->getPrintableSource().string());
1874                return UNKNOWN_ERROR;
1875            }
1876        }
1877    }
1878
1879    for (i = 0; i < ND; i++) {
1880        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1881            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1882                    "Invalid directory name, unable to add.");
1883            return UNKNOWN_ERROR;
1884        }
1885
1886        size_t j;
1887        for (j = i+1; j < ND; j++) {
1888            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1889                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1890                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1891                        "Directory is case-insensitive equivalent to: %s",
1892                        mDirs.valueAt(j)->getPrintableSource().string());
1893                return UNKNOWN_ERROR;
1894            }
1895        }
1896
1897        status_t err = mDirs.valueAt(i)->validate();
1898        if (err != NO_ERROR) {
1899            return err;
1900        }
1901    }
1902
1903    return NO_ERROR;
1904}
1905
1906void AaptDir::print(const String8& prefix) const
1907{
1908    const size_t ND=getDirs().size();
1909    size_t i;
1910    for (i=0; i<ND; i++) {
1911        getDirs().valueAt(i)->print(prefix);
1912    }
1913
1914    const size_t NF=getFiles().size();
1915    for (i=0; i<NF; i++) {
1916        getFiles().valueAt(i)->print(prefix);
1917    }
1918}
1919
1920String8 AaptDir::getPrintableSource() const
1921{
1922    if (mFiles.size() > 0) {
1923        // Arbitrarily pull the first file out of the list as the source dir.
1924        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1925    }
1926    if (mDirs.size() > 0) {
1927        // Or arbitrarily pull the first dir out of the list as the source dir.
1928        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1929    }
1930
1931    // Should never hit this case, but to be safe...
1932    return mPath;
1933
1934}
1935
1936// =========================================================================
1937// =========================================================================
1938// =========================================================================
1939
1940status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
1941{
1942    status_t err = NO_ERROR;
1943    size_t N = javaSymbols->mSymbols.size();
1944    for (size_t i=0; i<N; i++) {
1945        const String8& name = javaSymbols->mSymbols.keyAt(i);
1946        const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
1947        ssize_t pos = mSymbols.indexOfKey(name);
1948        if (pos < 0) {
1949            entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
1950            err = UNKNOWN_ERROR;
1951            continue;
1952        }
1953        //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
1954        //        i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
1955        mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
1956    }
1957
1958    N = javaSymbols->mNestedSymbols.size();
1959    for (size_t i=0; i<N; i++) {
1960        const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
1961        const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
1962        ssize_t pos = mNestedSymbols.indexOfKey(name);
1963        if (pos < 0) {
1964            SourcePos pos;
1965            pos.error("Java symbol dir %s not defined\n", name.string());
1966            err = UNKNOWN_ERROR;
1967            continue;
1968        }
1969        //printf("**** applying java symbols in dir %s\n", name.string());
1970        status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1971        if (myerr != NO_ERROR) {
1972            err = myerr;
1973        }
1974    }
1975
1976    return err;
1977}
1978
1979// =========================================================================
1980// =========================================================================
1981// =========================================================================
1982
1983AaptAssets::AaptAssets()
1984    : AaptDir(String8(), String8()),
1985      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1986{
1987}
1988
1989const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
1990    if (mChanged) {
1991    }
1992    return mGroupEntries;
1993}
1994
1995status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
1996{
1997    mChanged = true;
1998    return AaptDir::addFile(name, file);
1999}
2000
2001sp<AaptFile> AaptAssets::addFile(
2002        const String8& filePath, const AaptGroupEntry& entry,
2003        const String8& srcDir, sp<AaptGroup>* outGroup,
2004        const String8& resType)
2005{
2006    sp<AaptDir> dir = this;
2007    sp<AaptGroup> group;
2008    sp<AaptFile> file;
2009    String8 root, remain(filePath), partialPath;
2010    while (remain.length() > 0) {
2011        root = remain.walkPath(&remain);
2012        partialPath.appendPath(root);
2013
2014        const String8 rootStr(root);
2015
2016        if (remain.length() == 0) {
2017            ssize_t i = dir->getFiles().indexOfKey(rootStr);
2018            if (i >= 0) {
2019                group = dir->getFiles().valueAt(i);
2020            } else {
2021                group = new AaptGroup(rootStr, filePath);
2022                status_t res = dir->addFile(rootStr, group);
2023                if (res != NO_ERROR) {
2024                    return NULL;
2025                }
2026            }
2027            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2028            status_t res = group->addFile(file);
2029            if (res != NO_ERROR) {
2030                return NULL;
2031            }
2032            break;
2033
2034        } else {
2035            ssize_t i = dir->getDirs().indexOfKey(rootStr);
2036            if (i >= 0) {
2037                dir = dir->getDirs().valueAt(i);
2038            } else {
2039                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2040                status_t res = dir->addDir(rootStr, subdir);
2041                if (res != NO_ERROR) {
2042                    return NULL;
2043                }
2044                dir = subdir;
2045            }
2046        }
2047    }
2048
2049    mGroupEntries.add(entry);
2050    if (outGroup) *outGroup = group;
2051    return file;
2052}
2053
2054void AaptAssets::addResource(const String8& leafName, const String8& path,
2055                const sp<AaptFile>& file, const String8& resType)
2056{
2057    sp<AaptDir> res = AaptDir::makeDir(kResString);
2058    String8 dirname = file->getGroupEntry().toDirName(resType);
2059    sp<AaptDir> subdir = res->makeDir(dirname);
2060    sp<AaptGroup> grr = new AaptGroup(leafName, path);
2061    grr->addFile(file);
2062
2063    subdir->addFile(leafName, grr);
2064}
2065
2066
2067ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2068{
2069    int count;
2070    int totalCount = 0;
2071    FileType type;
2072    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2073    const size_t dirCount =resDirs.size();
2074    sp<AaptAssets> current = this;
2075
2076    const int N = bundle->getFileSpecCount();
2077
2078    /*
2079     * If a package manifest was specified, include that first.
2080     */
2081    if (bundle->getAndroidManifestFile() != NULL) {
2082        // place at root of zip.
2083        String8 srcFile(bundle->getAndroidManifestFile());
2084        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2085                NULL, String8());
2086        totalCount++;
2087    }
2088
2089    /*
2090     * If a directory of custom assets was supplied, slurp 'em up.
2091     */
2092    if (bundle->getAssetSourceDir()) {
2093        const char* assetDir = bundle->getAssetSourceDir();
2094
2095        FileType type = getFileType(assetDir);
2096        if (type == kFileTypeNonexistent) {
2097            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
2098            return UNKNOWN_ERROR;
2099        }
2100        if (type != kFileTypeDirectory) {
2101            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2102            return UNKNOWN_ERROR;
2103        }
2104
2105        String8 assetRoot(assetDir);
2106        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2107        AaptGroupEntry group;
2108        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2109                                            String8(), mFullAssetPaths);
2110        if (count < 0) {
2111            totalCount = count;
2112            goto bail;
2113        }
2114        if (count > 0) {
2115            mGroupEntries.add(group);
2116        }
2117        totalCount += count;
2118
2119        if (bundle->getVerbose())
2120            printf("Found %d custom asset file%s in %s\n",
2121                   count, (count==1) ? "" : "s", assetDir);
2122    }
2123
2124    /*
2125     * If a directory of resource-specific assets was supplied, slurp 'em up.
2126     */
2127    for (size_t i=0; i<dirCount; i++) {
2128        const char *res = resDirs[i];
2129        if (res) {
2130            type = getFileType(res);
2131            if (type == kFileTypeNonexistent) {
2132                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2133                return UNKNOWN_ERROR;
2134            }
2135            if (type == kFileTypeDirectory) {
2136                if (i>0) {
2137                    sp<AaptAssets> nextOverlay = new AaptAssets();
2138                    current->setOverlay(nextOverlay);
2139                    current = nextOverlay;
2140                    current->setFullResPaths(mFullResPaths);
2141                }
2142                count = current->slurpResourceTree(bundle, String8(res));
2143
2144                if (count < 0) {
2145                    totalCount = count;
2146                    goto bail;
2147                }
2148                totalCount += count;
2149            }
2150            else {
2151                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2152                return UNKNOWN_ERROR;
2153            }
2154        }
2155
2156    }
2157    /*
2158     * Now do any additional raw files.
2159     */
2160    for (int arg=0; arg<N; arg++) {
2161        const char* assetDir = bundle->getFileSpecEntry(arg);
2162
2163        FileType type = getFileType(assetDir);
2164        if (type == kFileTypeNonexistent) {
2165            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2166            return UNKNOWN_ERROR;
2167        }
2168        if (type != kFileTypeDirectory) {
2169            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2170            return UNKNOWN_ERROR;
2171        }
2172
2173        String8 assetRoot(assetDir);
2174
2175        if (bundle->getVerbose())
2176            printf("Processing raw dir '%s'\n", (const char*) assetDir);
2177
2178        /*
2179         * Do a recursive traversal of subdir tree.  We don't make any
2180         * guarantees about ordering, so we're okay with an inorder search
2181         * using whatever order the OS happens to hand back to us.
2182         */
2183        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2184        if (count < 0) {
2185            /* failure; report error and remove archive */
2186            totalCount = count;
2187            goto bail;
2188        }
2189        totalCount += count;
2190
2191        if (bundle->getVerbose())
2192            printf("Found %d asset file%s in %s\n",
2193                   count, (count==1) ? "" : "s", assetDir);
2194    }
2195
2196    count = validate();
2197    if (count != NO_ERROR) {
2198        totalCount = count;
2199        goto bail;
2200    }
2201
2202    count = filter(bundle);
2203    if (count != NO_ERROR) {
2204        totalCount = count;
2205        goto bail;
2206    }
2207
2208bail:
2209    return totalCount;
2210}
2211
2212ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2213                                    const AaptGroupEntry& kind,
2214                                    const String8& resType,
2215                                    sp<FilePathStore>& fullResPaths)
2216{
2217    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2218    if (res > 0) {
2219        mGroupEntries.add(kind);
2220    }
2221
2222    return res;
2223}
2224
2225ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2226{
2227    ssize_t err = 0;
2228
2229    DIR* dir = opendir(srcDir.string());
2230    if (dir == NULL) {
2231        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2232        return UNKNOWN_ERROR;
2233    }
2234
2235    status_t count = 0;
2236
2237    /*
2238     * Run through the directory, looking for dirs that match the
2239     * expected pattern.
2240     */
2241    while (1) {
2242        struct dirent* entry = readdir(dir);
2243        if (entry == NULL) {
2244            break;
2245        }
2246
2247        if (isHidden(srcDir.string(), entry->d_name)) {
2248            continue;
2249        }
2250
2251        String8 subdirName(srcDir);
2252        subdirName.appendPath(entry->d_name);
2253
2254        AaptGroupEntry group;
2255        String8 resType;
2256        bool b = group.initFromDirName(entry->d_name, &resType);
2257        if (!b) {
2258            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2259                    entry->d_name);
2260            err = -1;
2261            continue;
2262        }
2263
2264        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2265            int maxResInt = atoi(bundle->getMaxResVersion());
2266            const char *verString = group.getVersionString().string();
2267            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2268            if (dirVersionInt > maxResInt) {
2269              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2270              continue;
2271            }
2272        }
2273
2274        FileType type = getFileType(subdirName.string());
2275
2276        if (type == kFileTypeDirectory) {
2277            sp<AaptDir> dir = makeDir(resType);
2278            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2279                                                resType, mFullResPaths);
2280            if (res < 0) {
2281                count = res;
2282                goto bail;
2283            }
2284            if (res > 0) {
2285                mGroupEntries.add(group);
2286                count += res;
2287            }
2288
2289            // Only add this directory if we don't already have a resource dir
2290            // for the current type.  This ensures that we only add the dir once
2291            // for all configs.
2292            sp<AaptDir> rdir = resDir(resType);
2293            if (rdir == NULL) {
2294                mResDirs.add(dir);
2295            }
2296        } else {
2297            if (bundle->getVerbose()) {
2298                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2299            }
2300        }
2301    }
2302
2303bail:
2304    closedir(dir);
2305    dir = NULL;
2306
2307    if (err != 0) {
2308        return err;
2309    }
2310    return count;
2311}
2312
2313ssize_t
2314AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2315{
2316    int count = 0;
2317    SortedVector<AaptGroupEntry> entries;
2318
2319    ZipFile* zip = new ZipFile;
2320    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2321    if (err != NO_ERROR) {
2322        fprintf(stderr, "error opening zip file %s\n", filename);
2323        count = err;
2324        delete zip;
2325        return -1;
2326    }
2327
2328    const int N = zip->getNumEntries();
2329    for (int i=0; i<N; i++) {
2330        ZipEntry* entry = zip->getEntryByIndex(i);
2331        if (entry->getDeleted()) {
2332            continue;
2333        }
2334
2335        String8 entryName(entry->getFileName());
2336
2337        String8 dirName = entryName.getPathDir();
2338        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2339
2340        String8 resType;
2341        AaptGroupEntry kind;
2342
2343        String8 remain;
2344        if (entryName.walkPath(&remain) == kResourceDir) {
2345            // these are the resources, pull their type out of the directory name
2346            kind.initFromDirName(remain.walkPath().string(), &resType);
2347        } else {
2348            // these are untyped and don't have an AaptGroupEntry
2349        }
2350        if (entries.indexOf(kind) < 0) {
2351            entries.add(kind);
2352            mGroupEntries.add(kind);
2353        }
2354
2355        // use the one from the zip file if they both exist.
2356        dir->removeFile(entryName.getPathLeaf());
2357
2358        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2359        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2360        if (err != NO_ERROR) {
2361            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2362            count = err;
2363            goto bail;
2364        }
2365        file->setCompressionMethod(entry->getCompressionMethod());
2366
2367#if 0
2368        if (entryName == "AndroidManifest.xml") {
2369            printf("AndroidManifest.xml\n");
2370        }
2371        printf("\n\nfile: %s\n", entryName.string());
2372#endif
2373
2374        size_t len = entry->getUncompressedLen();
2375        void* data = zip->uncompress(entry);
2376        void* buf = file->editData(len);
2377        memcpy(buf, data, len);
2378
2379#if 0
2380        const int OFF = 0;
2381        const unsigned char* p = (unsigned char*)data;
2382        const unsigned char* end = p+len;
2383        p += OFF;
2384        for (int i=0; i<32 && p < end; i++) {
2385            printf("0x%03x ", i*0x10 + OFF);
2386            for (int j=0; j<0x10 && p < end; j++) {
2387                printf(" %02x", *p);
2388                p++;
2389            }
2390            printf("\n");
2391        }
2392#endif
2393
2394        free(data);
2395
2396        count++;
2397    }
2398
2399bail:
2400    delete zip;
2401    return count;
2402}
2403
2404status_t AaptAssets::filter(Bundle* bundle)
2405{
2406    ResourceFilter reqFilter;
2407    status_t err = reqFilter.parse(bundle->getConfigurations());
2408    if (err != NO_ERROR) {
2409        return err;
2410    }
2411
2412    ResourceFilter prefFilter;
2413    err = prefFilter.parse(bundle->getPreferredConfigurations());
2414    if (err != NO_ERROR) {
2415        return err;
2416    }
2417
2418    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2419        return NO_ERROR;
2420    }
2421
2422    if (bundle->getVerbose()) {
2423        if (!reqFilter.isEmpty()) {
2424            printf("Applying required filter: %s\n",
2425                    bundle->getConfigurations());
2426        }
2427        if (!prefFilter.isEmpty()) {
2428            printf("Applying preferred filter: %s\n",
2429                    bundle->getPreferredConfigurations());
2430        }
2431    }
2432
2433    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2434    const size_t ND = resdirs.size();
2435    for (size_t i=0; i<ND; i++) {
2436        const sp<AaptDir>& dir = resdirs.itemAt(i);
2437        if (dir->getLeaf() == kValuesDir) {
2438            // The "value" dir is special since a single file defines
2439            // multiple resources, so we can not do filtering on the
2440            // files themselves.
2441            continue;
2442        }
2443        if (dir->getLeaf() == kMipmapDir) {
2444            // We also skip the "mipmap" directory, since the point of this
2445            // is to include all densities without stripping.  If you put
2446            // other configurations in here as well they won't be stripped
2447            // either...  So don't do that.  Seriously.  What is wrong with you?
2448            continue;
2449        }
2450
2451        const size_t NG = dir->getFiles().size();
2452        for (size_t j=0; j<NG; j++) {
2453            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2454
2455            // First remove any configurations we know we don't need.
2456            for (size_t k=0; k<grp->getFiles().size(); k++) {
2457                sp<AaptFile> file = grp->getFiles().valueAt(k);
2458                if (k == 0 && grp->getFiles().size() == 1) {
2459                    // If this is the only file left, we need to keep it.
2460                    // Otherwise the resource IDs we are using will be inconsistent
2461                    // with what we get when not stripping.  Sucky, but at least
2462                    // for now we can rely on the back-end doing another filtering
2463                    // pass to take this out and leave us with this resource name
2464                    // containing no entries.
2465                    continue;
2466                }
2467                if (file->getPath().getPathExtension() == ".xml") {
2468                    // We can't remove .xml files at this point, because when
2469                    // we parse them they may add identifier resources, so
2470                    // removing them can cause our resource identifiers to
2471                    // become inconsistent.
2472                    continue;
2473                }
2474                const ResTable_config& config(file->getGroupEntry().toParams());
2475                if (!reqFilter.match(config)) {
2476                    if (bundle->getVerbose()) {
2477                        printf("Pruning unneeded resource: %s\n",
2478                                file->getPrintableSource().string());
2479                    }
2480                    grp->removeFile(k);
2481                    k--;
2482                }
2483            }
2484
2485            // Quick check: no preferred filters, nothing more to do.
2486            if (prefFilter.isEmpty()) {
2487                continue;
2488            }
2489
2490            // Now deal with preferred configurations.
2491            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2492                for (size_t k=0; k<grp->getFiles().size(); k++) {
2493                    sp<AaptFile> file = grp->getFiles().valueAt(k);
2494                    if (k == 0 && grp->getFiles().size() == 1) {
2495                        // If this is the only file left, we need to keep it.
2496                        // Otherwise the resource IDs we are using will be inconsistent
2497                        // with what we get when not stripping.  Sucky, but at least
2498                        // for now we can rely on the back-end doing another filtering
2499                        // pass to take this out and leave us with this resource name
2500                        // containing no entries.
2501                        continue;
2502                    }
2503                    if (file->getPath().getPathExtension() == ".xml") {
2504                        // We can't remove .xml files at this point, because when
2505                        // we parse them they may add identifier resources, so
2506                        // removing them can cause our resource identifiers to
2507                        // become inconsistent.
2508                        continue;
2509                    }
2510                    const ResTable_config& config(file->getGroupEntry().toParams());
2511                    if (!prefFilter.match(axis, config)) {
2512                        // This is a resource we would prefer not to have.  Check
2513                        // to see if have a similar variation that we would like
2514                        // to have and, if so, we can drop it.
2515                        for (size_t m=0; m<grp->getFiles().size(); m++) {
2516                            if (m == k) continue;
2517                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2518                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2519                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2520                                if (prefFilter.match(axis, mconfig)) {
2521                                    if (bundle->getVerbose()) {
2522                                        printf("Pruning unneeded resource: %s\n",
2523                                                file->getPrintableSource().string());
2524                                    }
2525                                    grp->removeFile(k);
2526                                    k--;
2527                                    break;
2528                                }
2529                            }
2530                        }
2531                    }
2532                }
2533            }
2534        }
2535    }
2536
2537    return NO_ERROR;
2538}
2539
2540sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2541{
2542    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2543    if (sym == NULL) {
2544        sym = new AaptSymbols();
2545        mSymbols.add(name, sym);
2546    }
2547    return sym;
2548}
2549
2550sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2551{
2552    sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2553    if (sym == NULL) {
2554        sym = new AaptSymbols();
2555        mJavaSymbols.add(name, sym);
2556    }
2557    return sym;
2558}
2559
2560status_t AaptAssets::applyJavaSymbols()
2561{
2562    size_t N = mJavaSymbols.size();
2563    for (size_t i=0; i<N; i++) {
2564        const String8& name = mJavaSymbols.keyAt(i);
2565        const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2566        ssize_t pos = mSymbols.indexOfKey(name);
2567        if (pos < 0) {
2568            SourcePos pos;
2569            pos.error("Java symbol dir %s not defined\n", name.string());
2570            return UNKNOWN_ERROR;
2571        }
2572        //printf("**** applying java symbols in dir %s\n", name.string());
2573        status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2574        if (err != NO_ERROR) {
2575            return err;
2576        }
2577    }
2578
2579    return NO_ERROR;
2580}
2581
2582bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2583    //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2584    //        sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2585    //        sym.isJavaSymbol ? 1 : 0);
2586    if (!mHavePrivateSymbols) return true;
2587    if (sym.isPublic) return true;
2588    if (includePrivate && sym.isJavaSymbol) return true;
2589    return false;
2590}
2591
2592status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2593{
2594    if (!mHaveIncludedAssets) {
2595        // Add in all includes.
2596        const Vector<const char*>& incl = bundle->getPackageIncludes();
2597        const size_t N=incl.size();
2598        for (size_t i=0; i<N; i++) {
2599            if (bundle->getVerbose())
2600                printf("Including resources from package: %s\n", incl[i]);
2601            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2602                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2603                        incl[i]);
2604                return UNKNOWN_ERROR;
2605            }
2606        }
2607        mHaveIncludedAssets = true;
2608    }
2609
2610    return NO_ERROR;
2611}
2612
2613status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2614{
2615    const ResTable& res = getIncludedResources();
2616    // XXX dirty!
2617    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2618}
2619
2620const ResTable& AaptAssets::getIncludedResources() const
2621{
2622    return mIncludedAssets.getResources(false);
2623}
2624
2625void AaptAssets::print(const String8& prefix) const
2626{
2627    String8 innerPrefix(prefix);
2628    innerPrefix.append("  ");
2629    String8 innerInnerPrefix(innerPrefix);
2630    innerInnerPrefix.append("  ");
2631    printf("%sConfigurations:\n", prefix.string());
2632    const size_t N=mGroupEntries.size();
2633    for (size_t i=0; i<N; i++) {
2634        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2635        printf("%s %s\n", prefix.string(),
2636                cname != "" ? cname.string() : "(default)");
2637    }
2638
2639    printf("\n%sFiles:\n", prefix.string());
2640    AaptDir::print(innerPrefix);
2641
2642    printf("\n%sResource Dirs:\n", prefix.string());
2643    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2644    const size_t NR = resdirs.size();
2645    for (size_t i=0; i<NR; i++) {
2646        const sp<AaptDir>& d = resdirs.itemAt(i);
2647        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
2648        d->print(innerInnerPrefix);
2649    }
2650}
2651
2652sp<AaptDir> AaptAssets::resDir(const String8& name) const
2653{
2654    const Vector<sp<AaptDir> >& resdirs = mResDirs;
2655    const size_t N = resdirs.size();
2656    for (size_t i=0; i<N; i++) {
2657        const sp<AaptDir>& d = resdirs.itemAt(i);
2658        if (d->getLeaf() == name) {
2659            return d;
2660        }
2661    }
2662    return NULL;
2663}
2664
2665bool
2666valid_symbol_name(const String8& symbol)
2667{
2668    static char const * const KEYWORDS[] = {
2669        "abstract", "assert", "boolean", "break",
2670        "byte", "case", "catch", "char", "class", "const", "continue",
2671        "default", "do", "double", "else", "enum", "extends", "final",
2672        "finally", "float", "for", "goto", "if", "implements", "import",
2673        "instanceof", "int", "interface", "long", "native", "new", "package",
2674        "private", "protected", "public", "return", "short", "static",
2675        "strictfp", "super", "switch", "synchronized", "this", "throw",
2676        "throws", "transient", "try", "void", "volatile", "while",
2677        "true", "false", "null",
2678        NULL
2679    };
2680    const char*const* k = KEYWORDS;
2681    const char*const s = symbol.string();
2682    while (*k) {
2683        if (0 == strcmp(s, *k)) {
2684            return false;
2685        }
2686        k++;
2687    }
2688    return true;
2689}
2690