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