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