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