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