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