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