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