AaptAssets.cpp revision 69cb87576ba163b61bb0e6477a3b7c57a9b11d40
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    }
917
918    return false;
919}
920
921bool AaptGroupEntry::getUiModeNightName(const char* name,
922                                          ResTable_config* out)
923{
924    if (strcmp(name, kWildcardName) == 0) {
925        if (out) out->uiMode =
926                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
927                | ResTable_config::UI_MODE_NIGHT_ANY;
928        return true;
929    } else if (strcmp(name, "night") == 0) {
930        if (out) out->uiMode =
931                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
932                | ResTable_config::UI_MODE_NIGHT_YES;
933        return true;
934    } else if (strcmp(name, "notnight") == 0) {
935      if (out) out->uiMode =
936              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
937              | ResTable_config::UI_MODE_NIGHT_NO;
938        return true;
939    }
940
941    return false;
942}
943
944bool AaptGroupEntry::getDensityName(const char* name,
945                                    ResTable_config* out)
946{
947    if (strcmp(name, kWildcardName) == 0) {
948        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
949        return true;
950    }
951
952    if (strcmp(name, "nodpi") == 0) {
953        if (out) out->density = ResTable_config::DENSITY_NONE;
954        return true;
955    }
956
957    if (strcmp(name, "ldpi") == 0) {
958        if (out) out->density = ResTable_config::DENSITY_LOW;
959        return true;
960    }
961
962    if (strcmp(name, "mdpi") == 0) {
963        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
964        return true;
965    }
966
967    if (strcmp(name, "hdpi") == 0) {
968        if (out) out->density = ResTable_config::DENSITY_HIGH;
969        return true;
970    }
971
972    if (strcmp(name, "xhdpi") == 0) {
973        if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
974        return true;
975    }
976
977    char* c = (char*)name;
978    while (*c >= '0' && *c <= '9') {
979        c++;
980    }
981
982    // check that we have 'dpi' after the last digit.
983    if (toupper(c[0]) != 'D' ||
984            toupper(c[1]) != 'P' ||
985            toupper(c[2]) != 'I' ||
986            c[3] != 0) {
987        return false;
988    }
989
990    // temporarily replace the first letter with \0 to
991    // use atoi.
992    char tmp = c[0];
993    c[0] = '\0';
994
995    int d = atoi(name);
996    c[0] = tmp;
997
998    if (d != 0) {
999        if (out) out->density = d;
1000        return true;
1001    }
1002
1003    return false;
1004}
1005
1006bool AaptGroupEntry::getTouchscreenName(const char* name,
1007                                        ResTable_config* out)
1008{
1009    if (strcmp(name, kWildcardName) == 0) {
1010        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1011        return true;
1012    } else if (strcmp(name, "notouch") == 0) {
1013        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1014        return true;
1015    } else if (strcmp(name, "stylus") == 0) {
1016        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1017        return true;
1018    } else if (strcmp(name, "finger") == 0) {
1019        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1020        return true;
1021    }
1022
1023    return false;
1024}
1025
1026bool AaptGroupEntry::getKeysHiddenName(const char* name,
1027                                       ResTable_config* out)
1028{
1029    uint8_t mask = 0;
1030    uint8_t value = 0;
1031    if (strcmp(name, kWildcardName) == 0) {
1032        mask = ResTable_config::MASK_KEYSHIDDEN;
1033        value = ResTable_config::KEYSHIDDEN_ANY;
1034    } else if (strcmp(name, "keysexposed") == 0) {
1035        mask = ResTable_config::MASK_KEYSHIDDEN;
1036        value = ResTable_config::KEYSHIDDEN_NO;
1037    } else if (strcmp(name, "keyshidden") == 0) {
1038        mask = ResTable_config::MASK_KEYSHIDDEN;
1039        value = ResTable_config::KEYSHIDDEN_YES;
1040    } else if (strcmp(name, "keyssoft") == 0) {
1041        mask = ResTable_config::MASK_KEYSHIDDEN;
1042        value = ResTable_config::KEYSHIDDEN_SOFT;
1043    }
1044
1045    if (mask != 0) {
1046        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1047        return true;
1048    }
1049
1050    return false;
1051}
1052
1053bool AaptGroupEntry::getKeyboardName(const char* name,
1054                                        ResTable_config* out)
1055{
1056    if (strcmp(name, kWildcardName) == 0) {
1057        if (out) out->keyboard = out->KEYBOARD_ANY;
1058        return true;
1059    } else if (strcmp(name, "nokeys") == 0) {
1060        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1061        return true;
1062    } else if (strcmp(name, "qwerty") == 0) {
1063        if (out) out->keyboard = out->KEYBOARD_QWERTY;
1064        return true;
1065    } else if (strcmp(name, "12key") == 0) {
1066        if (out) out->keyboard = out->KEYBOARD_12KEY;
1067        return true;
1068    }
1069
1070    return false;
1071}
1072
1073bool AaptGroupEntry::getNavHiddenName(const char* name,
1074                                       ResTable_config* out)
1075{
1076    uint8_t mask = 0;
1077    uint8_t value = 0;
1078    if (strcmp(name, kWildcardName) == 0) {
1079        mask = ResTable_config::MASK_NAVHIDDEN;
1080        value = ResTable_config::NAVHIDDEN_ANY;
1081    } else if (strcmp(name, "navexposed") == 0) {
1082        mask = ResTable_config::MASK_NAVHIDDEN;
1083        value = ResTable_config::NAVHIDDEN_NO;
1084    } else if (strcmp(name, "navhidden") == 0) {
1085        mask = ResTable_config::MASK_NAVHIDDEN;
1086        value = ResTable_config::NAVHIDDEN_YES;
1087    }
1088
1089    if (mask != 0) {
1090        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1091        return true;
1092    }
1093
1094    return false;
1095}
1096
1097bool AaptGroupEntry::getNavigationName(const char* name,
1098                                     ResTable_config* out)
1099{
1100    if (strcmp(name, kWildcardName) == 0) {
1101        if (out) out->navigation = out->NAVIGATION_ANY;
1102        return true;
1103    } else if (strcmp(name, "nonav") == 0) {
1104        if (out) out->navigation = out->NAVIGATION_NONAV;
1105        return true;
1106    } else if (strcmp(name, "dpad") == 0) {
1107        if (out) out->navigation = out->NAVIGATION_DPAD;
1108        return true;
1109    } else if (strcmp(name, "trackball") == 0) {
1110        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1111        return true;
1112    } else if (strcmp(name, "wheel") == 0) {
1113        if (out) out->navigation = out->NAVIGATION_WHEEL;
1114        return true;
1115    }
1116
1117    return false;
1118}
1119
1120bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1121{
1122    if (strcmp(name, kWildcardName) == 0) {
1123        if (out) {
1124            out->screenWidth = out->SCREENWIDTH_ANY;
1125            out->screenHeight = out->SCREENHEIGHT_ANY;
1126        }
1127        return true;
1128    }
1129
1130    const char* x = name;
1131    while (*x >= '0' && *x <= '9') x++;
1132    if (x == name || *x != 'x') return false;
1133    String8 xName(name, x-name);
1134    x++;
1135
1136    const char* y = x;
1137    while (*y >= '0' && *y <= '9') y++;
1138    if (y == name || *y != 0) return false;
1139    String8 yName(x, y-x);
1140
1141    uint16_t w = (uint16_t)atoi(xName.string());
1142    uint16_t h = (uint16_t)atoi(yName.string());
1143    if (w < h) {
1144        return false;
1145    }
1146
1147    if (out) {
1148        out->screenWidth = w;
1149        out->screenHeight = h;
1150    }
1151
1152    return true;
1153}
1154
1155bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1156{
1157    if (strcmp(name, kWildcardName) == 0) {
1158        if (out) {
1159            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1160        }
1161        return true;
1162    }
1163
1164    if (*name != 's') return false;
1165    name++;
1166    if (*name != 'w') return false;
1167    name++;
1168    const char* x = name;
1169    while (*x >= '0' && *x <= '9') x++;
1170    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1171    String8 xName(name, x-name);
1172
1173    if (out) {
1174        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1175    }
1176
1177    return true;
1178}
1179
1180bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1181{
1182    if (strcmp(name, kWildcardName) == 0) {
1183        if (out) {
1184            out->screenWidthDp = out->SCREENWIDTH_ANY;
1185        }
1186        return true;
1187    }
1188
1189    if (*name != 'w') return false;
1190    name++;
1191    const char* x = name;
1192    while (*x >= '0' && *x <= '9') x++;
1193    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1194    String8 xName(name, x-name);
1195
1196    if (out) {
1197        out->screenWidthDp = (uint16_t)atoi(xName.string());
1198    }
1199
1200    return true;
1201}
1202
1203bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1204{
1205    if (strcmp(name, kWildcardName) == 0) {
1206        if (out) {
1207            out->screenHeightDp = out->SCREENWIDTH_ANY;
1208        }
1209        return true;
1210    }
1211
1212    if (*name != 'h') return false;
1213    name++;
1214    const char* x = name;
1215    while (*x >= '0' && *x <= '9') x++;
1216    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1217    String8 xName(name, x-name);
1218
1219    if (out) {
1220        out->screenHeightDp = (uint16_t)atoi(xName.string());
1221    }
1222
1223    return true;
1224}
1225
1226bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1227{
1228    if (strcmp(name, kWildcardName) == 0) {
1229        if (out) {
1230            out->sdkVersion = out->SDKVERSION_ANY;
1231            out->minorVersion = out->MINORVERSION_ANY;
1232        }
1233        return true;
1234    }
1235
1236    if (*name != 'v') {
1237        return false;
1238    }
1239
1240    name++;
1241    const char* s = name;
1242    while (*s >= '0' && *s <= '9') s++;
1243    if (s == name || *s != 0) return false;
1244    String8 sdkName(name, s-name);
1245
1246    if (out) {
1247        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1248        out->minorVersion = 0;
1249    }
1250
1251    return true;
1252}
1253
1254int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1255{
1256    int v = mcc.compare(o.mcc);
1257    if (v == 0) v = mnc.compare(o.mnc);
1258    if (v == 0) v = locale.compare(o.locale);
1259    if (v == 0) v = vendor.compare(o.vendor);
1260    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1261    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1262    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1263    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1264    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1265    if (v == 0) v = orientation.compare(o.orientation);
1266    if (v == 0) v = uiModeType.compare(o.uiModeType);
1267    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1268    if (v == 0) v = density.compare(o.density);
1269    if (v == 0) v = touchscreen.compare(o.touchscreen);
1270    if (v == 0) v = keysHidden.compare(o.keysHidden);
1271    if (v == 0) v = keyboard.compare(o.keyboard);
1272    if (v == 0) v = navHidden.compare(o.navHidden);
1273    if (v == 0) v = navigation.compare(o.navigation);
1274    if (v == 0) v = screenSize.compare(o.screenSize);
1275    if (v == 0) v = version.compare(o.version);
1276    return v;
1277}
1278
1279ResTable_config AaptGroupEntry::toParams() const
1280{
1281    ResTable_config params;
1282    memset(&params, 0, sizeof(params));
1283    getMccName(mcc.string(), &params);
1284    getMncName(mnc.string(), &params);
1285    getLocaleName(locale.string(), &params);
1286    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1287    getScreenWidthDpName(screenWidthDp.string(), &params);
1288    getScreenHeightDpName(screenHeightDp.string(), &params);
1289    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1290    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1291    getOrientationName(orientation.string(), &params);
1292    getUiModeTypeName(uiModeType.string(), &params);
1293    getUiModeNightName(uiModeNight.string(), &params);
1294    getDensityName(density.string(), &params);
1295    getTouchscreenName(touchscreen.string(), &params);
1296    getKeysHiddenName(keysHidden.string(), &params);
1297    getKeyboardName(keyboard.string(), &params);
1298    getNavHiddenName(navHidden.string(), &params);
1299    getNavigationName(navigation.string(), &params);
1300    getScreenSizeName(screenSize.string(), &params);
1301    getVersionName(version.string(), &params);
1302
1303    // Fix up version number based on specified parameters.
1304    int minSdk = 0;
1305    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1306            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1307            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1308        minSdk = SDK_HONEYCOMB_MR2;
1309    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1310                != ResTable_config::UI_MODE_TYPE_ANY
1311            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1312                != ResTable_config::UI_MODE_NIGHT_ANY) {
1313        minSdk = SDK_FROYO;
1314    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1315                != ResTable_config::SCREENSIZE_ANY
1316            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1317                != ResTable_config::SCREENLONG_ANY
1318            || params.density != ResTable_config::DENSITY_DEFAULT) {
1319        minSdk = SDK_DONUT;
1320    }
1321
1322    if (minSdk > params.sdkVersion) {
1323        params.sdkVersion = minSdk;
1324    }
1325
1326    return params;
1327}
1328
1329// =========================================================================
1330// =========================================================================
1331// =========================================================================
1332
1333void* AaptFile::editData(size_t size)
1334{
1335    if (size <= mBufferSize) {
1336        mDataSize = size;
1337        return mData;
1338    }
1339    size_t allocSize = (size*3)/2;
1340    void* buf = realloc(mData, allocSize);
1341    if (buf == NULL) {
1342        return NULL;
1343    }
1344    mData = buf;
1345    mDataSize = size;
1346    mBufferSize = allocSize;
1347    return buf;
1348}
1349
1350void* AaptFile::editData(size_t* outSize)
1351{
1352    if (outSize) {
1353        *outSize = mDataSize;
1354    }
1355    return mData;
1356}
1357
1358void* AaptFile::padData(size_t wordSize)
1359{
1360    const size_t extra = mDataSize%wordSize;
1361    if (extra == 0) {
1362        return mData;
1363    }
1364
1365    size_t initial = mDataSize;
1366    void* data = editData(initial+(wordSize-extra));
1367    if (data != NULL) {
1368        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1369    }
1370    return data;
1371}
1372
1373status_t AaptFile::writeData(const void* data, size_t size)
1374{
1375    size_t end = mDataSize;
1376    size_t total = size + end;
1377    void* buf = editData(total);
1378    if (buf == NULL) {
1379        return UNKNOWN_ERROR;
1380    }
1381    memcpy(((char*)buf)+end, data, size);
1382    return NO_ERROR;
1383}
1384
1385void AaptFile::clearData()
1386{
1387    if (mData != NULL) free(mData);
1388    mData = NULL;
1389    mDataSize = 0;
1390    mBufferSize = 0;
1391}
1392
1393String8 AaptFile::getPrintableSource() const
1394{
1395    if (hasData()) {
1396        String8 name(mGroupEntry.locale.string());
1397        name.appendPath(mGroupEntry.vendor.string());
1398        name.appendPath(mPath);
1399        name.append(" #generated");
1400        return name;
1401    }
1402    return mSourceFile;
1403}
1404
1405// =========================================================================
1406// =========================================================================
1407// =========================================================================
1408
1409status_t AaptGroup::addFile(const sp<AaptFile>& file)
1410{
1411    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1412        file->mPath = mPath;
1413        mFiles.add(file->getGroupEntry(), file);
1414        return NO_ERROR;
1415    }
1416
1417    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1418                                               getPrintableSource().string());
1419    return UNKNOWN_ERROR;
1420}
1421
1422void AaptGroup::removeFile(size_t index)
1423{
1424	mFiles.removeItemsAt(index);
1425}
1426
1427void AaptGroup::print() const
1428{
1429    printf("  %s\n", getPath().string());
1430    const size_t N=mFiles.size();
1431    size_t i;
1432    for (i=0; i<N; i++) {
1433        sp<AaptFile> file = mFiles.valueAt(i);
1434        const AaptGroupEntry& e = file->getGroupEntry();
1435        if (file->hasData()) {
1436            printf("      Gen: (%s) %d bytes\n", e.toString().string(),
1437                    (int)file->getSize());
1438        } else {
1439            printf("      Src: %s\n", file->getPrintableSource().string());
1440        }
1441    }
1442}
1443
1444String8 AaptGroup::getPrintableSource() const
1445{
1446    if (mFiles.size() > 0) {
1447        // Arbitrarily pull the first source file out of the list.
1448        return mFiles.valueAt(0)->getPrintableSource();
1449    }
1450
1451    // Should never hit this case, but to be safe...
1452    return getPath();
1453
1454}
1455
1456// =========================================================================
1457// =========================================================================
1458// =========================================================================
1459
1460status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1461{
1462    if (mFiles.indexOfKey(name) >= 0) {
1463        return ALREADY_EXISTS;
1464    }
1465    mFiles.add(name, file);
1466    return NO_ERROR;
1467}
1468
1469status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1470{
1471    if (mDirs.indexOfKey(name) >= 0) {
1472        return ALREADY_EXISTS;
1473    }
1474    mDirs.add(name, dir);
1475    return NO_ERROR;
1476}
1477
1478sp<AaptDir> AaptDir::makeDir(const String8& path)
1479{
1480    String8 name;
1481    String8 remain = path;
1482
1483    sp<AaptDir> subdir = this;
1484    while (name = remain.walkPath(&remain), remain != "") {
1485        subdir = subdir->makeDir(name);
1486    }
1487
1488    ssize_t i = subdir->mDirs.indexOfKey(name);
1489    if (i >= 0) {
1490        return subdir->mDirs.valueAt(i);
1491    }
1492    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1493    subdir->mDirs.add(name, dir);
1494    return dir;
1495}
1496
1497void AaptDir::removeFile(const String8& name)
1498{
1499    mFiles.removeItem(name);
1500}
1501
1502void AaptDir::removeDir(const String8& name)
1503{
1504    mDirs.removeItem(name);
1505}
1506
1507status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1508{
1509	sp<AaptGroup> origGroup;
1510
1511	// Find and remove the given file with shear, brute force!
1512	const size_t NG = mFiles.size();
1513	size_t i;
1514	for (i=0; origGroup == NULL && i<NG; i++) {
1515		sp<AaptGroup> g = mFiles.valueAt(i);
1516		const size_t NF = g->getFiles().size();
1517		for (size_t j=0; j<NF; j++) {
1518			if (g->getFiles().valueAt(j) == file) {
1519				origGroup = g;
1520				g->removeFile(j);
1521				if (NF == 1) {
1522					mFiles.removeItemsAt(i);
1523				}
1524				break;
1525			}
1526		}
1527	}
1528
1529	//printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1530
1531	// Place the file under its new name.
1532	if (origGroup != NULL) {
1533		return addLeafFile(newName, file);
1534	}
1535
1536	return NO_ERROR;
1537}
1538
1539status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1540{
1541    sp<AaptGroup> group;
1542    if (mFiles.indexOfKey(leafName) >= 0) {
1543        group = mFiles.valueFor(leafName);
1544    } else {
1545        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1546        mFiles.add(leafName, group);
1547    }
1548
1549    return group->addFile(file);
1550}
1551
1552ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1553                            const AaptGroupEntry& kind, const String8& resType)
1554{
1555    Vector<String8> fileNames;
1556
1557    {
1558        DIR* dir = NULL;
1559
1560        dir = opendir(srcDir.string());
1561        if (dir == NULL) {
1562            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1563            return UNKNOWN_ERROR;
1564        }
1565
1566        /*
1567         * Slurp the filenames out of the directory.
1568         */
1569        while (1) {
1570            struct dirent* entry;
1571
1572            entry = readdir(dir);
1573            if (entry == NULL)
1574                break;
1575
1576            if (isHidden(srcDir.string(), entry->d_name))
1577                continue;
1578
1579            fileNames.add(String8(entry->d_name));
1580        }
1581
1582        closedir(dir);
1583    }
1584
1585    ssize_t count = 0;
1586
1587    /*
1588     * Stash away the files and recursively descend into subdirectories.
1589     */
1590    const size_t N = fileNames.size();
1591    size_t i;
1592    for (i = 0; i < N; i++) {
1593        String8 pathName(srcDir);
1594        FileType type;
1595
1596        pathName.appendPath(fileNames[i].string());
1597        type = getFileType(pathName.string());
1598        if (type == kFileTypeDirectory) {
1599            sp<AaptDir> subdir;
1600            bool notAdded = false;
1601            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1602                subdir = mDirs.valueFor(fileNames[i]);
1603            } else {
1604                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1605                notAdded = true;
1606            }
1607            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1608                                                resType);
1609            if (res < NO_ERROR) {
1610                return res;
1611            }
1612            if (res > 0 && notAdded) {
1613                mDirs.add(fileNames[i], subdir);
1614            }
1615            count += res;
1616        } else if (type == kFileTypeRegular) {
1617            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1618            status_t err = addLeafFile(fileNames[i], file);
1619            if (err != NO_ERROR) {
1620                return err;
1621            }
1622
1623            count++;
1624
1625        } else {
1626            if (bundle->getVerbose())
1627                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1628        }
1629    }
1630
1631    return count;
1632}
1633
1634status_t AaptDir::validate() const
1635{
1636    const size_t NF = mFiles.size();
1637    const size_t ND = mDirs.size();
1638    size_t i;
1639    for (i = 0; i < NF; i++) {
1640        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1641            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1642                    "Invalid filename.  Unable to add.");
1643            return UNKNOWN_ERROR;
1644        }
1645
1646        size_t j;
1647        for (j = i+1; j < NF; j++) {
1648            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1649                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1650                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1651                        "File is case-insensitive equivalent to: %s",
1652                        mFiles.valueAt(j)->getPrintableSource().string());
1653                return UNKNOWN_ERROR;
1654            }
1655
1656            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1657            // (this is mostly caught by the "marked" stuff, below)
1658        }
1659
1660        for (j = 0; j < ND; j++) {
1661            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1662                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1663                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1664                        "File conflicts with dir from: %s",
1665                        mDirs.valueAt(j)->getPrintableSource().string());
1666                return UNKNOWN_ERROR;
1667            }
1668        }
1669    }
1670
1671    for (i = 0; i < ND; i++) {
1672        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1673            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1674                    "Invalid directory name, unable to add.");
1675            return UNKNOWN_ERROR;
1676        }
1677
1678        size_t j;
1679        for (j = i+1; j < ND; j++) {
1680            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1681                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1682                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1683                        "Directory is case-insensitive equivalent to: %s",
1684                        mDirs.valueAt(j)->getPrintableSource().string());
1685                return UNKNOWN_ERROR;
1686            }
1687        }
1688
1689        status_t err = mDirs.valueAt(i)->validate();
1690        if (err != NO_ERROR) {
1691            return err;
1692        }
1693    }
1694
1695    return NO_ERROR;
1696}
1697
1698void AaptDir::print() const
1699{
1700    const size_t ND=getDirs().size();
1701    size_t i;
1702    for (i=0; i<ND; i++) {
1703        getDirs().valueAt(i)->print();
1704    }
1705
1706    const size_t NF=getFiles().size();
1707    for (i=0; i<NF; i++) {
1708        getFiles().valueAt(i)->print();
1709    }
1710}
1711
1712String8 AaptDir::getPrintableSource() const
1713{
1714    if (mFiles.size() > 0) {
1715        // Arbitrarily pull the first file out of the list as the source dir.
1716        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1717    }
1718    if (mDirs.size() > 0) {
1719        // Or arbitrarily pull the first dir out of the list as the source dir.
1720        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1721    }
1722
1723    // Should never hit this case, but to be safe...
1724    return mPath;
1725
1726}
1727
1728// =========================================================================
1729// =========================================================================
1730// =========================================================================
1731
1732sp<AaptFile> AaptAssets::addFile(
1733        const String8& filePath, const AaptGroupEntry& entry,
1734        const String8& srcDir, sp<AaptGroup>* outGroup,
1735        const String8& resType)
1736{
1737    sp<AaptDir> dir = this;
1738    sp<AaptGroup> group;
1739    sp<AaptFile> file;
1740    String8 root, remain(filePath), partialPath;
1741    while (remain.length() > 0) {
1742        root = remain.walkPath(&remain);
1743        partialPath.appendPath(root);
1744
1745        const String8 rootStr(root);
1746
1747        if (remain.length() == 0) {
1748            ssize_t i = dir->getFiles().indexOfKey(rootStr);
1749            if (i >= 0) {
1750                group = dir->getFiles().valueAt(i);
1751            } else {
1752                group = new AaptGroup(rootStr, filePath);
1753                status_t res = dir->addFile(rootStr, group);
1754                if (res != NO_ERROR) {
1755                    return NULL;
1756                }
1757            }
1758            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1759            status_t res = group->addFile(file);
1760            if (res != NO_ERROR) {
1761                return NULL;
1762            }
1763            break;
1764
1765        } else {
1766            ssize_t i = dir->getDirs().indexOfKey(rootStr);
1767            if (i >= 0) {
1768                dir = dir->getDirs().valueAt(i);
1769            } else {
1770                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1771                status_t res = dir->addDir(rootStr, subdir);
1772                if (res != NO_ERROR) {
1773                    return NULL;
1774                }
1775                dir = subdir;
1776            }
1777        }
1778    }
1779
1780    mGroupEntries.add(entry);
1781    if (outGroup) *outGroup = group;
1782    return file;
1783}
1784
1785void AaptAssets::addResource(const String8& leafName, const String8& path,
1786                const sp<AaptFile>& file, const String8& resType)
1787{
1788    sp<AaptDir> res = AaptDir::makeDir(kResString);
1789    String8 dirname = file->getGroupEntry().toDirName(resType);
1790    sp<AaptDir> subdir = res->makeDir(dirname);
1791    sp<AaptGroup> grr = new AaptGroup(leafName, path);
1792    grr->addFile(file);
1793
1794    subdir->addFile(leafName, grr);
1795}
1796
1797
1798ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1799{
1800    int count;
1801    int totalCount = 0;
1802    FileType type;
1803    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1804    const size_t dirCount =resDirs.size();
1805    sp<AaptAssets> current = this;
1806
1807    const int N = bundle->getFileSpecCount();
1808
1809    /*
1810     * If a package manifest was specified, include that first.
1811     */
1812    if (bundle->getAndroidManifestFile() != NULL) {
1813        // place at root of zip.
1814        String8 srcFile(bundle->getAndroidManifestFile());
1815        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1816                NULL, String8());
1817        totalCount++;
1818    }
1819
1820    /*
1821     * If a directory of custom assets was supplied, slurp 'em up.
1822     */
1823    if (bundle->getAssetSourceDir()) {
1824        const char* assetDir = bundle->getAssetSourceDir();
1825
1826        FileType type = getFileType(assetDir);
1827        if (type == kFileTypeNonexistent) {
1828            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1829            return UNKNOWN_ERROR;
1830        }
1831        if (type != kFileTypeDirectory) {
1832            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1833            return UNKNOWN_ERROR;
1834        }
1835
1836        String8 assetRoot(assetDir);
1837        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1838        AaptGroupEntry group;
1839        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1840                                            String8());
1841        if (count < 0) {
1842            totalCount = count;
1843            goto bail;
1844        }
1845        if (count > 0) {
1846            mGroupEntries.add(group);
1847        }
1848        totalCount += count;
1849
1850        if (bundle->getVerbose())
1851            printf("Found %d custom asset file%s in %s\n",
1852                   count, (count==1) ? "" : "s", assetDir);
1853    }
1854
1855    /*
1856     * If a directory of resource-specific assets was supplied, slurp 'em up.
1857     */
1858    for (size_t i=0; i<dirCount; i++) {
1859        const char *res = resDirs[i];
1860        if (res) {
1861            type = getFileType(res);
1862            if (type == kFileTypeNonexistent) {
1863                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1864                return UNKNOWN_ERROR;
1865            }
1866            if (type == kFileTypeDirectory) {
1867                if (i>0) {
1868                    sp<AaptAssets> nextOverlay = new AaptAssets();
1869                    current->setOverlay(nextOverlay);
1870                    current = nextOverlay;
1871                }
1872                count = current->slurpResourceTree(bundle, String8(res));
1873
1874                if (count < 0) {
1875                    totalCount = count;
1876                    goto bail;
1877                }
1878                totalCount += count;
1879            }
1880            else {
1881                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1882                return UNKNOWN_ERROR;
1883            }
1884        }
1885
1886    }
1887    /*
1888     * Now do any additional raw files.
1889     */
1890    for (int arg=0; arg<N; arg++) {
1891        const char* assetDir = bundle->getFileSpecEntry(arg);
1892
1893        FileType type = getFileType(assetDir);
1894        if (type == kFileTypeNonexistent) {
1895            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1896            return UNKNOWN_ERROR;
1897        }
1898        if (type != kFileTypeDirectory) {
1899            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1900            return UNKNOWN_ERROR;
1901        }
1902
1903        String8 assetRoot(assetDir);
1904
1905        if (bundle->getVerbose())
1906            printf("Processing raw dir '%s'\n", (const char*) assetDir);
1907
1908        /*
1909         * Do a recursive traversal of subdir tree.  We don't make any
1910         * guarantees about ordering, so we're okay with an inorder search
1911         * using whatever order the OS happens to hand back to us.
1912         */
1913        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
1914        if (count < 0) {
1915            /* failure; report error and remove archive */
1916            totalCount = count;
1917            goto bail;
1918        }
1919        totalCount += count;
1920
1921        if (bundle->getVerbose())
1922            printf("Found %d asset file%s in %s\n",
1923                   count, (count==1) ? "" : "s", assetDir);
1924    }
1925
1926    count = validate();
1927    if (count != NO_ERROR) {
1928        totalCount = count;
1929        goto bail;
1930    }
1931
1932
1933bail:
1934    return totalCount;
1935}
1936
1937ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1938                                    const AaptGroupEntry& kind,
1939                                    const String8& resType)
1940{
1941    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
1942    if (res > 0) {
1943        mGroupEntries.add(kind);
1944    }
1945
1946    return res;
1947}
1948
1949ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1950{
1951    ssize_t err = 0;
1952
1953    DIR* dir = opendir(srcDir.string());
1954    if (dir == NULL) {
1955        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1956        return UNKNOWN_ERROR;
1957    }
1958
1959    status_t count = 0;
1960
1961    /*
1962     * Run through the directory, looking for dirs that match the
1963     * expected pattern.
1964     */
1965    while (1) {
1966        struct dirent* entry = readdir(dir);
1967        if (entry == NULL) {
1968            break;
1969        }
1970
1971        if (isHidden(srcDir.string(), entry->d_name)) {
1972            continue;
1973        }
1974
1975        String8 subdirName(srcDir);
1976        subdirName.appendPath(entry->d_name);
1977
1978        AaptGroupEntry group;
1979        String8 resType;
1980        bool b = group.initFromDirName(entry->d_name, &resType);
1981        if (!b) {
1982            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
1983                    entry->d_name);
1984            err = -1;
1985            continue;
1986        }
1987
1988        if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
1989            int maxResInt = atoi(bundle->getMaxResVersion());
1990            const char *verString = group.version.string();
1991            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1992            if (dirVersionInt > maxResInt) {
1993              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1994              continue;
1995            }
1996        }
1997
1998        FileType type = getFileType(subdirName.string());
1999
2000        if (type == kFileTypeDirectory) {
2001            sp<AaptDir> dir = makeDir(String8(entry->d_name));
2002            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2003                                                resType);
2004            if (res < 0) {
2005                count = res;
2006                goto bail;
2007            }
2008            if (res > 0) {
2009                mGroupEntries.add(group);
2010                count += res;
2011            }
2012
2013            mDirs.add(dir);
2014        } else {
2015            if (bundle->getVerbose()) {
2016                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2017            }
2018        }
2019    }
2020
2021bail:
2022    closedir(dir);
2023    dir = NULL;
2024
2025    if (err != 0) {
2026        return err;
2027    }
2028    return count;
2029}
2030
2031ssize_t
2032AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2033{
2034    int count = 0;
2035    SortedVector<AaptGroupEntry> entries;
2036
2037    ZipFile* zip = new ZipFile;
2038    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2039    if (err != NO_ERROR) {
2040        fprintf(stderr, "error opening zip file %s\n", filename);
2041        count = err;
2042        delete zip;
2043        return -1;
2044    }
2045
2046    const int N = zip->getNumEntries();
2047    for (int i=0; i<N; i++) {
2048        ZipEntry* entry = zip->getEntryByIndex(i);
2049        if (entry->getDeleted()) {
2050            continue;
2051        }
2052
2053        String8 entryName(entry->getFileName());
2054
2055        String8 dirName = entryName.getPathDir();
2056        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2057
2058        String8 resType;
2059        AaptGroupEntry kind;
2060
2061        String8 remain;
2062        if (entryName.walkPath(&remain) == kResourceDir) {
2063            // these are the resources, pull their type out of the directory name
2064            kind.initFromDirName(remain.walkPath().string(), &resType);
2065        } else {
2066            // these are untyped and don't have an AaptGroupEntry
2067        }
2068        if (entries.indexOf(kind) < 0) {
2069            entries.add(kind);
2070            mGroupEntries.add(kind);
2071        }
2072
2073        // use the one from the zip file if they both exist.
2074        dir->removeFile(entryName.getPathLeaf());
2075
2076        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2077        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2078        if (err != NO_ERROR) {
2079            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2080            count = err;
2081            goto bail;
2082        }
2083        file->setCompressionMethod(entry->getCompressionMethod());
2084
2085#if 0
2086        if (entryName == "AndroidManifest.xml") {
2087            printf("AndroidManifest.xml\n");
2088        }
2089        printf("\n\nfile: %s\n", entryName.string());
2090#endif
2091
2092        size_t len = entry->getUncompressedLen();
2093        void* data = zip->uncompress(entry);
2094        void* buf = file->editData(len);
2095        memcpy(buf, data, len);
2096
2097#if 0
2098        const int OFF = 0;
2099        const unsigned char* p = (unsigned char*)data;
2100        const unsigned char* end = p+len;
2101        p += OFF;
2102        for (int i=0; i<32 && p < end; i++) {
2103            printf("0x%03x ", i*0x10 + OFF);
2104            for (int j=0; j<0x10 && p < end; j++) {
2105                printf(" %02x", *p);
2106                p++;
2107            }
2108            printf("\n");
2109        }
2110#endif
2111
2112        free(data);
2113
2114        count++;
2115    }
2116
2117bail:
2118    delete zip;
2119    return count;
2120}
2121
2122sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2123{
2124    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2125    if (sym == NULL) {
2126        sym = new AaptSymbols();
2127        mSymbols.add(name, sym);
2128    }
2129    return sym;
2130}
2131
2132status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2133{
2134    if (!mHaveIncludedAssets) {
2135        // Add in all includes.
2136        const Vector<const char*>& incl = bundle->getPackageIncludes();
2137        const size_t N=incl.size();
2138        for (size_t i=0; i<N; i++) {
2139            if (bundle->getVerbose())
2140                printf("Including resources from package: %s\n", incl[i]);
2141            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2142                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2143                        incl[i]);
2144                return UNKNOWN_ERROR;
2145            }
2146        }
2147        mHaveIncludedAssets = true;
2148    }
2149
2150    return NO_ERROR;
2151}
2152
2153status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2154{
2155    const ResTable& res = getIncludedResources();
2156    // XXX dirty!
2157    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2158}
2159
2160const ResTable& AaptAssets::getIncludedResources() const
2161{
2162    return mIncludedAssets.getResources(false);
2163}
2164
2165void AaptAssets::print() const
2166{
2167    printf("Locale/Vendor pairs:\n");
2168    const size_t N=mGroupEntries.size();
2169    for (size_t i=0; i<N; i++) {
2170        printf("   %s/%s\n",
2171               mGroupEntries.itemAt(i).locale.string(),
2172               mGroupEntries.itemAt(i).vendor.string());
2173    }
2174
2175    printf("\nFiles:\n");
2176    AaptDir::print();
2177}
2178
2179sp<AaptDir> AaptAssets::resDir(const String8& name)
2180{
2181    const Vector<sp<AaptDir> >& dirs = mDirs;
2182    const size_t N = dirs.size();
2183    for (size_t i=0; i<N; i++) {
2184        const sp<AaptDir>& d = dirs.itemAt(i);
2185        if (d->getLeaf() == name) {
2186            return d;
2187        }
2188    }
2189    return NULL;
2190}
2191
2192bool
2193valid_symbol_name(const String8& symbol)
2194{
2195    static char const * const KEYWORDS[] = {
2196        "abstract", "assert", "boolean", "break",
2197        "byte", "case", "catch", "char", "class", "const", "continue",
2198        "default", "do", "double", "else", "enum", "extends", "final",
2199        "finally", "float", "for", "goto", "if", "implements", "import",
2200        "instanceof", "int", "interface", "long", "native", "new", "package",
2201        "private", "protected", "public", "return", "short", "static",
2202        "strictfp", "super", "switch", "synchronized", "this", "throw",
2203        "throws", "transient", "try", "void", "volatile", "while",
2204        "true", "false", "null",
2205        NULL
2206    };
2207    const char*const* k = KEYWORDS;
2208    const char*const s = symbol.string();
2209    while (*k) {
2210        if (0 == strcmp(s, *k)) {
2211            return false;
2212        }
2213        k++;
2214    }
2215    return true;
2216}
2217