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