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