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