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    } else if (strcmp(name, "xlarge") == 0) {
770        if (out) out->screenLayout =
771                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
772                | ResTable_config::SCREENSIZE_XLARGE;
773        return true;
774    }
775
776    return false;
777}
778
779bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
780                                     ResTable_config* out)
781{
782    if (strcmp(name, kWildcardName) == 0) {
783        if (out) out->screenLayout =
784                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
785                | ResTable_config::SCREENLONG_ANY;
786        return true;
787    } else if (strcmp(name, "long") == 0) {
788        if (out) out->screenLayout =
789                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
790                | ResTable_config::SCREENLONG_YES;
791        return true;
792    } else if (strcmp(name, "notlong") == 0) {
793        if (out) out->screenLayout =
794                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
795                | ResTable_config::SCREENLONG_NO;
796        return true;
797    }
798
799    return false;
800}
801
802bool AaptGroupEntry::getOrientationName(const char* name,
803                                        ResTable_config* out)
804{
805    if (strcmp(name, kWildcardName) == 0) {
806        if (out) out->orientation = out->ORIENTATION_ANY;
807        return true;
808    } else if (strcmp(name, "port") == 0) {
809        if (out) out->orientation = out->ORIENTATION_PORT;
810        return true;
811    } else if (strcmp(name, "land") == 0) {
812        if (out) out->orientation = out->ORIENTATION_LAND;
813        return true;
814    } else if (strcmp(name, "square") == 0) {
815        if (out) out->orientation = out->ORIENTATION_SQUARE;
816        return true;
817    }
818
819    return false;
820}
821
822bool AaptGroupEntry::getUiModeTypeName(const char* name,
823                                       ResTable_config* out)
824{
825    if (strcmp(name, kWildcardName) == 0) {
826        if (out) out->uiMode =
827                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
828                | ResTable_config::UI_MODE_TYPE_ANY;
829        return true;
830    } else if (strcmp(name, "desk") == 0) {
831      if (out) out->uiMode =
832              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
833              | ResTable_config::UI_MODE_TYPE_DESK;
834        return true;
835    } else if (strcmp(name, "car") == 0) {
836      if (out) out->uiMode =
837              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
838              | ResTable_config::UI_MODE_TYPE_CAR;
839        return true;
840    }
841
842    return false;
843}
844
845bool AaptGroupEntry::getUiModeNightName(const char* name,
846                                          ResTable_config* out)
847{
848    if (strcmp(name, kWildcardName) == 0) {
849        if (out) out->uiMode =
850                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
851                | ResTable_config::UI_MODE_NIGHT_ANY;
852        return true;
853    } else if (strcmp(name, "night") == 0) {
854        if (out) out->uiMode =
855                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
856                | ResTable_config::UI_MODE_NIGHT_YES;
857        return true;
858    } else if (strcmp(name, "notnight") == 0) {
859      if (out) out->uiMode =
860              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
861              | ResTable_config::UI_MODE_NIGHT_NO;
862        return true;
863    }
864
865    return false;
866}
867
868bool AaptGroupEntry::getDensityName(const char* name,
869                                    ResTable_config* out)
870{
871    if (strcmp(name, kWildcardName) == 0) {
872        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
873        return true;
874    }
875
876    if (strcmp(name, "nodpi") == 0) {
877        if (out) out->density = ResTable_config::DENSITY_NONE;
878        return true;
879    }
880
881    if (strcmp(name, "ldpi") == 0) {
882        if (out) out->density = ResTable_config::DENSITY_LOW;
883        return true;
884    }
885
886    if (strcmp(name, "mdpi") == 0) {
887        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
888        return true;
889    }
890
891    if (strcmp(name, "hdpi") == 0) {
892        if (out) out->density = ResTable_config::DENSITY_HIGH;
893        return true;
894    }
895
896    if (strcmp(name, "xhdpi") == 0) {
897        if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
898        return true;
899    }
900
901    char* c = (char*)name;
902    while (*c >= '0' && *c <= '9') {
903        c++;
904    }
905
906    // check that we have 'dpi' after the last digit.
907    if (toupper(c[0]) != 'D' ||
908            toupper(c[1]) != 'P' ||
909            toupper(c[2]) != 'I' ||
910            c[3] != 0) {
911        return false;
912    }
913
914    // temporarily replace the first letter with \0 to
915    // use atoi.
916    char tmp = c[0];
917    c[0] = '\0';
918
919    int d = atoi(name);
920    c[0] = tmp;
921
922    if (d != 0) {
923        if (out) out->density = d;
924        return true;
925    }
926
927    return false;
928}
929
930bool AaptGroupEntry::getTouchscreenName(const char* name,
931                                        ResTable_config* out)
932{
933    if (strcmp(name, kWildcardName) == 0) {
934        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
935        return true;
936    } else if (strcmp(name, "notouch") == 0) {
937        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
938        return true;
939    } else if (strcmp(name, "stylus") == 0) {
940        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
941        return true;
942    } else if (strcmp(name, "finger") == 0) {
943        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
944        return true;
945    }
946
947    return false;
948}
949
950bool AaptGroupEntry::getKeysHiddenName(const char* name,
951                                       ResTable_config* out)
952{
953    uint8_t mask = 0;
954    uint8_t value = 0;
955    if (strcmp(name, kWildcardName) == 0) {
956        mask = ResTable_config::MASK_KEYSHIDDEN;
957        value = ResTable_config::KEYSHIDDEN_ANY;
958    } else if (strcmp(name, "keysexposed") == 0) {
959        mask = ResTable_config::MASK_KEYSHIDDEN;
960        value = ResTable_config::KEYSHIDDEN_NO;
961    } else if (strcmp(name, "keyshidden") == 0) {
962        mask = ResTable_config::MASK_KEYSHIDDEN;
963        value = ResTable_config::KEYSHIDDEN_YES;
964    } else if (strcmp(name, "keyssoft") == 0) {
965        mask = ResTable_config::MASK_KEYSHIDDEN;
966        value = ResTable_config::KEYSHIDDEN_SOFT;
967    }
968
969    if (mask != 0) {
970        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
971        return true;
972    }
973
974    return false;
975}
976
977bool AaptGroupEntry::getKeyboardName(const char* name,
978                                        ResTable_config* out)
979{
980    if (strcmp(name, kWildcardName) == 0) {
981        if (out) out->keyboard = out->KEYBOARD_ANY;
982        return true;
983    } else if (strcmp(name, "nokeys") == 0) {
984        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
985        return true;
986    } else if (strcmp(name, "qwerty") == 0) {
987        if (out) out->keyboard = out->KEYBOARD_QWERTY;
988        return true;
989    } else if (strcmp(name, "12key") == 0) {
990        if (out) out->keyboard = out->KEYBOARD_12KEY;
991        return true;
992    }
993
994    return false;
995}
996
997bool AaptGroupEntry::getNavHiddenName(const char* name,
998                                       ResTable_config* out)
999{
1000    uint8_t mask = 0;
1001    uint8_t value = 0;
1002    if (strcmp(name, kWildcardName) == 0) {
1003        mask = ResTable_config::MASK_NAVHIDDEN;
1004        value = ResTable_config::NAVHIDDEN_ANY;
1005    } else if (strcmp(name, "navexposed") == 0) {
1006        mask = ResTable_config::MASK_NAVHIDDEN;
1007        value = ResTable_config::NAVHIDDEN_NO;
1008    } else if (strcmp(name, "navhidden") == 0) {
1009        mask = ResTable_config::MASK_NAVHIDDEN;
1010        value = ResTable_config::NAVHIDDEN_YES;
1011    }
1012
1013    if (mask != 0) {
1014        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1015        return true;
1016    }
1017
1018    return false;
1019}
1020
1021bool AaptGroupEntry::getNavigationName(const char* name,
1022                                     ResTable_config* out)
1023{
1024    if (strcmp(name, kWildcardName) == 0) {
1025        if (out) out->navigation = out->NAVIGATION_ANY;
1026        return true;
1027    } else if (strcmp(name, "nonav") == 0) {
1028        if (out) out->navigation = out->NAVIGATION_NONAV;
1029        return true;
1030    } else if (strcmp(name, "dpad") == 0) {
1031        if (out) out->navigation = out->NAVIGATION_DPAD;
1032        return true;
1033    } else if (strcmp(name, "trackball") == 0) {
1034        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1035        return true;
1036    } else if (strcmp(name, "wheel") == 0) {
1037        if (out) out->navigation = out->NAVIGATION_WHEEL;
1038        return true;
1039    }
1040
1041    return false;
1042}
1043
1044bool AaptGroupEntry::getScreenSizeName(const char* name,
1045                                       ResTable_config* out)
1046{
1047    if (strcmp(name, kWildcardName) == 0) {
1048        if (out) {
1049            out->screenWidth = out->SCREENWIDTH_ANY;
1050            out->screenHeight = out->SCREENHEIGHT_ANY;
1051        }
1052        return true;
1053    }
1054
1055    const char* x = name;
1056    while (*x >= '0' && *x <= '9') x++;
1057    if (x == name || *x != 'x') return false;
1058    String8 xName(name, x-name);
1059    x++;
1060
1061    const char* y = x;
1062    while (*y >= '0' && *y <= '9') y++;
1063    if (y == name || *y != 0) return false;
1064    String8 yName(x, y-x);
1065
1066    uint16_t w = (uint16_t)atoi(xName.string());
1067    uint16_t h = (uint16_t)atoi(yName.string());
1068    if (w < h) {
1069        return false;
1070    }
1071
1072    if (out) {
1073        out->screenWidth = w;
1074        out->screenHeight = h;
1075    }
1076
1077    return true;
1078}
1079
1080bool AaptGroupEntry::getVersionName(const char* name,
1081                                    ResTable_config* out)
1082{
1083    if (strcmp(name, kWildcardName) == 0) {
1084        if (out) {
1085            out->sdkVersion = out->SDKVERSION_ANY;
1086            out->minorVersion = out->MINORVERSION_ANY;
1087        }
1088        return true;
1089    }
1090
1091    if (*name != 'v') {
1092        return false;
1093    }
1094
1095    name++;
1096    const char* s = name;
1097    while (*s >= '0' && *s <= '9') s++;
1098    if (s == name || *s != 0) return false;
1099    String8 sdkName(name, s-name);
1100
1101    if (out) {
1102        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1103        out->minorVersion = 0;
1104    }
1105
1106    return true;
1107}
1108
1109int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1110{
1111    int v = mcc.compare(o.mcc);
1112    if (v == 0) v = mnc.compare(o.mnc);
1113    if (v == 0) v = locale.compare(o.locale);
1114    if (v == 0) v = vendor.compare(o.vendor);
1115    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1116    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1117    if (v == 0) v = orientation.compare(o.orientation);
1118    if (v == 0) v = uiModeType.compare(o.uiModeType);
1119    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1120    if (v == 0) v = density.compare(o.density);
1121    if (v == 0) v = touchscreen.compare(o.touchscreen);
1122    if (v == 0) v = keysHidden.compare(o.keysHidden);
1123    if (v == 0) v = keyboard.compare(o.keyboard);
1124    if (v == 0) v = navHidden.compare(o.navHidden);
1125    if (v == 0) v = navigation.compare(o.navigation);
1126    if (v == 0) v = screenSize.compare(o.screenSize);
1127    if (v == 0) v = version.compare(o.version);
1128    return v;
1129}
1130
1131ResTable_config AaptGroupEntry::toParams() const
1132{
1133    ResTable_config params;
1134    memset(&params, 0, sizeof(params));
1135    getMccName(mcc.string(), &params);
1136    getMncName(mnc.string(), &params);
1137    getLocaleName(locale.string(), &params);
1138    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1139    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1140    getOrientationName(orientation.string(), &params);
1141    getUiModeTypeName(uiModeType.string(), &params);
1142    getUiModeNightName(uiModeNight.string(), &params);
1143    getDensityName(density.string(), &params);
1144    getTouchscreenName(touchscreen.string(), &params);
1145    getKeysHiddenName(keysHidden.string(), &params);
1146    getKeyboardName(keyboard.string(), &params);
1147    getNavHiddenName(navHidden.string(), &params);
1148    getNavigationName(navigation.string(), &params);
1149    getScreenSizeName(screenSize.string(), &params);
1150    getVersionName(version.string(), &params);
1151
1152    // Fix up version number based on specified parameters.
1153    int minSdk = 0;
1154    if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1155                != ResTable_config::UI_MODE_TYPE_ANY
1156            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1157                != ResTable_config::UI_MODE_NIGHT_ANY) {
1158        minSdk = SDK_FROYO;
1159    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1160                != ResTable_config::SCREENSIZE_ANY
1161            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1162                != ResTable_config::SCREENLONG_ANY
1163            || params.density != ResTable_config::DENSITY_DEFAULT) {
1164        minSdk = SDK_DONUT;
1165    }
1166
1167    if (minSdk > params.sdkVersion) {
1168        params.sdkVersion = minSdk;
1169    }
1170
1171    return params;
1172}
1173
1174// =========================================================================
1175// =========================================================================
1176// =========================================================================
1177
1178void* AaptFile::editData(size_t size)
1179{
1180    if (size <= mBufferSize) {
1181        mDataSize = size;
1182        return mData;
1183    }
1184    size_t allocSize = (size*3)/2;
1185    void* buf = realloc(mData, allocSize);
1186    if (buf == NULL) {
1187        return NULL;
1188    }
1189    mData = buf;
1190    mDataSize = size;
1191    mBufferSize = allocSize;
1192    return buf;
1193}
1194
1195void* AaptFile::editData(size_t* outSize)
1196{
1197    if (outSize) {
1198        *outSize = mDataSize;
1199    }
1200    return mData;
1201}
1202
1203void* AaptFile::padData(size_t wordSize)
1204{
1205    const size_t extra = mDataSize%wordSize;
1206    if (extra == 0) {
1207        return mData;
1208    }
1209
1210    size_t initial = mDataSize;
1211    void* data = editData(initial+(wordSize-extra));
1212    if (data != NULL) {
1213        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1214    }
1215    return data;
1216}
1217
1218status_t AaptFile::writeData(const void* data, size_t size)
1219{
1220    size_t end = mDataSize;
1221    size_t total = size + end;
1222    void* buf = editData(total);
1223    if (buf == NULL) {
1224        return UNKNOWN_ERROR;
1225    }
1226    memcpy(((char*)buf)+end, data, size);
1227    return NO_ERROR;
1228}
1229
1230void AaptFile::clearData()
1231{
1232    if (mData != NULL) free(mData);
1233    mData = NULL;
1234    mDataSize = 0;
1235    mBufferSize = 0;
1236}
1237
1238String8 AaptFile::getPrintableSource() const
1239{
1240    if (hasData()) {
1241        String8 name(mGroupEntry.locale.string());
1242        name.appendPath(mGroupEntry.vendor.string());
1243        name.appendPath(mPath);
1244        name.append(" #generated");
1245        return name;
1246    }
1247    return mSourceFile;
1248}
1249
1250// =========================================================================
1251// =========================================================================
1252// =========================================================================
1253
1254status_t AaptGroup::addFile(const sp<AaptFile>& file)
1255{
1256    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1257        file->mPath = mPath;
1258        mFiles.add(file->getGroupEntry(), file);
1259        return NO_ERROR;
1260    }
1261
1262    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1263                                               getPrintableSource().string());
1264    return UNKNOWN_ERROR;
1265}
1266
1267void AaptGroup::removeFile(size_t index)
1268{
1269	mFiles.removeItemsAt(index);
1270}
1271
1272void AaptGroup::print() const
1273{
1274    printf("  %s\n", getPath().string());
1275    const size_t N=mFiles.size();
1276    size_t i;
1277    for (i=0; i<N; i++) {
1278        sp<AaptFile> file = mFiles.valueAt(i);
1279        const AaptGroupEntry& e = file->getGroupEntry();
1280        if (file->hasData()) {
1281            printf("      Gen: (%s) %d bytes\n", e.toString().string(),
1282                    (int)file->getSize());
1283        } else {
1284            printf("      Src: %s\n", file->getPrintableSource().string());
1285        }
1286    }
1287}
1288
1289String8 AaptGroup::getPrintableSource() const
1290{
1291    if (mFiles.size() > 0) {
1292        // Arbitrarily pull the first source file out of the list.
1293        return mFiles.valueAt(0)->getPrintableSource();
1294    }
1295
1296    // Should never hit this case, but to be safe...
1297    return getPath();
1298
1299}
1300
1301// =========================================================================
1302// =========================================================================
1303// =========================================================================
1304
1305status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1306{
1307    if (mFiles.indexOfKey(name) >= 0) {
1308        return ALREADY_EXISTS;
1309    }
1310    mFiles.add(name, file);
1311    return NO_ERROR;
1312}
1313
1314status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1315{
1316    if (mDirs.indexOfKey(name) >= 0) {
1317        return ALREADY_EXISTS;
1318    }
1319    mDirs.add(name, dir);
1320    return NO_ERROR;
1321}
1322
1323sp<AaptDir> AaptDir::makeDir(const String8& path)
1324{
1325    String8 name;
1326    String8 remain = path;
1327
1328    sp<AaptDir> subdir = this;
1329    while (name = remain.walkPath(&remain), remain != "") {
1330        subdir = subdir->makeDir(name);
1331    }
1332
1333    ssize_t i = subdir->mDirs.indexOfKey(name);
1334    if (i >= 0) {
1335        return subdir->mDirs.valueAt(i);
1336    }
1337    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1338    subdir->mDirs.add(name, dir);
1339    return dir;
1340}
1341
1342void AaptDir::removeFile(const String8& name)
1343{
1344    mFiles.removeItem(name);
1345}
1346
1347void AaptDir::removeDir(const String8& name)
1348{
1349    mDirs.removeItem(name);
1350}
1351
1352status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1353{
1354	sp<AaptGroup> origGroup;
1355
1356	// Find and remove the given file with shear, brute force!
1357	const size_t NG = mFiles.size();
1358	size_t i;
1359	for (i=0; origGroup == NULL && i<NG; i++) {
1360		sp<AaptGroup> g = mFiles.valueAt(i);
1361		const size_t NF = g->getFiles().size();
1362		for (size_t j=0; j<NF; j++) {
1363			if (g->getFiles().valueAt(j) == file) {
1364				origGroup = g;
1365				g->removeFile(j);
1366				if (NF == 1) {
1367					mFiles.removeItemsAt(i);
1368				}
1369				break;
1370			}
1371		}
1372	}
1373
1374	//printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1375
1376	// Place the file under its new name.
1377	if (origGroup != NULL) {
1378		return addLeafFile(newName, file);
1379	}
1380
1381	return NO_ERROR;
1382}
1383
1384status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1385{
1386    sp<AaptGroup> group;
1387    if (mFiles.indexOfKey(leafName) >= 0) {
1388        group = mFiles.valueFor(leafName);
1389    } else {
1390        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1391        mFiles.add(leafName, group);
1392    }
1393
1394    return group->addFile(file);
1395}
1396
1397ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1398                            const AaptGroupEntry& kind, const String8& resType)
1399{
1400    Vector<String8> fileNames;
1401
1402    {
1403        DIR* dir = NULL;
1404
1405        dir = opendir(srcDir.string());
1406        if (dir == NULL) {
1407            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1408            return UNKNOWN_ERROR;
1409        }
1410
1411        /*
1412         * Slurp the filenames out of the directory.
1413         */
1414        while (1) {
1415            struct dirent* entry;
1416
1417            entry = readdir(dir);
1418            if (entry == NULL)
1419                break;
1420
1421            if (isHidden(srcDir.string(), entry->d_name))
1422                continue;
1423
1424            fileNames.add(String8(entry->d_name));
1425        }
1426
1427        closedir(dir);
1428    }
1429
1430    ssize_t count = 0;
1431
1432    /*
1433     * Stash away the files and recursively descend into subdirectories.
1434     */
1435    const size_t N = fileNames.size();
1436    size_t i;
1437    for (i = 0; i < N; i++) {
1438        String8 pathName(srcDir);
1439        FileType type;
1440
1441        pathName.appendPath(fileNames[i].string());
1442        type = getFileType(pathName.string());
1443        if (type == kFileTypeDirectory) {
1444            sp<AaptDir> subdir;
1445            bool notAdded = false;
1446            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1447                subdir = mDirs.valueFor(fileNames[i]);
1448            } else {
1449                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1450                notAdded = true;
1451            }
1452            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1453                                                resType);
1454            if (res < NO_ERROR) {
1455                return res;
1456            }
1457            if (res > 0 && notAdded) {
1458                mDirs.add(fileNames[i], subdir);
1459            }
1460            count += res;
1461        } else if (type == kFileTypeRegular) {
1462            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1463            status_t err = addLeafFile(fileNames[i], file);
1464            if (err != NO_ERROR) {
1465                return err;
1466            }
1467
1468            count++;
1469
1470        } else {
1471            if (bundle->getVerbose())
1472                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1473        }
1474    }
1475
1476    return count;
1477}
1478
1479status_t AaptDir::validate() const
1480{
1481    const size_t NF = mFiles.size();
1482    const size_t ND = mDirs.size();
1483    size_t i;
1484    for (i = 0; i < NF; i++) {
1485        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1486            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1487                    "Invalid filename.  Unable to add.");
1488            return UNKNOWN_ERROR;
1489        }
1490
1491        size_t j;
1492        for (j = i+1; j < NF; j++) {
1493            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1494                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1495                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1496                        "File is case-insensitive equivalent to: %s",
1497                        mFiles.valueAt(j)->getPrintableSource().string());
1498                return UNKNOWN_ERROR;
1499            }
1500
1501            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1502            // (this is mostly caught by the "marked" stuff, below)
1503        }
1504
1505        for (j = 0; j < ND; j++) {
1506            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1507                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1508                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1509                        "File conflicts with dir from: %s",
1510                        mDirs.valueAt(j)->getPrintableSource().string());
1511                return UNKNOWN_ERROR;
1512            }
1513        }
1514    }
1515
1516    for (i = 0; i < ND; i++) {
1517        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1518            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1519                    "Invalid directory name, unable to add.");
1520            return UNKNOWN_ERROR;
1521        }
1522
1523        size_t j;
1524        for (j = i+1; j < ND; j++) {
1525            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1526                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1527                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1528                        "Directory is case-insensitive equivalent to: %s",
1529                        mDirs.valueAt(j)->getPrintableSource().string());
1530                return UNKNOWN_ERROR;
1531            }
1532        }
1533
1534        status_t err = mDirs.valueAt(i)->validate();
1535        if (err != NO_ERROR) {
1536            return err;
1537        }
1538    }
1539
1540    return NO_ERROR;
1541}
1542
1543void AaptDir::print() const
1544{
1545    const size_t ND=getDirs().size();
1546    size_t i;
1547    for (i=0; i<ND; i++) {
1548        getDirs().valueAt(i)->print();
1549    }
1550
1551    const size_t NF=getFiles().size();
1552    for (i=0; i<NF; i++) {
1553        getFiles().valueAt(i)->print();
1554    }
1555}
1556
1557String8 AaptDir::getPrintableSource() const
1558{
1559    if (mFiles.size() > 0) {
1560        // Arbitrarily pull the first file out of the list as the source dir.
1561        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1562    }
1563    if (mDirs.size() > 0) {
1564        // Or arbitrarily pull the first dir out of the list as the source dir.
1565        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1566    }
1567
1568    // Should never hit this case, but to be safe...
1569    return mPath;
1570
1571}
1572
1573// =========================================================================
1574// =========================================================================
1575// =========================================================================
1576
1577sp<AaptFile> AaptAssets::addFile(
1578        const String8& filePath, const AaptGroupEntry& entry,
1579        const String8& srcDir, sp<AaptGroup>* outGroup,
1580        const String8& resType)
1581{
1582    sp<AaptDir> dir = this;
1583    sp<AaptGroup> group;
1584    sp<AaptFile> file;
1585    String8 root, remain(filePath), partialPath;
1586    while (remain.length() > 0) {
1587        root = remain.walkPath(&remain);
1588        partialPath.appendPath(root);
1589
1590        const String8 rootStr(root);
1591
1592        if (remain.length() == 0) {
1593            ssize_t i = dir->getFiles().indexOfKey(rootStr);
1594            if (i >= 0) {
1595                group = dir->getFiles().valueAt(i);
1596            } else {
1597                group = new AaptGroup(rootStr, filePath);
1598                status_t res = dir->addFile(rootStr, group);
1599                if (res != NO_ERROR) {
1600                    return NULL;
1601                }
1602            }
1603            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1604            status_t res = group->addFile(file);
1605            if (res != NO_ERROR) {
1606                return NULL;
1607            }
1608            break;
1609
1610        } else {
1611            ssize_t i = dir->getDirs().indexOfKey(rootStr);
1612            if (i >= 0) {
1613                dir = dir->getDirs().valueAt(i);
1614            } else {
1615                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1616                status_t res = dir->addDir(rootStr, subdir);
1617                if (res != NO_ERROR) {
1618                    return NULL;
1619                }
1620                dir = subdir;
1621            }
1622        }
1623    }
1624
1625    mGroupEntries.add(entry);
1626    if (outGroup) *outGroup = group;
1627    return file;
1628}
1629
1630void AaptAssets::addResource(const String8& leafName, const String8& path,
1631                const sp<AaptFile>& file, const String8& resType)
1632{
1633    sp<AaptDir> res = AaptDir::makeDir(kResString);
1634    String8 dirname = file->getGroupEntry().toDirName(resType);
1635    sp<AaptDir> subdir = res->makeDir(dirname);
1636    sp<AaptGroup> grr = new AaptGroup(leafName, path);
1637    grr->addFile(file);
1638
1639    subdir->addFile(leafName, grr);
1640}
1641
1642
1643ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1644{
1645    int count;
1646    int totalCount = 0;
1647    FileType type;
1648    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1649    const size_t dirCount =resDirs.size();
1650    sp<AaptAssets> current = this;
1651
1652    const int N = bundle->getFileSpecCount();
1653
1654    /*
1655     * If a package manifest was specified, include that first.
1656     */
1657    if (bundle->getAndroidManifestFile() != NULL) {
1658        // place at root of zip.
1659        String8 srcFile(bundle->getAndroidManifestFile());
1660        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1661                NULL, String8());
1662        totalCount++;
1663    }
1664
1665    /*
1666     * If a directory of custom assets was supplied, slurp 'em up.
1667     */
1668    if (bundle->getAssetSourceDir()) {
1669        const char* assetDir = bundle->getAssetSourceDir();
1670
1671        FileType type = getFileType(assetDir);
1672        if (type == kFileTypeNonexistent) {
1673            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1674            return UNKNOWN_ERROR;
1675        }
1676        if (type != kFileTypeDirectory) {
1677            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1678            return UNKNOWN_ERROR;
1679        }
1680
1681        String8 assetRoot(assetDir);
1682        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1683        AaptGroupEntry group;
1684        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1685                                            String8());
1686        if (count < 0) {
1687            totalCount = count;
1688            goto bail;
1689        }
1690        if (count > 0) {
1691            mGroupEntries.add(group);
1692        }
1693        totalCount += count;
1694
1695        if (bundle->getVerbose())
1696            printf("Found %d custom asset file%s in %s\n",
1697                   count, (count==1) ? "" : "s", assetDir);
1698    }
1699
1700    /*
1701     * If a directory of resource-specific assets was supplied, slurp 'em up.
1702     */
1703    for (size_t i=0; i<dirCount; i++) {
1704        const char *res = resDirs[i];
1705        if (res) {
1706            type = getFileType(res);
1707            if (type == kFileTypeNonexistent) {
1708                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1709                return UNKNOWN_ERROR;
1710            }
1711            if (type == kFileTypeDirectory) {
1712                if (i>0) {
1713                    sp<AaptAssets> nextOverlay = new AaptAssets();
1714                    current->setOverlay(nextOverlay);
1715                    current = nextOverlay;
1716                }
1717                count = current->slurpResourceTree(bundle, String8(res));
1718
1719                if (count < 0) {
1720                    totalCount = count;
1721                    goto bail;
1722                }
1723                totalCount += count;
1724            }
1725            else {
1726                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1727                return UNKNOWN_ERROR;
1728            }
1729        }
1730
1731    }
1732    /*
1733     * Now do any additional raw files.
1734     */
1735    for (int arg=0; arg<N; arg++) {
1736        const char* assetDir = bundle->getFileSpecEntry(arg);
1737
1738        FileType type = getFileType(assetDir);
1739        if (type == kFileTypeNonexistent) {
1740            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1741            return UNKNOWN_ERROR;
1742        }
1743        if (type != kFileTypeDirectory) {
1744            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1745            return UNKNOWN_ERROR;
1746        }
1747
1748        String8 assetRoot(assetDir);
1749
1750        if (bundle->getVerbose())
1751            printf("Processing raw dir '%s'\n", (const char*) assetDir);
1752
1753        /*
1754         * Do a recursive traversal of subdir tree.  We don't make any
1755         * guarantees about ordering, so we're okay with an inorder search
1756         * using whatever order the OS happens to hand back to us.
1757         */
1758        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
1759        if (count < 0) {
1760            /* failure; report error and remove archive */
1761            totalCount = count;
1762            goto bail;
1763        }
1764        totalCount += count;
1765
1766        if (bundle->getVerbose())
1767            printf("Found %d asset file%s in %s\n",
1768                   count, (count==1) ? "" : "s", assetDir);
1769    }
1770
1771    count = validate();
1772    if (count != NO_ERROR) {
1773        totalCount = count;
1774        goto bail;
1775    }
1776
1777
1778bail:
1779    return totalCount;
1780}
1781
1782ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1783                                    const AaptGroupEntry& kind,
1784                                    const String8& resType)
1785{
1786    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
1787    if (res > 0) {
1788        mGroupEntries.add(kind);
1789    }
1790
1791    return res;
1792}
1793
1794ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1795{
1796    ssize_t err = 0;
1797
1798    DIR* dir = opendir(srcDir.string());
1799    if (dir == NULL) {
1800        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1801        return UNKNOWN_ERROR;
1802    }
1803
1804    status_t count = 0;
1805
1806    /*
1807     * Run through the directory, looking for dirs that match the
1808     * expected pattern.
1809     */
1810    while (1) {
1811        struct dirent* entry = readdir(dir);
1812        if (entry == NULL) {
1813            break;
1814        }
1815
1816        if (isHidden(srcDir.string(), entry->d_name)) {
1817            continue;
1818        }
1819
1820        String8 subdirName(srcDir);
1821        subdirName.appendPath(entry->d_name);
1822
1823        AaptGroupEntry group;
1824        String8 resType;
1825        bool b = group.initFromDirName(entry->d_name, &resType);
1826        if (!b) {
1827            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
1828                    entry->d_name);
1829            err = -1;
1830            continue;
1831        }
1832
1833        if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
1834            int maxResInt = atoi(bundle->getMaxResVersion());
1835            const char *verString = group.version.string();
1836            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1837            if (dirVersionInt > maxResInt) {
1838              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1839              continue;
1840            }
1841        }
1842
1843        FileType type = getFileType(subdirName.string());
1844
1845        if (type == kFileTypeDirectory) {
1846            sp<AaptDir> dir = makeDir(String8(entry->d_name));
1847            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1848                                                resType);
1849            if (res < 0) {
1850                count = res;
1851                goto bail;
1852            }
1853            if (res > 0) {
1854                mGroupEntries.add(group);
1855                count += res;
1856            }
1857
1858            mDirs.add(dir);
1859        } else {
1860            if (bundle->getVerbose()) {
1861                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
1862            }
1863        }
1864    }
1865
1866bail:
1867    closedir(dir);
1868    dir = NULL;
1869
1870    if (err != 0) {
1871        return err;
1872    }
1873    return count;
1874}
1875
1876ssize_t
1877AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
1878{
1879    int count = 0;
1880    SortedVector<AaptGroupEntry> entries;
1881
1882    ZipFile* zip = new ZipFile;
1883    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1884    if (err != NO_ERROR) {
1885        fprintf(stderr, "error opening zip file %s\n", filename);
1886        count = err;
1887        delete zip;
1888        return -1;
1889    }
1890
1891    const int N = zip->getNumEntries();
1892    for (int i=0; i<N; i++) {
1893        ZipEntry* entry = zip->getEntryByIndex(i);
1894        if (entry->getDeleted()) {
1895            continue;
1896        }
1897
1898        String8 entryName(entry->getFileName());
1899
1900        String8 dirName = entryName.getPathDir();
1901        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1902
1903        String8 resType;
1904        AaptGroupEntry kind;
1905
1906        String8 remain;
1907        if (entryName.walkPath(&remain) == kResourceDir) {
1908            // these are the resources, pull their type out of the directory name
1909            kind.initFromDirName(remain.walkPath().string(), &resType);
1910        } else {
1911            // these are untyped and don't have an AaptGroupEntry
1912        }
1913        if (entries.indexOf(kind) < 0) {
1914            entries.add(kind);
1915            mGroupEntries.add(kind);
1916        }
1917
1918        // use the one from the zip file if they both exist.
1919        dir->removeFile(entryName.getPathLeaf());
1920
1921        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1922        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1923        if (err != NO_ERROR) {
1924            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1925            count = err;
1926            goto bail;
1927        }
1928        file->setCompressionMethod(entry->getCompressionMethod());
1929
1930#if 0
1931        if (entryName == "AndroidManifest.xml") {
1932            printf("AndroidManifest.xml\n");
1933        }
1934        printf("\n\nfile: %s\n", entryName.string());
1935#endif
1936
1937        size_t len = entry->getUncompressedLen();
1938        void* data = zip->uncompress(entry);
1939        void* buf = file->editData(len);
1940        memcpy(buf, data, len);
1941
1942#if 0
1943        const int OFF = 0;
1944        const unsigned char* p = (unsigned char*)data;
1945        const unsigned char* end = p+len;
1946        p += OFF;
1947        for (int i=0; i<32 && p < end; i++) {
1948            printf("0x%03x ", i*0x10 + OFF);
1949            for (int j=0; j<0x10 && p < end; j++) {
1950                printf(" %02x", *p);
1951                p++;
1952            }
1953            printf("\n");
1954        }
1955#endif
1956
1957        free(data);
1958
1959        count++;
1960    }
1961
1962bail:
1963    delete zip;
1964    return count;
1965}
1966
1967sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1968{
1969    sp<AaptSymbols> sym = mSymbols.valueFor(name);
1970    if (sym == NULL) {
1971        sym = new AaptSymbols();
1972        mSymbols.add(name, sym);
1973    }
1974    return sym;
1975}
1976
1977status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1978{
1979    if (!mHaveIncludedAssets) {
1980        // Add in all includes.
1981        const Vector<const char*>& incl = bundle->getPackageIncludes();
1982        const size_t N=incl.size();
1983        for (size_t i=0; i<N; i++) {
1984            if (bundle->getVerbose())
1985                printf("Including resources from package: %s\n", incl[i]);
1986            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
1987                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1988                        incl[i]);
1989                return UNKNOWN_ERROR;
1990            }
1991        }
1992        mHaveIncludedAssets = true;
1993    }
1994
1995    return NO_ERROR;
1996}
1997
1998status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1999{
2000    const ResTable& res = getIncludedResources();
2001    // XXX dirty!
2002    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2003}
2004
2005const ResTable& AaptAssets::getIncludedResources() const
2006{
2007    return mIncludedAssets.getResources(false);
2008}
2009
2010void AaptAssets::print() const
2011{
2012    printf("Locale/Vendor pairs:\n");
2013    const size_t N=mGroupEntries.size();
2014    for (size_t i=0; i<N; i++) {
2015        printf("   %s/%s\n",
2016               mGroupEntries.itemAt(i).locale.string(),
2017               mGroupEntries.itemAt(i).vendor.string());
2018    }
2019
2020    printf("\nFiles:\n");
2021    AaptDir::print();
2022}
2023
2024sp<AaptDir> AaptAssets::resDir(const String8& name)
2025{
2026    const Vector<sp<AaptDir> >& dirs = mDirs;
2027    const size_t N = dirs.size();
2028    for (size_t i=0; i<N; i++) {
2029        const sp<AaptDir>& d = dirs.itemAt(i);
2030        if (d->getLeaf() == name) {
2031            return d;
2032        }
2033    }
2034    return NULL;
2035}
2036
2037bool
2038valid_symbol_name(const String8& symbol)
2039{
2040    static char const * const KEYWORDS[] = {
2041        "abstract", "assert", "boolean", "break",
2042        "byte", "case", "catch", "char", "class", "const", "continue",
2043        "default", "do", "double", "else", "enum", "extends", "final",
2044        "finally", "float", "for", "goto", "if", "implements", "import",
2045        "instanceof", "int", "interface", "long", "native", "new", "package",
2046        "private", "protected", "public", "return", "short", "static",
2047        "strictfp", "super", "switch", "synchronized", "this", "throw",
2048        "throws", "transient", "try", "void", "volatile", "while",
2049        "true", "false", "null",
2050        NULL
2051    };
2052    const char*const* k = KEYWORDS;
2053    const char*const s = symbol.string();
2054    while (*k) {
2055        if (0 == strcmp(s, *k)) {
2056            return false;
2057        }
2058        k++;
2059    }
2060    return true;
2061}
2062