AaptAssets.cpp revision 9139868b6c5cb29b3665984225dceb0193fec31d
1//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
6#include "Main.h"
7
8#include <utils/misc.h>
9#include <utils/SortedVector.h>
10
11#include <ctype.h>
12#include <dirent.h>
13#include <errno.h>
14
15static const char* kDefaultLocale = "default";
16static const char* kWildcardName = "any";
17static const char* kAssetDir = "assets";
18static const char* kResourceDir = "res";
19static const char* kInvalidChars = "/\\:";
20static const size_t kMaxAssetFileName = 100;
21
22static const String8 kResString(kResourceDir);
23
24/*
25 * Names of asset files must meet the following criteria:
26 *
27 *  - the filename length must be less than kMaxAssetFileName bytes long
28 *    (and can't be empty)
29 *  - all characters must be 7-bit printable ASCII
30 *  - none of { '/' '\\' ':' }
31 *
32 * Pass in just the filename, not the full path.
33 */
34static bool validateFileName(const char* fileName)
35{
36    const char* cp = fileName;
37    size_t len = 0;
38
39    while (*cp != '\0') {
40        if ((*cp & 0x80) != 0)
41            return false;           // reject high ASCII
42        if (*cp < 0x20 || *cp >= 0x7f)
43            return false;           // reject control chars and 0x7f
44        if (strchr(kInvalidChars, *cp) != NULL)
45            return false;           // reject path sep chars
46        cp++;
47        len++;
48    }
49
50    if (len < 1 || len > kMaxAssetFileName)
51        return false;               // reject empty or too long
52
53    return true;
54}
55
56static bool isHidden(const char *root, const char *path)
57{
58    const char *ext  = NULL;
59    const char *type = NULL;
60
61    // Skip all hidden files.
62    if (path[0] == '.') {
63        // Skip ., .. and  .svn but don't chatter about it.
64        if (strcmp(path, ".") == 0
65            || strcmp(path, "..") == 0
66            || strcmp(path, ".svn") == 0) {
67            return true;
68        }
69        type = "hidden";
70    } else if (path[0] == '_') {
71        // skip directories starting with _ (don't chatter about it)
72        String8 subdirName(root);
73        subdirName.appendPath(path);
74        if (getFileType(subdirName.string()) == kFileTypeDirectory) {
75            return true;
76        }
77    } else if (strcmp(path, "CVS") == 0) {
78        // Skip CVS but don't chatter about it.
79        return true;
80    } else if (strcasecmp(path, "thumbs.db") == 0
81               || strcasecmp(path, "picasa.ini") == 0) {
82        // Skip suspected image indexes files.
83        type = "index";
84    } else if (path[strlen(path)-1] == '~') {
85        // Skip suspected emacs backup files.
86        type = "backup";
87    } else if ((ext = strrchr(path, '.')) != NULL && strcmp(ext, ".scc") == 0) {
88        // Skip VisualSourceSafe files and don't chatter about it
89        return true;
90    } else {
91        // Let everything else through.
92        return false;
93    }
94
95    /* If we get this far, "type" should be set and the file
96     * should be skipped.
97     */
98    String8 subdirName(root);
99    subdirName.appendPath(path);
100    fprintf(stderr, "    (skipping %s %s '%s')\n", type,
101            getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
102            subdirName.string());
103
104    return true;
105}
106
107// =========================================================================
108// =========================================================================
109// =========================================================================
110
111status_t
112AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
113{
114    ResTable_config config;
115
116    // IMSI - MCC
117    if (getMccName(part.string(), &config)) {
118        *axis = AXIS_MCC;
119        *value = config.mcc;
120        return 0;
121    }
122
123    // IMSI - MNC
124    if (getMncName(part.string(), &config)) {
125        *axis = AXIS_MNC;
126        *value = config.mnc;
127        return 0;
128    }
129
130    // locale - language
131    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
132        *axis = AXIS_LANGUAGE;
133        *value = part[1] << 8 | part[0];
134        return 0;
135    }
136
137    // locale - language_REGION
138    if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
139            && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
140        *axis = AXIS_LANGUAGE;
141        *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
142        return 0;
143    }
144
145    // smallest screen dp width
146    if (getSmallestScreenWidthDpName(part.string(), &config)) {
147        *axis = AXIS_SMALLESTSCREENWIDTHDP;
148        *value = config.smallestScreenWidthDp;
149        return 0;
150    }
151
152    // screen dp width
153    if (getScreenWidthDpName(part.string(), &config)) {
154        *axis = AXIS_SCREENWIDTHDP;
155        *value = config.screenWidthDp;
156        return 0;
157    }
158
159    // screen dp height
160    if (getScreenHeightDpName(part.string(), &config)) {
161        *axis = AXIS_SCREENHEIGHTDP;
162        *value = config.screenHeightDp;
163        return 0;
164    }
165
166    // screen layout size
167    if (getScreenLayoutSizeName(part.string(), &config)) {
168        *axis = AXIS_SCREENLAYOUTSIZE;
169        *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
170        return 0;
171    }
172
173    // screen layout long
174    if (getScreenLayoutLongName(part.string(), &config)) {
175        *axis = AXIS_SCREENLAYOUTLONG;
176        *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
177        return 0;
178    }
179
180    // orientation
181    if (getOrientationName(part.string(), &config)) {
182        *axis = AXIS_ORIENTATION;
183        *value = config.orientation;
184        return 0;
185    }
186
187    // ui mode type
188    if (getUiModeTypeName(part.string(), &config)) {
189        *axis = AXIS_UIMODETYPE;
190        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
191        return 0;
192    }
193
194    // ui mode night
195    if (getUiModeNightName(part.string(), &config)) {
196        *axis = AXIS_UIMODENIGHT;
197        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
198        return 0;
199    }
200
201    // density
202    if (getDensityName(part.string(), &config)) {
203        *axis = AXIS_DENSITY;
204        *value = config.density;
205        return 0;
206    }
207
208    // touchscreen
209    if (getTouchscreenName(part.string(), &config)) {
210        *axis = AXIS_TOUCHSCREEN;
211        *value = config.touchscreen;
212        return 0;
213    }
214
215    // keyboard hidden
216    if (getKeysHiddenName(part.string(), &config)) {
217        *axis = AXIS_KEYSHIDDEN;
218        *value = config.inputFlags;
219        return 0;
220    }
221
222    // keyboard
223    if (getKeyboardName(part.string(), &config)) {
224        *axis = AXIS_KEYBOARD;
225        *value = config.keyboard;
226        return 0;
227    }
228
229    // navigation hidden
230    if (getNavHiddenName(part.string(), &config)) {
231        *axis = AXIS_NAVHIDDEN;
232        *value = config.inputFlags;
233        return 0;
234    }
235
236    // navigation
237    if (getNavigationName(part.string(), &config)) {
238        *axis = AXIS_NAVIGATION;
239        *value = config.navigation;
240        return 0;
241    }
242
243    // screen size
244    if (getScreenSizeName(part.string(), &config)) {
245        *axis = AXIS_SCREENSIZE;
246        *value = config.screenSize;
247        return 0;
248    }
249
250    // version
251    if (getVersionName(part.string(), &config)) {
252        *axis = AXIS_VERSION;
253        *value = config.version;
254        return 0;
255    }
256
257    return 1;
258}
259
260bool
261AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
262{
263    Vector<String8> parts;
264
265    String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
266    String8 touch, key, keysHidden, nav, navHidden, size, vers;
267    String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
268
269    const char *p = dir;
270    const char *q;
271    while (NULL != (q = strchr(p, '-'))) {
272        String8 val(p, q-p);
273        val.toLower();
274        parts.add(val);
275        //printf("part: %s\n", parts[parts.size()-1].string());
276        p = q+1;
277    }
278    String8 val(p);
279    val.toLower();
280    parts.add(val);
281    //printf("part: %s\n", parts[parts.size()-1].string());
282
283    const int N = parts.size();
284    int index = 0;
285    String8 part = parts[index];
286
287    // resource type
288    if (!isValidResourceType(part)) {
289        return false;
290    }
291    *resType = part;
292
293    index++;
294    if (index == N) {
295        goto success;
296    }
297    part = parts[index];
298
299    // imsi - mcc
300    if (getMccName(part.string())) {
301        mcc = part;
302
303        index++;
304        if (index == N) {
305            goto success;
306        }
307        part = parts[index];
308    } else {
309        //printf("not mcc: %s\n", part.string());
310    }
311
312    // imsi - mnc
313    if (getMncName(part.string())) {
314        mnc = part;
315
316        index++;
317        if (index == N) {
318            goto success;
319        }
320        part = parts[index];
321    } else {
322        //printf("not mcc: %s\n", part.string());
323    }
324
325    // locale - language
326    if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
327        loc = part;
328
329        index++;
330        if (index == N) {
331            goto success;
332        }
333        part = parts[index];
334    } else {
335        //printf("not language: %s\n", part.string());
336    }
337
338    // locale - region
339    if (loc.length() > 0
340            && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
341        loc += "-";
342        part.toUpper();
343        loc += part.string() + 1;
344
345        index++;
346        if (index == N) {
347            goto success;
348        }
349        part = parts[index];
350    } else {
351        //printf("not region: %s\n", part.string());
352    }
353
354    if (getSmallestScreenWidthDpName(part.string())) {
355        smallestwidthdp = part;
356
357        index++;
358        if (index == N) {
359            goto success;
360        }
361        part = parts[index];
362    } else {
363        //printf("not smallest screen width dp: %s\n", part.string());
364    }
365
366    if (getScreenWidthDpName(part.string())) {
367        widthdp = part;
368
369        index++;
370        if (index == N) {
371            goto success;
372        }
373        part = parts[index];
374    } else {
375        //printf("not screen width dp: %s\n", part.string());
376    }
377
378    if (getScreenHeightDpName(part.string())) {
379        heightdp = part;
380
381        index++;
382        if (index == N) {
383            goto success;
384        }
385        part = parts[index];
386    } else {
387        //printf("not screen height dp: %s\n", part.string());
388    }
389
390    if (getScreenLayoutSizeName(part.string())) {
391        layoutsize = part;
392
393        index++;
394        if (index == N) {
395            goto success;
396        }
397        part = parts[index];
398    } else {
399        //printf("not screen layout size: %s\n", part.string());
400    }
401
402    if (getScreenLayoutLongName(part.string())) {
403        layoutlong = part;
404
405        index++;
406        if (index == N) {
407            goto success;
408        }
409        part = parts[index];
410    } else {
411        //printf("not screen layout long: %s\n", part.string());
412    }
413
414    // orientation
415    if (getOrientationName(part.string())) {
416        orient = part;
417
418        index++;
419        if (index == N) {
420            goto success;
421        }
422        part = parts[index];
423    } else {
424        //printf("not orientation: %s\n", part.string());
425    }
426
427    // ui mode type
428    if (getUiModeTypeName(part.string())) {
429        uiModeType = part;
430
431        index++;
432        if (index == N) {
433            goto success;
434        }
435        part = parts[index];
436    } else {
437        //printf("not ui mode type: %s\n", part.string());
438    }
439
440    // ui mode night
441    if (getUiModeNightName(part.string())) {
442        uiModeNight = part;
443
444        index++;
445        if (index == N) {
446            goto success;
447        }
448        part = parts[index];
449    } else {
450        //printf("not ui mode night: %s\n", part.string());
451    }
452
453    // density
454    if (getDensityName(part.string())) {
455        den = part;
456
457        index++;
458        if (index == N) {
459            goto success;
460        }
461        part = parts[index];
462    } else {
463        //printf("not density: %s\n", part.string());
464    }
465
466    // touchscreen
467    if (getTouchscreenName(part.string())) {
468        touch = part;
469
470        index++;
471        if (index == N) {
472            goto success;
473        }
474        part = parts[index];
475    } else {
476        //printf("not touchscreen: %s\n", part.string());
477    }
478
479    // keyboard hidden
480    if (getKeysHiddenName(part.string())) {
481        keysHidden = part;
482
483        index++;
484        if (index == N) {
485            goto success;
486        }
487        part = parts[index];
488    } else {
489        //printf("not keysHidden: %s\n", part.string());
490    }
491
492    // keyboard
493    if (getKeyboardName(part.string())) {
494        key = part;
495
496        index++;
497        if (index == N) {
498            goto success;
499        }
500        part = parts[index];
501    } else {
502        //printf("not keyboard: %s\n", part.string());
503    }
504
505    // navigation hidden
506    if (getNavHiddenName(part.string())) {
507        navHidden = part;
508
509        index++;
510        if (index == N) {
511            goto success;
512        }
513        part = parts[index];
514    } else {
515        //printf("not navHidden: %s\n", part.string());
516    }
517
518    if (getNavigationName(part.string())) {
519        nav = part;
520
521        index++;
522        if (index == N) {
523            goto success;
524        }
525        part = parts[index];
526    } else {
527        //printf("not navigation: %s\n", part.string());
528    }
529
530    if (getScreenSizeName(part.string())) {
531        size = part;
532
533        index++;
534        if (index == N) {
535            goto success;
536        }
537        part = parts[index];
538    } else {
539        //printf("not screen size: %s\n", part.string());
540    }
541
542    if (getVersionName(part.string())) {
543        vers = part;
544
545        index++;
546        if (index == N) {
547            goto success;
548        }
549        part = parts[index];
550    } else {
551        //printf("not version: %s\n", part.string());
552    }
553
554    // if there are extra parts, it doesn't match
555    return false;
556
557success:
558    this->mcc = mcc;
559    this->mnc = mnc;
560    this->locale = loc;
561    this->screenLayoutSize = layoutsize;
562    this->screenLayoutLong = layoutlong;
563    this->smallestScreenWidthDp = smallestwidthdp;
564    this->screenWidthDp = widthdp;
565    this->screenHeightDp = heightdp;
566    this->orientation = orient;
567    this->uiModeType = uiModeType;
568    this->uiModeNight = uiModeNight;
569    this->density = den;
570    this->touchscreen = touch;
571    this->keysHidden = keysHidden;
572    this->keyboard = key;
573    this->navHidden = navHidden;
574    this->navigation = nav;
575    this->screenSize = size;
576    this->version = vers;
577
578    // what is this anyway?
579    this->vendor = "";
580
581    return true;
582}
583
584String8
585AaptGroupEntry::toString() const
586{
587    String8 s = this->mcc;
588    s += ",";
589    s += this->mnc;
590    s += ",";
591    s += this->locale;
592    s += ",";
593    s += smallestScreenWidthDp;
594    s += ",";
595    s += screenWidthDp;
596    s += ",";
597    s += screenHeightDp;
598    s += ",";
599    s += screenLayoutSize;
600    s += ",";
601    s += screenLayoutLong;
602    s += ",";
603    s += this->orientation;
604    s += ",";
605    s += uiModeType;
606    s += ",";
607    s += uiModeNight;
608    s += ",";
609    s += density;
610    s += ",";
611    s += touchscreen;
612    s += ",";
613    s += keysHidden;
614    s += ",";
615    s += keyboard;
616    s += ",";
617    s += navHidden;
618    s += ",";
619    s += navigation;
620    s += ",";
621    s += screenSize;
622    s += ",";
623    s += version;
624    return s;
625}
626
627String8
628AaptGroupEntry::toDirName(const String8& resType) const
629{
630    String8 s = resType;
631    if (this->mcc != "") {
632        s += "-";
633        s += mcc;
634    }
635    if (this->mnc != "") {
636        s += "-";
637        s += mnc;
638    }
639    if (this->locale != "") {
640        s += "-";
641        s += locale;
642    }
643    if (this->smallestScreenWidthDp != "") {
644        s += "-";
645        s += smallestScreenWidthDp;
646    }
647    if (this->screenWidthDp != "") {
648        s += "-";
649        s += screenWidthDp;
650    }
651    if (this->screenHeightDp != "") {
652        s += "-";
653        s += screenHeightDp;
654    }
655    if (this->screenLayoutSize != "") {
656        s += "-";
657        s += screenLayoutSize;
658    }
659    if (this->screenLayoutLong != "") {
660        s += "-";
661        s += screenLayoutLong;
662    }
663    if (this->orientation != "") {
664        s += "-";
665        s += orientation;
666    }
667    if (this->uiModeType != "") {
668        s += "-";
669        s += uiModeType;
670    }
671    if (this->uiModeNight != "") {
672        s += "-";
673        s += uiModeNight;
674    }
675    if (this->density != "") {
676        s += "-";
677        s += density;
678    }
679    if (this->touchscreen != "") {
680        s += "-";
681        s += touchscreen;
682    }
683    if (this->keysHidden != "") {
684        s += "-";
685        s += keysHidden;
686    }
687    if (this->keyboard != "") {
688        s += "-";
689        s += keyboard;
690    }
691    if (this->navHidden != "") {
692        s += "-";
693        s += navHidden;
694    }
695    if (this->navigation != "") {
696        s += "-";
697        s += navigation;
698    }
699    if (this->screenSize != "") {
700        s += "-";
701        s += screenSize;
702    }
703    if (this->version != "") {
704        s += "-";
705        s += version;
706    }
707
708    return s;
709}
710
711bool AaptGroupEntry::getMccName(const char* name,
712                                    ResTable_config* out)
713{
714    if (strcmp(name, kWildcardName) == 0) {
715        if (out) out->mcc = 0;
716        return true;
717    }
718    const char* c = name;
719    if (tolower(*c) != 'm') return false;
720    c++;
721    if (tolower(*c) != 'c') return false;
722    c++;
723    if (tolower(*c) != 'c') return false;
724    c++;
725
726    const char* val = c;
727
728    while (*c >= '0' && *c <= '9') {
729        c++;
730    }
731    if (*c != 0) return false;
732    if (c-val != 3) return false;
733
734    int d = atoi(val);
735    if (d != 0) {
736        if (out) out->mcc = d;
737        return true;
738    }
739
740    return false;
741}
742
743bool AaptGroupEntry::getMncName(const char* name,
744                                    ResTable_config* out)
745{
746    if (strcmp(name, kWildcardName) == 0) {
747        if (out) out->mcc = 0;
748        return true;
749    }
750    const char* c = name;
751    if (tolower(*c) != 'm') return false;
752    c++;
753    if (tolower(*c) != 'n') return false;
754    c++;
755    if (tolower(*c) != 'c') return false;
756    c++;
757
758    const char* val = c;
759
760    while (*c >= '0' && *c <= '9') {
761        c++;
762    }
763    if (*c != 0) return false;
764    if (c-val == 0 || c-val > 3) return false;
765
766    if (out) {
767        out->mnc = atoi(val);
768    }
769
770    return true;
771}
772
773/*
774 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
775 * "default")?
776 *
777 * TODO: Should insist that the first two letters are lower case, and the
778 * second two are upper.
779 */
780bool AaptGroupEntry::getLocaleName(const char* fileName,
781                                   ResTable_config* out)
782{
783    if (strcmp(fileName, kWildcardName) == 0
784            || strcmp(fileName, kDefaultLocale) == 0) {
785        if (out) {
786            out->language[0] = 0;
787            out->language[1] = 0;
788            out->country[0] = 0;
789            out->country[1] = 0;
790        }
791        return true;
792    }
793
794    if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
795        if (out) {
796            out->language[0] = fileName[0];
797            out->language[1] = fileName[1];
798            out->country[0] = 0;
799            out->country[1] = 0;
800        }
801        return true;
802    }
803
804    if (strlen(fileName) == 5 &&
805        isalpha(fileName[0]) &&
806        isalpha(fileName[1]) &&
807        fileName[2] == '-' &&
808        isalpha(fileName[3]) &&
809        isalpha(fileName[4])) {
810        if (out) {
811            out->language[0] = fileName[0];
812            out->language[1] = fileName[1];
813            out->country[0] = fileName[3];
814            out->country[1] = fileName[4];
815        }
816        return true;
817    }
818
819    return false;
820}
821
822bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
823                                     ResTable_config* out)
824{
825    if (strcmp(name, kWildcardName) == 0) {
826        if (out) out->screenLayout =
827                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
828                | ResTable_config::SCREENSIZE_ANY;
829        return true;
830    } else if (strcmp(name, "small") == 0) {
831        if (out) out->screenLayout =
832                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
833                | ResTable_config::SCREENSIZE_SMALL;
834        return true;
835    } else if (strcmp(name, "normal") == 0) {
836        if (out) out->screenLayout =
837                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
838                | ResTable_config::SCREENSIZE_NORMAL;
839        return true;
840    } else if (strcmp(name, "large") == 0) {
841        if (out) out->screenLayout =
842                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
843                | ResTable_config::SCREENSIZE_LARGE;
844        return true;
845    } else if (strcmp(name, "xlarge") == 0) {
846        if (out) out->screenLayout =
847                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
848                | ResTable_config::SCREENSIZE_XLARGE;
849        return true;
850    }
851
852    return false;
853}
854
855bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
856                                     ResTable_config* out)
857{
858    if (strcmp(name, kWildcardName) == 0) {
859        if (out) out->screenLayout =
860                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
861                | ResTable_config::SCREENLONG_ANY;
862        return true;
863    } else if (strcmp(name, "long") == 0) {
864        if (out) out->screenLayout =
865                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
866                | ResTable_config::SCREENLONG_YES;
867        return true;
868    } else if (strcmp(name, "notlong") == 0) {
869        if (out) out->screenLayout =
870                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
871                | ResTable_config::SCREENLONG_NO;
872        return true;
873    }
874
875    return false;
876}
877
878bool AaptGroupEntry::getOrientationName(const char* name,
879                                        ResTable_config* out)
880{
881    if (strcmp(name, kWildcardName) == 0) {
882        if (out) out->orientation = out->ORIENTATION_ANY;
883        return true;
884    } else if (strcmp(name, "port") == 0) {
885        if (out) out->orientation = out->ORIENTATION_PORT;
886        return true;
887    } else if (strcmp(name, "land") == 0) {
888        if (out) out->orientation = out->ORIENTATION_LAND;
889        return true;
890    } else if (strcmp(name, "square") == 0) {
891        if (out) out->orientation = out->ORIENTATION_SQUARE;
892        return true;
893    }
894
895    return false;
896}
897
898bool AaptGroupEntry::getUiModeTypeName(const char* name,
899                                       ResTable_config* out)
900{
901    if (strcmp(name, kWildcardName) == 0) {
902        if (out) out->uiMode =
903                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
904                | ResTable_config::UI_MODE_TYPE_ANY;
905        return true;
906    } else if (strcmp(name, "desk") == 0) {
907      if (out) out->uiMode =
908              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
909              | ResTable_config::UI_MODE_TYPE_DESK;
910        return true;
911    } else if (strcmp(name, "car") == 0) {
912      if (out) out->uiMode =
913              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
914              | ResTable_config::UI_MODE_TYPE_CAR;
915        return true;
916    } else if (strcmp(name, "television") == 0) {
917      if (out) out->uiMode =
918              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
919              | ResTable_config::UI_MODE_TYPE_TELEVISION;
920        return true;
921    }
922
923    return false;
924}
925
926bool AaptGroupEntry::getUiModeNightName(const char* name,
927                                          ResTable_config* out)
928{
929    if (strcmp(name, kWildcardName) == 0) {
930        if (out) out->uiMode =
931                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
932                | ResTable_config::UI_MODE_NIGHT_ANY;
933        return true;
934    } else if (strcmp(name, "night") == 0) {
935        if (out) out->uiMode =
936                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
937                | ResTable_config::UI_MODE_NIGHT_YES;
938        return true;
939    } else if (strcmp(name, "notnight") == 0) {
940      if (out) out->uiMode =
941              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
942              | ResTable_config::UI_MODE_NIGHT_NO;
943        return true;
944    }
945
946    return false;
947}
948
949bool AaptGroupEntry::getDensityName(const char* name,
950                                    ResTable_config* out)
951{
952    if (strcmp(name, kWildcardName) == 0) {
953        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
954        return true;
955    }
956
957    if (strcmp(name, "nodpi") == 0) {
958        if (out) out->density = ResTable_config::DENSITY_NONE;
959        return true;
960    }
961
962    if (strcmp(name, "ldpi") == 0) {
963        if (out) out->density = ResTable_config::DENSITY_LOW;
964        return true;
965    }
966
967    if (strcmp(name, "mdpi") == 0) {
968        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
969        return true;
970    }
971
972    if (strcmp(name, "tvdpi") == 0) {
973        if (out) out->density = ResTable_config::DENSITY_TV;
974        return true;
975    }
976
977    if (strcmp(name, "hdpi") == 0) {
978        if (out) out->density = ResTable_config::DENSITY_HIGH;
979        return true;
980    }
981
982    if (strcmp(name, "xhdpi") == 0) {
983        if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
984        return true;
985    }
986
987    char* c = (char*)name;
988    while (*c >= '0' && *c <= '9') {
989        c++;
990    }
991
992    // check that we have 'dpi' after the last digit.
993    if (toupper(c[0]) != 'D' ||
994            toupper(c[1]) != 'P' ||
995            toupper(c[2]) != 'I' ||
996            c[3] != 0) {
997        return false;
998    }
999
1000    // temporarily replace the first letter with \0 to
1001    // use atoi.
1002    char tmp = c[0];
1003    c[0] = '\0';
1004
1005    int d = atoi(name);
1006    c[0] = tmp;
1007
1008    if (d != 0) {
1009        if (out) out->density = d;
1010        return true;
1011    }
1012
1013    return false;
1014}
1015
1016bool AaptGroupEntry::getTouchscreenName(const char* name,
1017                                        ResTable_config* out)
1018{
1019    if (strcmp(name, kWildcardName) == 0) {
1020        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1021        return true;
1022    } else if (strcmp(name, "notouch") == 0) {
1023        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1024        return true;
1025    } else if (strcmp(name, "stylus") == 0) {
1026        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1027        return true;
1028    } else if (strcmp(name, "finger") == 0) {
1029        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1030        return true;
1031    }
1032
1033    return false;
1034}
1035
1036bool AaptGroupEntry::getKeysHiddenName(const char* name,
1037                                       ResTable_config* out)
1038{
1039    uint8_t mask = 0;
1040    uint8_t value = 0;
1041    if (strcmp(name, kWildcardName) == 0) {
1042        mask = ResTable_config::MASK_KEYSHIDDEN;
1043        value = ResTable_config::KEYSHIDDEN_ANY;
1044    } else if (strcmp(name, "keysexposed") == 0) {
1045        mask = ResTable_config::MASK_KEYSHIDDEN;
1046        value = ResTable_config::KEYSHIDDEN_NO;
1047    } else if (strcmp(name, "keyshidden") == 0) {
1048        mask = ResTable_config::MASK_KEYSHIDDEN;
1049        value = ResTable_config::KEYSHIDDEN_YES;
1050    } else if (strcmp(name, "keyssoft") == 0) {
1051        mask = ResTable_config::MASK_KEYSHIDDEN;
1052        value = ResTable_config::KEYSHIDDEN_SOFT;
1053    }
1054
1055    if (mask != 0) {
1056        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1057        return true;
1058    }
1059
1060    return false;
1061}
1062
1063bool AaptGroupEntry::getKeyboardName(const char* name,
1064                                        ResTable_config* out)
1065{
1066    if (strcmp(name, kWildcardName) == 0) {
1067        if (out) out->keyboard = out->KEYBOARD_ANY;
1068        return true;
1069    } else if (strcmp(name, "nokeys") == 0) {
1070        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1071        return true;
1072    } else if (strcmp(name, "qwerty") == 0) {
1073        if (out) out->keyboard = out->KEYBOARD_QWERTY;
1074        return true;
1075    } else if (strcmp(name, "12key") == 0) {
1076        if (out) out->keyboard = out->KEYBOARD_12KEY;
1077        return true;
1078    }
1079
1080    return false;
1081}
1082
1083bool AaptGroupEntry::getNavHiddenName(const char* name,
1084                                       ResTable_config* out)
1085{
1086    uint8_t mask = 0;
1087    uint8_t value = 0;
1088    if (strcmp(name, kWildcardName) == 0) {
1089        mask = ResTable_config::MASK_NAVHIDDEN;
1090        value = ResTable_config::NAVHIDDEN_ANY;
1091    } else if (strcmp(name, "navexposed") == 0) {
1092        mask = ResTable_config::MASK_NAVHIDDEN;
1093        value = ResTable_config::NAVHIDDEN_NO;
1094    } else if (strcmp(name, "navhidden") == 0) {
1095        mask = ResTable_config::MASK_NAVHIDDEN;
1096        value = ResTable_config::NAVHIDDEN_YES;
1097    }
1098
1099    if (mask != 0) {
1100        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1101        return true;
1102    }
1103
1104    return false;
1105}
1106
1107bool AaptGroupEntry::getNavigationName(const char* name,
1108                                     ResTable_config* out)
1109{
1110    if (strcmp(name, kWildcardName) == 0) {
1111        if (out) out->navigation = out->NAVIGATION_ANY;
1112        return true;
1113    } else if (strcmp(name, "nonav") == 0) {
1114        if (out) out->navigation = out->NAVIGATION_NONAV;
1115        return true;
1116    } else if (strcmp(name, "dpad") == 0) {
1117        if (out) out->navigation = out->NAVIGATION_DPAD;
1118        return true;
1119    } else if (strcmp(name, "trackball") == 0) {
1120        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1121        return true;
1122    } else if (strcmp(name, "wheel") == 0) {
1123        if (out) out->navigation = out->NAVIGATION_WHEEL;
1124        return true;
1125    }
1126
1127    return false;
1128}
1129
1130bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1131{
1132    if (strcmp(name, kWildcardName) == 0) {
1133        if (out) {
1134            out->screenWidth = out->SCREENWIDTH_ANY;
1135            out->screenHeight = out->SCREENHEIGHT_ANY;
1136        }
1137        return true;
1138    }
1139
1140    const char* x = name;
1141    while (*x >= '0' && *x <= '9') x++;
1142    if (x == name || *x != 'x') return false;
1143    String8 xName(name, x-name);
1144    x++;
1145
1146    const char* y = x;
1147    while (*y >= '0' && *y <= '9') y++;
1148    if (y == name || *y != 0) return false;
1149    String8 yName(x, y-x);
1150
1151    uint16_t w = (uint16_t)atoi(xName.string());
1152    uint16_t h = (uint16_t)atoi(yName.string());
1153    if (w < h) {
1154        return false;
1155    }
1156
1157    if (out) {
1158        out->screenWidth = w;
1159        out->screenHeight = h;
1160    }
1161
1162    return true;
1163}
1164
1165bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1166{
1167    if (strcmp(name, kWildcardName) == 0) {
1168        if (out) {
1169            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1170        }
1171        return true;
1172    }
1173
1174    if (*name != 's') return false;
1175    name++;
1176    if (*name != 'w') return false;
1177    name++;
1178    const char* x = name;
1179    while (*x >= '0' && *x <= '9') x++;
1180    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1181    String8 xName(name, x-name);
1182
1183    if (out) {
1184        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1185    }
1186
1187    return true;
1188}
1189
1190bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1191{
1192    if (strcmp(name, kWildcardName) == 0) {
1193        if (out) {
1194            out->screenWidthDp = out->SCREENWIDTH_ANY;
1195        }
1196        return true;
1197    }
1198
1199    if (*name != 'w') return false;
1200    name++;
1201    const char* x = name;
1202    while (*x >= '0' && *x <= '9') x++;
1203    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1204    String8 xName(name, x-name);
1205
1206    if (out) {
1207        out->screenWidthDp = (uint16_t)atoi(xName.string());
1208    }
1209
1210    return true;
1211}
1212
1213bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1214{
1215    if (strcmp(name, kWildcardName) == 0) {
1216        if (out) {
1217            out->screenHeightDp = out->SCREENWIDTH_ANY;
1218        }
1219        return true;
1220    }
1221
1222    if (*name != 'h') return false;
1223    name++;
1224    const char* x = name;
1225    while (*x >= '0' && *x <= '9') x++;
1226    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1227    String8 xName(name, x-name);
1228
1229    if (out) {
1230        out->screenHeightDp = (uint16_t)atoi(xName.string());
1231    }
1232
1233    return true;
1234}
1235
1236bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1237{
1238    if (strcmp(name, kWildcardName) == 0) {
1239        if (out) {
1240            out->sdkVersion = out->SDKVERSION_ANY;
1241            out->minorVersion = out->MINORVERSION_ANY;
1242        }
1243        return true;
1244    }
1245
1246    if (*name != 'v') {
1247        return false;
1248    }
1249
1250    name++;
1251    const char* s = name;
1252    while (*s >= '0' && *s <= '9') s++;
1253    if (s == name || *s != 0) return false;
1254    String8 sdkName(name, s-name);
1255
1256    if (out) {
1257        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1258        out->minorVersion = 0;
1259    }
1260
1261    return true;
1262}
1263
1264int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1265{
1266    int v = mcc.compare(o.mcc);
1267    if (v == 0) v = mnc.compare(o.mnc);
1268    if (v == 0) v = locale.compare(o.locale);
1269    if (v == 0) v = vendor.compare(o.vendor);
1270    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1271    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1272    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1273    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1274    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1275    if (v == 0) v = orientation.compare(o.orientation);
1276    if (v == 0) v = uiModeType.compare(o.uiModeType);
1277    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1278    if (v == 0) v = density.compare(o.density);
1279    if (v == 0) v = touchscreen.compare(o.touchscreen);
1280    if (v == 0) v = keysHidden.compare(o.keysHidden);
1281    if (v == 0) v = keyboard.compare(o.keyboard);
1282    if (v == 0) v = navHidden.compare(o.navHidden);
1283    if (v == 0) v = navigation.compare(o.navigation);
1284    if (v == 0) v = screenSize.compare(o.screenSize);
1285    if (v == 0) v = version.compare(o.version);
1286    return v;
1287}
1288
1289ResTable_config AaptGroupEntry::toParams() const
1290{
1291    ResTable_config params;
1292    memset(&params, 0, sizeof(params));
1293    getMccName(mcc.string(), &params);
1294    getMncName(mnc.string(), &params);
1295    getLocaleName(locale.string(), &params);
1296    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1297    getScreenWidthDpName(screenWidthDp.string(), &params);
1298    getScreenHeightDpName(screenHeightDp.string(), &params);
1299    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1300    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1301    getOrientationName(orientation.string(), &params);
1302    getUiModeTypeName(uiModeType.string(), &params);
1303    getUiModeNightName(uiModeNight.string(), &params);
1304    getDensityName(density.string(), &params);
1305    getTouchscreenName(touchscreen.string(), &params);
1306    getKeysHiddenName(keysHidden.string(), &params);
1307    getKeyboardName(keyboard.string(), &params);
1308    getNavHiddenName(navHidden.string(), &params);
1309    getNavigationName(navigation.string(), &params);
1310    getScreenSizeName(screenSize.string(), &params);
1311    getVersionName(version.string(), &params);
1312
1313    // Fix up version number based on specified parameters.
1314    int minSdk = 0;
1315    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1316            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1317            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1318        minSdk = SDK_HONEYCOMB_MR2;
1319    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1320                != ResTable_config::UI_MODE_TYPE_ANY
1321            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1322                != ResTable_config::UI_MODE_NIGHT_ANY) {
1323        minSdk = SDK_FROYO;
1324    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1325                != ResTable_config::SCREENSIZE_ANY
1326            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1327                != ResTable_config::SCREENLONG_ANY
1328            || params.density != ResTable_config::DENSITY_DEFAULT) {
1329        minSdk = SDK_DONUT;
1330    }
1331
1332    if (minSdk > params.sdkVersion) {
1333        params.sdkVersion = minSdk;
1334    }
1335
1336    return params;
1337}
1338
1339// =========================================================================
1340// =========================================================================
1341// =========================================================================
1342
1343void* AaptFile::editData(size_t size)
1344{
1345    if (size <= mBufferSize) {
1346        mDataSize = size;
1347        return mData;
1348    }
1349    size_t allocSize = (size*3)/2;
1350    void* buf = realloc(mData, allocSize);
1351    if (buf == NULL) {
1352        return NULL;
1353    }
1354    mData = buf;
1355    mDataSize = size;
1356    mBufferSize = allocSize;
1357    return buf;
1358}
1359
1360void* AaptFile::editData(size_t* outSize)
1361{
1362    if (outSize) {
1363        *outSize = mDataSize;
1364    }
1365    return mData;
1366}
1367
1368void* AaptFile::padData(size_t wordSize)
1369{
1370    const size_t extra = mDataSize%wordSize;
1371    if (extra == 0) {
1372        return mData;
1373    }
1374
1375    size_t initial = mDataSize;
1376    void* data = editData(initial+(wordSize-extra));
1377    if (data != NULL) {
1378        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1379    }
1380    return data;
1381}
1382
1383status_t AaptFile::writeData(const void* data, size_t size)
1384{
1385    size_t end = mDataSize;
1386    size_t total = size + end;
1387    void* buf = editData(total);
1388    if (buf == NULL) {
1389        return UNKNOWN_ERROR;
1390    }
1391    memcpy(((char*)buf)+end, data, size);
1392    return NO_ERROR;
1393}
1394
1395void AaptFile::clearData()
1396{
1397    if (mData != NULL) free(mData);
1398    mData = NULL;
1399    mDataSize = 0;
1400    mBufferSize = 0;
1401}
1402
1403String8 AaptFile::getPrintableSource() const
1404{
1405    if (hasData()) {
1406        String8 name(mGroupEntry.locale.string());
1407        name.appendPath(mGroupEntry.vendor.string());
1408        name.appendPath(mPath);
1409        name.append(" #generated");
1410        return name;
1411    }
1412    return mSourceFile;
1413}
1414
1415// =========================================================================
1416// =========================================================================
1417// =========================================================================
1418
1419status_t AaptGroup::addFile(const sp<AaptFile>& file)
1420{
1421    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1422        file->mPath = mPath;
1423        mFiles.add(file->getGroupEntry(), file);
1424        return NO_ERROR;
1425    }
1426
1427    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1428                                               getPrintableSource().string());
1429    return UNKNOWN_ERROR;
1430}
1431
1432void AaptGroup::removeFile(size_t index)
1433{
1434	mFiles.removeItemsAt(index);
1435}
1436
1437void AaptGroup::print() const
1438{
1439    printf("  %s\n", getPath().string());
1440    const size_t N=mFiles.size();
1441    size_t i;
1442    for (i=0; i<N; i++) {
1443        sp<AaptFile> file = mFiles.valueAt(i);
1444        const AaptGroupEntry& e = file->getGroupEntry();
1445        if (file->hasData()) {
1446            printf("      Gen: (%s) %d bytes\n", e.toString().string(),
1447                    (int)file->getSize());
1448        } else {
1449            printf("      Src: %s\n", file->getPrintableSource().string());
1450        }
1451    }
1452}
1453
1454String8 AaptGroup::getPrintableSource() const
1455{
1456    if (mFiles.size() > 0) {
1457        // Arbitrarily pull the first source file out of the list.
1458        return mFiles.valueAt(0)->getPrintableSource();
1459    }
1460
1461    // Should never hit this case, but to be safe...
1462    return getPath();
1463
1464}
1465
1466// =========================================================================
1467// =========================================================================
1468// =========================================================================
1469
1470status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1471{
1472    if (mFiles.indexOfKey(name) >= 0) {
1473        return ALREADY_EXISTS;
1474    }
1475    mFiles.add(name, file);
1476    return NO_ERROR;
1477}
1478
1479status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1480{
1481    if (mDirs.indexOfKey(name) >= 0) {
1482        return ALREADY_EXISTS;
1483    }
1484    mDirs.add(name, dir);
1485    return NO_ERROR;
1486}
1487
1488sp<AaptDir> AaptDir::makeDir(const String8& path)
1489{
1490    String8 name;
1491    String8 remain = path;
1492
1493    sp<AaptDir> subdir = this;
1494    while (name = remain.walkPath(&remain), remain != "") {
1495        subdir = subdir->makeDir(name);
1496    }
1497
1498    ssize_t i = subdir->mDirs.indexOfKey(name);
1499    if (i >= 0) {
1500        return subdir->mDirs.valueAt(i);
1501    }
1502    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1503    subdir->mDirs.add(name, dir);
1504    return dir;
1505}
1506
1507void AaptDir::removeFile(const String8& name)
1508{
1509    mFiles.removeItem(name);
1510}
1511
1512void AaptDir::removeDir(const String8& name)
1513{
1514    mDirs.removeItem(name);
1515}
1516
1517status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1518{
1519	sp<AaptGroup> origGroup;
1520
1521	// Find and remove the given file with shear, brute force!
1522	const size_t NG = mFiles.size();
1523	size_t i;
1524	for (i=0; origGroup == NULL && i<NG; i++) {
1525		sp<AaptGroup> g = mFiles.valueAt(i);
1526		const size_t NF = g->getFiles().size();
1527		for (size_t j=0; j<NF; j++) {
1528			if (g->getFiles().valueAt(j) == file) {
1529				origGroup = g;
1530				g->removeFile(j);
1531				if (NF == 1) {
1532					mFiles.removeItemsAt(i);
1533				}
1534				break;
1535			}
1536		}
1537	}
1538
1539	//printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1540
1541	// Place the file under its new name.
1542	if (origGroup != NULL) {
1543		return addLeafFile(newName, file);
1544	}
1545
1546	return NO_ERROR;
1547}
1548
1549status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1550{
1551    sp<AaptGroup> group;
1552    if (mFiles.indexOfKey(leafName) >= 0) {
1553        group = mFiles.valueFor(leafName);
1554    } else {
1555        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1556        mFiles.add(leafName, group);
1557    }
1558
1559    return group->addFile(file);
1560}
1561
1562ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1563                            const AaptGroupEntry& kind, const String8& resType,
1564                            sp<FilePathStore>& fullResPaths)
1565{
1566    Vector<String8> fileNames;
1567    {
1568        DIR* dir = NULL;
1569
1570        dir = opendir(srcDir.string());
1571        if (dir == NULL) {
1572            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1573            return UNKNOWN_ERROR;
1574        }
1575
1576        /*
1577         * Slurp the filenames out of the directory.
1578         */
1579        while (1) {
1580            struct dirent* entry;
1581
1582            entry = readdir(dir);
1583            if (entry == NULL)
1584                break;
1585
1586            if (isHidden(srcDir.string(), entry->d_name))
1587                continue;
1588
1589            String8 name(entry->d_name);
1590            fileNames.add(name);
1591            // Add fully qualified path for dependency purposes
1592            // if we're collecting them
1593            if (fullResPaths != NULL) {
1594                fullResPaths->add(srcDir.appendPathCopy(name));
1595            }
1596        }
1597        closedir(dir);
1598    }
1599
1600    ssize_t count = 0;
1601
1602    /*
1603     * Stash away the files and recursively descend into subdirectories.
1604     */
1605    const size_t N = fileNames.size();
1606    size_t i;
1607    for (i = 0; i < N; i++) {
1608        String8 pathName(srcDir);
1609        FileType type;
1610
1611        pathName.appendPath(fileNames[i].string());
1612        type = getFileType(pathName.string());
1613        if (type == kFileTypeDirectory) {
1614            sp<AaptDir> subdir;
1615            bool notAdded = false;
1616            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1617                subdir = mDirs.valueFor(fileNames[i]);
1618            } else {
1619                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1620                notAdded = true;
1621            }
1622            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1623                                                resType, fullResPaths);
1624            if (res < NO_ERROR) {
1625                return res;
1626            }
1627            if (res > 0 && notAdded) {
1628                mDirs.add(fileNames[i], subdir);
1629            }
1630            count += res;
1631        } else if (type == kFileTypeRegular) {
1632            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1633            status_t err = addLeafFile(fileNames[i], file);
1634            if (err != NO_ERROR) {
1635                return err;
1636            }
1637
1638            count++;
1639
1640        } else {
1641            if (bundle->getVerbose())
1642                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1643        }
1644    }
1645
1646    return count;
1647}
1648
1649status_t AaptDir::validate() const
1650{
1651    const size_t NF = mFiles.size();
1652    const size_t ND = mDirs.size();
1653    size_t i;
1654    for (i = 0; i < NF; i++) {
1655        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1656            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1657                    "Invalid filename.  Unable to add.");
1658            return UNKNOWN_ERROR;
1659        }
1660
1661        size_t j;
1662        for (j = i+1; j < NF; j++) {
1663            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1664                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1665                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1666                        "File is case-insensitive equivalent to: %s",
1667                        mFiles.valueAt(j)->getPrintableSource().string());
1668                return UNKNOWN_ERROR;
1669            }
1670
1671            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1672            // (this is mostly caught by the "marked" stuff, below)
1673        }
1674
1675        for (j = 0; j < ND; j++) {
1676            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1677                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1678                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1679                        "File conflicts with dir from: %s",
1680                        mDirs.valueAt(j)->getPrintableSource().string());
1681                return UNKNOWN_ERROR;
1682            }
1683        }
1684    }
1685
1686    for (i = 0; i < ND; i++) {
1687        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1688            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1689                    "Invalid directory name, unable to add.");
1690            return UNKNOWN_ERROR;
1691        }
1692
1693        size_t j;
1694        for (j = i+1; j < ND; j++) {
1695            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1696                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1697                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1698                        "Directory is case-insensitive equivalent to: %s",
1699                        mDirs.valueAt(j)->getPrintableSource().string());
1700                return UNKNOWN_ERROR;
1701            }
1702        }
1703
1704        status_t err = mDirs.valueAt(i)->validate();
1705        if (err != NO_ERROR) {
1706            return err;
1707        }
1708    }
1709
1710    return NO_ERROR;
1711}
1712
1713void AaptDir::print() const
1714{
1715    const size_t ND=getDirs().size();
1716    size_t i;
1717    for (i=0; i<ND; i++) {
1718        getDirs().valueAt(i)->print();
1719    }
1720
1721    const size_t NF=getFiles().size();
1722    for (i=0; i<NF; i++) {
1723        getFiles().valueAt(i)->print();
1724    }
1725}
1726
1727String8 AaptDir::getPrintableSource() const
1728{
1729    if (mFiles.size() > 0) {
1730        // Arbitrarily pull the first file out of the list as the source dir.
1731        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1732    }
1733    if (mDirs.size() > 0) {
1734        // Or arbitrarily pull the first dir out of the list as the source dir.
1735        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1736    }
1737
1738    // Should never hit this case, but to be safe...
1739    return mPath;
1740
1741}
1742
1743// =========================================================================
1744// =========================================================================
1745// =========================================================================
1746
1747sp<AaptFile> AaptAssets::addFile(
1748        const String8& filePath, const AaptGroupEntry& entry,
1749        const String8& srcDir, sp<AaptGroup>* outGroup,
1750        const String8& resType)
1751{
1752    sp<AaptDir> dir = this;
1753    sp<AaptGroup> group;
1754    sp<AaptFile> file;
1755    String8 root, remain(filePath), partialPath;
1756    while (remain.length() > 0) {
1757        root = remain.walkPath(&remain);
1758        partialPath.appendPath(root);
1759
1760        const String8 rootStr(root);
1761
1762        if (remain.length() == 0) {
1763            ssize_t i = dir->getFiles().indexOfKey(rootStr);
1764            if (i >= 0) {
1765                group = dir->getFiles().valueAt(i);
1766            } else {
1767                group = new AaptGroup(rootStr, filePath);
1768                status_t res = dir->addFile(rootStr, group);
1769                if (res != NO_ERROR) {
1770                    return NULL;
1771                }
1772            }
1773            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1774            status_t res = group->addFile(file);
1775            if (res != NO_ERROR) {
1776                return NULL;
1777            }
1778            break;
1779
1780        } else {
1781            ssize_t i = dir->getDirs().indexOfKey(rootStr);
1782            if (i >= 0) {
1783                dir = dir->getDirs().valueAt(i);
1784            } else {
1785                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1786                status_t res = dir->addDir(rootStr, subdir);
1787                if (res != NO_ERROR) {
1788                    return NULL;
1789                }
1790                dir = subdir;
1791            }
1792        }
1793    }
1794
1795    mGroupEntries.add(entry);
1796    if (outGroup) *outGroup = group;
1797    return file;
1798}
1799
1800void AaptAssets::addResource(const String8& leafName, const String8& path,
1801                const sp<AaptFile>& file, const String8& resType)
1802{
1803    sp<AaptDir> res = AaptDir::makeDir(kResString);
1804    String8 dirname = file->getGroupEntry().toDirName(resType);
1805    sp<AaptDir> subdir = res->makeDir(dirname);
1806    sp<AaptGroup> grr = new AaptGroup(leafName, path);
1807    grr->addFile(file);
1808
1809    subdir->addFile(leafName, grr);
1810}
1811
1812
1813ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1814{
1815    int count;
1816    int totalCount = 0;
1817    FileType type;
1818    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1819    const size_t dirCount =resDirs.size();
1820    sp<AaptAssets> current = this;
1821
1822    const int N = bundle->getFileSpecCount();
1823
1824    /*
1825     * If a package manifest was specified, include that first.
1826     */
1827    if (bundle->getAndroidManifestFile() != NULL) {
1828        // place at root of zip.
1829        String8 srcFile(bundle->getAndroidManifestFile());
1830        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1831                NULL, String8());
1832        totalCount++;
1833    }
1834
1835    /*
1836     * If a directory of custom assets was supplied, slurp 'em up.
1837     */
1838    if (bundle->getAssetSourceDir()) {
1839        const char* assetDir = bundle->getAssetSourceDir();
1840
1841        FileType type = getFileType(assetDir);
1842        if (type == kFileTypeNonexistent) {
1843            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1844            return UNKNOWN_ERROR;
1845        }
1846        if (type != kFileTypeDirectory) {
1847            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1848            return UNKNOWN_ERROR;
1849        }
1850
1851        String8 assetRoot(assetDir);
1852        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1853        AaptGroupEntry group;
1854        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1855                                            String8(), mFullResPaths);
1856        if (count < 0) {
1857            totalCount = count;
1858            goto bail;
1859        }
1860        if (count > 0) {
1861            mGroupEntries.add(group);
1862        }
1863        totalCount += count;
1864
1865        if (bundle->getVerbose())
1866            printf("Found %d custom asset file%s in %s\n",
1867                   count, (count==1) ? "" : "s", assetDir);
1868    }
1869
1870    /*
1871     * If a directory of resource-specific assets was supplied, slurp 'em up.
1872     */
1873    for (size_t i=0; i<dirCount; i++) {
1874        const char *res = resDirs[i];
1875        if (res) {
1876            type = getFileType(res);
1877            if (type == kFileTypeNonexistent) {
1878                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1879                return UNKNOWN_ERROR;
1880            }
1881            if (type == kFileTypeDirectory) {
1882                if (i>0) {
1883                    sp<AaptAssets> nextOverlay = new AaptAssets();
1884                    current->setOverlay(nextOverlay);
1885                    current = nextOverlay;
1886                    current->setFullResPaths(mFullResPaths);
1887                }
1888                count = current->slurpResourceTree(bundle, String8(res));
1889
1890                if (count < 0) {
1891                    totalCount = count;
1892                    goto bail;
1893                }
1894                totalCount += count;
1895            }
1896            else {
1897                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1898                return UNKNOWN_ERROR;
1899            }
1900        }
1901
1902    }
1903    /*
1904     * Now do any additional raw files.
1905     */
1906    for (int arg=0; arg<N; arg++) {
1907        const char* assetDir = bundle->getFileSpecEntry(arg);
1908
1909        FileType type = getFileType(assetDir);
1910        if (type == kFileTypeNonexistent) {
1911            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1912            return UNKNOWN_ERROR;
1913        }
1914        if (type != kFileTypeDirectory) {
1915            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1916            return UNKNOWN_ERROR;
1917        }
1918
1919        String8 assetRoot(assetDir);
1920
1921        if (bundle->getVerbose())
1922            printf("Processing raw dir '%s'\n", (const char*) assetDir);
1923
1924        /*
1925         * Do a recursive traversal of subdir tree.  We don't make any
1926         * guarantees about ordering, so we're okay with an inorder search
1927         * using whatever order the OS happens to hand back to us.
1928         */
1929        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullResPaths);
1930        if (count < 0) {
1931            /* failure; report error and remove archive */
1932            totalCount = count;
1933            goto bail;
1934        }
1935        totalCount += count;
1936
1937        if (bundle->getVerbose())
1938            printf("Found %d asset file%s in %s\n",
1939                   count, (count==1) ? "" : "s", assetDir);
1940    }
1941
1942    count = validate();
1943    if (count != NO_ERROR) {
1944        totalCount = count;
1945        goto bail;
1946    }
1947
1948
1949bail:
1950    return totalCount;
1951}
1952
1953ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1954                                    const AaptGroupEntry& kind,
1955                                    const String8& resType,
1956                                    sp<FilePathStore>& fullResPaths)
1957{
1958    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
1959    if (res > 0) {
1960        mGroupEntries.add(kind);
1961    }
1962
1963    return res;
1964}
1965
1966ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1967{
1968    ssize_t err = 0;
1969
1970    DIR* dir = opendir(srcDir.string());
1971    if (dir == NULL) {
1972        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1973        return UNKNOWN_ERROR;
1974    }
1975
1976    status_t count = 0;
1977
1978    /*
1979     * Run through the directory, looking for dirs that match the
1980     * expected pattern.
1981     */
1982    while (1) {
1983        struct dirent* entry = readdir(dir);
1984        if (entry == NULL) {
1985            break;
1986        }
1987
1988        if (isHidden(srcDir.string(), entry->d_name)) {
1989            continue;
1990        }
1991
1992        String8 subdirName(srcDir);
1993        subdirName.appendPath(entry->d_name);
1994
1995        AaptGroupEntry group;
1996        String8 resType;
1997        bool b = group.initFromDirName(entry->d_name, &resType);
1998        if (!b) {
1999            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2000                    entry->d_name);
2001            err = -1;
2002            continue;
2003        }
2004
2005        if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
2006            int maxResInt = atoi(bundle->getMaxResVersion());
2007            const char *verString = group.version.string();
2008            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2009            if (dirVersionInt > maxResInt) {
2010              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2011              continue;
2012            }
2013        }
2014
2015        FileType type = getFileType(subdirName.string());
2016
2017        if (type == kFileTypeDirectory) {
2018            sp<AaptDir> dir = makeDir(String8(entry->d_name));
2019            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2020                                                resType, mFullResPaths);
2021            if (res < 0) {
2022                count = res;
2023                goto bail;
2024            }
2025            if (res > 0) {
2026                mGroupEntries.add(group);
2027                count += res;
2028            }
2029
2030            mDirs.add(dir);
2031        } else {
2032            if (bundle->getVerbose()) {
2033                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2034            }
2035        }
2036    }
2037
2038bail:
2039    closedir(dir);
2040    dir = NULL;
2041
2042    if (err != 0) {
2043        return err;
2044    }
2045    return count;
2046}
2047
2048ssize_t
2049AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2050{
2051    int count = 0;
2052    SortedVector<AaptGroupEntry> entries;
2053
2054    ZipFile* zip = new ZipFile;
2055    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2056    if (err != NO_ERROR) {
2057        fprintf(stderr, "error opening zip file %s\n", filename);
2058        count = err;
2059        delete zip;
2060        return -1;
2061    }
2062
2063    const int N = zip->getNumEntries();
2064    for (int i=0; i<N; i++) {
2065        ZipEntry* entry = zip->getEntryByIndex(i);
2066        if (entry->getDeleted()) {
2067            continue;
2068        }
2069
2070        String8 entryName(entry->getFileName());
2071
2072        String8 dirName = entryName.getPathDir();
2073        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2074
2075        String8 resType;
2076        AaptGroupEntry kind;
2077
2078        String8 remain;
2079        if (entryName.walkPath(&remain) == kResourceDir) {
2080            // these are the resources, pull their type out of the directory name
2081            kind.initFromDirName(remain.walkPath().string(), &resType);
2082        } else {
2083            // these are untyped and don't have an AaptGroupEntry
2084        }
2085        if (entries.indexOf(kind) < 0) {
2086            entries.add(kind);
2087            mGroupEntries.add(kind);
2088        }
2089
2090        // use the one from the zip file if they both exist.
2091        dir->removeFile(entryName.getPathLeaf());
2092
2093        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2094        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2095        if (err != NO_ERROR) {
2096            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2097            count = err;
2098            goto bail;
2099        }
2100        file->setCompressionMethod(entry->getCompressionMethod());
2101
2102#if 0
2103        if (entryName == "AndroidManifest.xml") {
2104            printf("AndroidManifest.xml\n");
2105        }
2106        printf("\n\nfile: %s\n", entryName.string());
2107#endif
2108
2109        size_t len = entry->getUncompressedLen();
2110        void* data = zip->uncompress(entry);
2111        void* buf = file->editData(len);
2112        memcpy(buf, data, len);
2113
2114#if 0
2115        const int OFF = 0;
2116        const unsigned char* p = (unsigned char*)data;
2117        const unsigned char* end = p+len;
2118        p += OFF;
2119        for (int i=0; i<32 && p < end; i++) {
2120            printf("0x%03x ", i*0x10 + OFF);
2121            for (int j=0; j<0x10 && p < end; j++) {
2122                printf(" %02x", *p);
2123                p++;
2124            }
2125            printf("\n");
2126        }
2127#endif
2128
2129        free(data);
2130
2131        count++;
2132    }
2133
2134bail:
2135    delete zip;
2136    return count;
2137}
2138
2139sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2140{
2141    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2142    if (sym == NULL) {
2143        sym = new AaptSymbols();
2144        mSymbols.add(name, sym);
2145    }
2146    return sym;
2147}
2148
2149status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2150{
2151    if (!mHaveIncludedAssets) {
2152        // Add in all includes.
2153        const Vector<const char*>& incl = bundle->getPackageIncludes();
2154        const size_t N=incl.size();
2155        for (size_t i=0; i<N; i++) {
2156            if (bundle->getVerbose())
2157                printf("Including resources from package: %s\n", incl[i]);
2158            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2159                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2160                        incl[i]);
2161                return UNKNOWN_ERROR;
2162            }
2163        }
2164        mHaveIncludedAssets = true;
2165    }
2166
2167    return NO_ERROR;
2168}
2169
2170status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2171{
2172    const ResTable& res = getIncludedResources();
2173    // XXX dirty!
2174    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2175}
2176
2177const ResTable& AaptAssets::getIncludedResources() const
2178{
2179    return mIncludedAssets.getResources(false);
2180}
2181
2182void AaptAssets::print() const
2183{
2184    printf("Locale/Vendor pairs:\n");
2185    const size_t N=mGroupEntries.size();
2186    for (size_t i=0; i<N; i++) {
2187        printf("   %s/%s\n",
2188               mGroupEntries.itemAt(i).locale.string(),
2189               mGroupEntries.itemAt(i).vendor.string());
2190    }
2191
2192    printf("\nFiles:\n");
2193    AaptDir::print();
2194}
2195
2196sp<AaptDir> AaptAssets::resDir(const String8& name)
2197{
2198    const Vector<sp<AaptDir> >& dirs = mDirs;
2199    const size_t N = dirs.size();
2200    for (size_t i=0; i<N; i++) {
2201        const sp<AaptDir>& d = dirs.itemAt(i);
2202        if (d->getLeaf() == name) {
2203            return d;
2204        }
2205    }
2206    return NULL;
2207}
2208
2209bool
2210valid_symbol_name(const String8& symbol)
2211{
2212    static char const * const KEYWORDS[] = {
2213        "abstract", "assert", "boolean", "break",
2214        "byte", "case", "catch", "char", "class", "const", "continue",
2215        "default", "do", "double", "else", "enum", "extends", "final",
2216        "finally", "float", "for", "goto", "if", "implements", "import",
2217        "instanceof", "int", "interface", "long", "native", "new", "package",
2218        "private", "protected", "public", "return", "short", "static",
2219        "strictfp", "super", "switch", "synchronized", "this", "throw",
2220        "throws", "transient", "try", "void", "volatile", "while",
2221        "true", "false", "null",
2222        NULL
2223    };
2224    const char*const* k = KEYWORDS;
2225    const char*const s = symbol.string();
2226    while (*k) {
2227        if (0 == strcmp(s, *k)) {
2228            return false;
2229        }
2230        k++;
2231    }
2232    return true;
2233}
2234