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