AaptAssets.cpp revision b96cbbd11c4590bec846212c33361e02293f18b5
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{
1565    Vector<String8> fileNames;
1566
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            fileNames.add(String8(entry->d_name));
1590        }
1591
1592        closedir(dir);
1593    }
1594
1595    ssize_t count = 0;
1596
1597    /*
1598     * Stash away the files and recursively descend into subdirectories.
1599     */
1600    const size_t N = fileNames.size();
1601    size_t i;
1602    for (i = 0; i < N; i++) {
1603        String8 pathName(srcDir);
1604        FileType type;
1605
1606        pathName.appendPath(fileNames[i].string());
1607        type = getFileType(pathName.string());
1608        if (type == kFileTypeDirectory) {
1609            sp<AaptDir> subdir;
1610            bool notAdded = false;
1611            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1612                subdir = mDirs.valueFor(fileNames[i]);
1613            } else {
1614                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1615                notAdded = true;
1616            }
1617            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1618                                                resType);
1619            if (res < NO_ERROR) {
1620                return res;
1621            }
1622            if (res > 0 && notAdded) {
1623                mDirs.add(fileNames[i], subdir);
1624            }
1625            count += res;
1626        } else if (type == kFileTypeRegular) {
1627            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1628            status_t err = addLeafFile(fileNames[i], file);
1629            if (err != NO_ERROR) {
1630                return err;
1631            }
1632
1633            count++;
1634
1635        } else {
1636            if (bundle->getVerbose())
1637                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1638        }
1639    }
1640
1641    return count;
1642}
1643
1644status_t AaptDir::validate() const
1645{
1646    const size_t NF = mFiles.size();
1647    const size_t ND = mDirs.size();
1648    size_t i;
1649    for (i = 0; i < NF; i++) {
1650        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1651            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1652                    "Invalid filename.  Unable to add.");
1653            return UNKNOWN_ERROR;
1654        }
1655
1656        size_t j;
1657        for (j = i+1; j < NF; j++) {
1658            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1659                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1660                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1661                        "File is case-insensitive equivalent to: %s",
1662                        mFiles.valueAt(j)->getPrintableSource().string());
1663                return UNKNOWN_ERROR;
1664            }
1665
1666            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1667            // (this is mostly caught by the "marked" stuff, below)
1668        }
1669
1670        for (j = 0; j < ND; j++) {
1671            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1672                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1673                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1674                        "File conflicts with dir from: %s",
1675                        mDirs.valueAt(j)->getPrintableSource().string());
1676                return UNKNOWN_ERROR;
1677            }
1678        }
1679    }
1680
1681    for (i = 0; i < ND; i++) {
1682        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1683            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1684                    "Invalid directory name, unable to add.");
1685            return UNKNOWN_ERROR;
1686        }
1687
1688        size_t j;
1689        for (j = i+1; j < ND; j++) {
1690            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1691                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1692                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1693                        "Directory is case-insensitive equivalent to: %s",
1694                        mDirs.valueAt(j)->getPrintableSource().string());
1695                return UNKNOWN_ERROR;
1696            }
1697        }
1698
1699        status_t err = mDirs.valueAt(i)->validate();
1700        if (err != NO_ERROR) {
1701            return err;
1702        }
1703    }
1704
1705    return NO_ERROR;
1706}
1707
1708void AaptDir::print() const
1709{
1710    const size_t ND=getDirs().size();
1711    size_t i;
1712    for (i=0; i<ND; i++) {
1713        getDirs().valueAt(i)->print();
1714    }
1715
1716    const size_t NF=getFiles().size();
1717    for (i=0; i<NF; i++) {
1718        getFiles().valueAt(i)->print();
1719    }
1720}
1721
1722String8 AaptDir::getPrintableSource() const
1723{
1724    if (mFiles.size() > 0) {
1725        // Arbitrarily pull the first file out of the list as the source dir.
1726        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1727    }
1728    if (mDirs.size() > 0) {
1729        // Or arbitrarily pull the first dir out of the list as the source dir.
1730        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1731    }
1732
1733    // Should never hit this case, but to be safe...
1734    return mPath;
1735
1736}
1737
1738// =========================================================================
1739// =========================================================================
1740// =========================================================================
1741
1742sp<AaptFile> AaptAssets::addFile(
1743        const String8& filePath, const AaptGroupEntry& entry,
1744        const String8& srcDir, sp<AaptGroup>* outGroup,
1745        const String8& resType)
1746{
1747    sp<AaptDir> dir = this;
1748    sp<AaptGroup> group;
1749    sp<AaptFile> file;
1750    String8 root, remain(filePath), partialPath;
1751    while (remain.length() > 0) {
1752        root = remain.walkPath(&remain);
1753        partialPath.appendPath(root);
1754
1755        const String8 rootStr(root);
1756
1757        if (remain.length() == 0) {
1758            ssize_t i = dir->getFiles().indexOfKey(rootStr);
1759            if (i >= 0) {
1760                group = dir->getFiles().valueAt(i);
1761            } else {
1762                group = new AaptGroup(rootStr, filePath);
1763                status_t res = dir->addFile(rootStr, group);
1764                if (res != NO_ERROR) {
1765                    return NULL;
1766                }
1767            }
1768            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1769            status_t res = group->addFile(file);
1770            if (res != NO_ERROR) {
1771                return NULL;
1772            }
1773            break;
1774
1775        } else {
1776            ssize_t i = dir->getDirs().indexOfKey(rootStr);
1777            if (i >= 0) {
1778                dir = dir->getDirs().valueAt(i);
1779            } else {
1780                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1781                status_t res = dir->addDir(rootStr, subdir);
1782                if (res != NO_ERROR) {
1783                    return NULL;
1784                }
1785                dir = subdir;
1786            }
1787        }
1788    }
1789
1790    mGroupEntries.add(entry);
1791    if (outGroup) *outGroup = group;
1792    return file;
1793}
1794
1795void AaptAssets::addResource(const String8& leafName, const String8& path,
1796                const sp<AaptFile>& file, const String8& resType)
1797{
1798    sp<AaptDir> res = AaptDir::makeDir(kResString);
1799    String8 dirname = file->getGroupEntry().toDirName(resType);
1800    sp<AaptDir> subdir = res->makeDir(dirname);
1801    sp<AaptGroup> grr = new AaptGroup(leafName, path);
1802    grr->addFile(file);
1803
1804    subdir->addFile(leafName, grr);
1805}
1806
1807
1808ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1809{
1810    int count;
1811    int totalCount = 0;
1812    FileType type;
1813    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1814    const size_t dirCount =resDirs.size();
1815    sp<AaptAssets> current = this;
1816
1817    const int N = bundle->getFileSpecCount();
1818
1819    /*
1820     * If a package manifest was specified, include that first.
1821     */
1822    if (bundle->getAndroidManifestFile() != NULL) {
1823        // place at root of zip.
1824        String8 srcFile(bundle->getAndroidManifestFile());
1825        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1826                NULL, String8());
1827        totalCount++;
1828    }
1829
1830    /*
1831     * If a directory of custom assets was supplied, slurp 'em up.
1832     */
1833    if (bundle->getAssetSourceDir()) {
1834        const char* assetDir = bundle->getAssetSourceDir();
1835
1836        FileType type = getFileType(assetDir);
1837        if (type == kFileTypeNonexistent) {
1838            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1839            return UNKNOWN_ERROR;
1840        }
1841        if (type != kFileTypeDirectory) {
1842            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1843            return UNKNOWN_ERROR;
1844        }
1845
1846        String8 assetRoot(assetDir);
1847        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1848        AaptGroupEntry group;
1849        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1850                                            String8());
1851        if (count < 0) {
1852            totalCount = count;
1853            goto bail;
1854        }
1855        if (count > 0) {
1856            mGroupEntries.add(group);
1857        }
1858        totalCount += count;
1859
1860        if (bundle->getVerbose())
1861            printf("Found %d custom asset file%s in %s\n",
1862                   count, (count==1) ? "" : "s", assetDir);
1863    }
1864
1865    /*
1866     * If a directory of resource-specific assets was supplied, slurp 'em up.
1867     */
1868    for (size_t i=0; i<dirCount; i++) {
1869        const char *res = resDirs[i];
1870        if (res) {
1871            type = getFileType(res);
1872            if (type == kFileTypeNonexistent) {
1873                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1874                return UNKNOWN_ERROR;
1875            }
1876            if (type == kFileTypeDirectory) {
1877                if (i>0) {
1878                    sp<AaptAssets> nextOverlay = new AaptAssets();
1879                    current->setOverlay(nextOverlay);
1880                    current = nextOverlay;
1881                }
1882                count = current->slurpResourceTree(bundle, String8(res));
1883
1884                if (count < 0) {
1885                    totalCount = count;
1886                    goto bail;
1887                }
1888                totalCount += count;
1889            }
1890            else {
1891                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1892                return UNKNOWN_ERROR;
1893            }
1894        }
1895
1896    }
1897    /*
1898     * Now do any additional raw files.
1899     */
1900    for (int arg=0; arg<N; arg++) {
1901        const char* assetDir = bundle->getFileSpecEntry(arg);
1902
1903        FileType type = getFileType(assetDir);
1904        if (type == kFileTypeNonexistent) {
1905            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1906            return UNKNOWN_ERROR;
1907        }
1908        if (type != kFileTypeDirectory) {
1909            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1910            return UNKNOWN_ERROR;
1911        }
1912
1913        String8 assetRoot(assetDir);
1914
1915        if (bundle->getVerbose())
1916            printf("Processing raw dir '%s'\n", (const char*) assetDir);
1917
1918        /*
1919         * Do a recursive traversal of subdir tree.  We don't make any
1920         * guarantees about ordering, so we're okay with an inorder search
1921         * using whatever order the OS happens to hand back to us.
1922         */
1923        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
1924        if (count < 0) {
1925            /* failure; report error and remove archive */
1926            totalCount = count;
1927            goto bail;
1928        }
1929        totalCount += count;
1930
1931        if (bundle->getVerbose())
1932            printf("Found %d asset file%s in %s\n",
1933                   count, (count==1) ? "" : "s", assetDir);
1934    }
1935
1936    count = validate();
1937    if (count != NO_ERROR) {
1938        totalCount = count;
1939        goto bail;
1940    }
1941
1942
1943bail:
1944    return totalCount;
1945}
1946
1947ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1948                                    const AaptGroupEntry& kind,
1949                                    const String8& resType)
1950{
1951    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
1952    if (res > 0) {
1953        mGroupEntries.add(kind);
1954    }
1955
1956    return res;
1957}
1958
1959ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1960{
1961    ssize_t err = 0;
1962
1963    DIR* dir = opendir(srcDir.string());
1964    if (dir == NULL) {
1965        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1966        return UNKNOWN_ERROR;
1967    }
1968
1969    status_t count = 0;
1970
1971    /*
1972     * Run through the directory, looking for dirs that match the
1973     * expected pattern.
1974     */
1975    while (1) {
1976        struct dirent* entry = readdir(dir);
1977        if (entry == NULL) {
1978            break;
1979        }
1980
1981        if (isHidden(srcDir.string(), entry->d_name)) {
1982            continue;
1983        }
1984
1985        String8 subdirName(srcDir);
1986        subdirName.appendPath(entry->d_name);
1987
1988        AaptGroupEntry group;
1989        String8 resType;
1990        bool b = group.initFromDirName(entry->d_name, &resType);
1991        if (!b) {
1992            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
1993                    entry->d_name);
1994            err = -1;
1995            continue;
1996        }
1997
1998        if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
1999            int maxResInt = atoi(bundle->getMaxResVersion());
2000            const char *verString = group.version.string();
2001            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2002            if (dirVersionInt > maxResInt) {
2003              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2004              continue;
2005            }
2006        }
2007
2008        FileType type = getFileType(subdirName.string());
2009
2010        if (type == kFileTypeDirectory) {
2011            sp<AaptDir> dir = makeDir(String8(entry->d_name));
2012            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2013                                                resType);
2014            if (res < 0) {
2015                count = res;
2016                goto bail;
2017            }
2018            if (res > 0) {
2019                mGroupEntries.add(group);
2020                count += res;
2021            }
2022
2023            mDirs.add(dir);
2024        } else {
2025            if (bundle->getVerbose()) {
2026                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
2027            }
2028        }
2029    }
2030
2031bail:
2032    closedir(dir);
2033    dir = NULL;
2034
2035    if (err != 0) {
2036        return err;
2037    }
2038    return count;
2039}
2040
2041ssize_t
2042AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2043{
2044    int count = 0;
2045    SortedVector<AaptGroupEntry> entries;
2046
2047    ZipFile* zip = new ZipFile;
2048    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2049    if (err != NO_ERROR) {
2050        fprintf(stderr, "error opening zip file %s\n", filename);
2051        count = err;
2052        delete zip;
2053        return -1;
2054    }
2055
2056    const int N = zip->getNumEntries();
2057    for (int i=0; i<N; i++) {
2058        ZipEntry* entry = zip->getEntryByIndex(i);
2059        if (entry->getDeleted()) {
2060            continue;
2061        }
2062
2063        String8 entryName(entry->getFileName());
2064
2065        String8 dirName = entryName.getPathDir();
2066        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2067
2068        String8 resType;
2069        AaptGroupEntry kind;
2070
2071        String8 remain;
2072        if (entryName.walkPath(&remain) == kResourceDir) {
2073            // these are the resources, pull their type out of the directory name
2074            kind.initFromDirName(remain.walkPath().string(), &resType);
2075        } else {
2076            // these are untyped and don't have an AaptGroupEntry
2077        }
2078        if (entries.indexOf(kind) < 0) {
2079            entries.add(kind);
2080            mGroupEntries.add(kind);
2081        }
2082
2083        // use the one from the zip file if they both exist.
2084        dir->removeFile(entryName.getPathLeaf());
2085
2086        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2087        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2088        if (err != NO_ERROR) {
2089            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2090            count = err;
2091            goto bail;
2092        }
2093        file->setCompressionMethod(entry->getCompressionMethod());
2094
2095#if 0
2096        if (entryName == "AndroidManifest.xml") {
2097            printf("AndroidManifest.xml\n");
2098        }
2099        printf("\n\nfile: %s\n", entryName.string());
2100#endif
2101
2102        size_t len = entry->getUncompressedLen();
2103        void* data = zip->uncompress(entry);
2104        void* buf = file->editData(len);
2105        memcpy(buf, data, len);
2106
2107#if 0
2108        const int OFF = 0;
2109        const unsigned char* p = (unsigned char*)data;
2110        const unsigned char* end = p+len;
2111        p += OFF;
2112        for (int i=0; i<32 && p < end; i++) {
2113            printf("0x%03x ", i*0x10 + OFF);
2114            for (int j=0; j<0x10 && p < end; j++) {
2115                printf(" %02x", *p);
2116                p++;
2117            }
2118            printf("\n");
2119        }
2120#endif
2121
2122        free(data);
2123
2124        count++;
2125    }
2126
2127bail:
2128    delete zip;
2129    return count;
2130}
2131
2132sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2133{
2134    sp<AaptSymbols> sym = mSymbols.valueFor(name);
2135    if (sym == NULL) {
2136        sym = new AaptSymbols();
2137        mSymbols.add(name, sym);
2138    }
2139    return sym;
2140}
2141
2142status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2143{
2144    if (!mHaveIncludedAssets) {
2145        // Add in all includes.
2146        const Vector<const char*>& incl = bundle->getPackageIncludes();
2147        const size_t N=incl.size();
2148        for (size_t i=0; i<N; i++) {
2149            if (bundle->getVerbose())
2150                printf("Including resources from package: %s\n", incl[i]);
2151            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2152                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2153                        incl[i]);
2154                return UNKNOWN_ERROR;
2155            }
2156        }
2157        mHaveIncludedAssets = true;
2158    }
2159
2160    return NO_ERROR;
2161}
2162
2163status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2164{
2165    const ResTable& res = getIncludedResources();
2166    // XXX dirty!
2167    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2168}
2169
2170const ResTable& AaptAssets::getIncludedResources() const
2171{
2172    return mIncludedAssets.getResources(false);
2173}
2174
2175void AaptAssets::print() const
2176{
2177    printf("Locale/Vendor pairs:\n");
2178    const size_t N=mGroupEntries.size();
2179    for (size_t i=0; i<N; i++) {
2180        printf("   %s/%s\n",
2181               mGroupEntries.itemAt(i).locale.string(),
2182               mGroupEntries.itemAt(i).vendor.string());
2183    }
2184
2185    printf("\nFiles:\n");
2186    AaptDir::print();
2187}
2188
2189sp<AaptDir> AaptAssets::resDir(const String8& name)
2190{
2191    const Vector<sp<AaptDir> >& dirs = mDirs;
2192    const size_t N = dirs.size();
2193    for (size_t i=0; i<N; i++) {
2194        const sp<AaptDir>& d = dirs.itemAt(i);
2195        if (d->getLeaf() == name) {
2196            return d;
2197        }
2198    }
2199    return NULL;
2200}
2201
2202bool
2203valid_symbol_name(const String8& symbol)
2204{
2205    static char const * const KEYWORDS[] = {
2206        "abstract", "assert", "boolean", "break",
2207        "byte", "case", "catch", "char", "class", "const", "continue",
2208        "default", "do", "double", "else", "enum", "extends", "final",
2209        "finally", "float", "for", "goto", "if", "implements", "import",
2210        "instanceof", "int", "interface", "long", "native", "new", "package",
2211        "private", "protected", "public", "return", "short", "static",
2212        "strictfp", "super", "switch", "synchronized", "this", "throw",
2213        "throws", "transient", "try", "void", "volatile", "while",
2214        "true", "false", "null",
2215        NULL
2216    };
2217    const char*const* k = KEYWORDS;
2218    const char*const s = symbol.string();
2219    while (*k) {
2220        if (0 == strcmp(s, *k)) {
2221            return false;
2222        }
2223        k++;
2224    }
2225    return true;
2226}
2227