]> git.saurik.com Git - android/aapt.git/blob - AaptAssets.cpp
Add support for "-rtl" in resources
[android/aapt.git] / AaptAssets.cpp
1 //
2 // Copyright 2006 The Android Open Source Project
3 //
4
5 #include "AaptAssets.h"
6 #include "ResourceFilter.h"
7 #include "Main.h"
8
9 #include <utils/misc.h>
10 #include <utils/SortedVector.h>
11
12 #include <ctype.h>
13 #include <dirent.h>
14 #include <errno.h>
15
16 static const char* kDefaultLocale = "default";
17 static const char* kWildcardName = "any";
18 static const char* kAssetDir = "assets";
19 static const char* kResourceDir = "res";
20 static const char* kValuesDir = "values";
21 static const char* kMipmapDir = "mipmap";
22 static const char* kInvalidChars = "/\\:";
23 static const size_t kMaxAssetFileName = 100;
24
25 static const String8 kResString(kResourceDir);
26
27 /*
28 * Names of asset files must meet the following criteria:
29 *
30 * - the filename length must be less than kMaxAssetFileName bytes long
31 * (and can't be empty)
32 * - all characters must be 7-bit printable ASCII
33 * - none of { '/' '\\' ':' }
34 *
35 * Pass in just the filename, not the full path.
36 */
37 static bool validateFileName(const char* fileName)
38 {
39 const char* cp = fileName;
40 size_t len = 0;
41
42 while (*cp != '\0') {
43 if ((*cp & 0x80) != 0)
44 return false; // reject high ASCII
45 if (*cp < 0x20 || *cp >= 0x7f)
46 return false; // reject control chars and 0x7f
47 if (strchr(kInvalidChars, *cp) != NULL)
48 return false; // reject path sep chars
49 cp++;
50 len++;
51 }
52
53 if (len < 1 || len > kMaxAssetFileName)
54 return false; // reject empty or too long
55
56 return true;
57 }
58
59 // The default to use if no other ignore pattern is defined.
60 const char * const gDefaultIgnoreAssets =
61 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
62 // The ignore pattern that can be passed via --ignore-assets in Main.cpp
63 const char * gUserIgnoreAssets = NULL;
64
65 static bool isHidden(const char *root, const char *path)
66 {
67 // Patterns syntax:
68 // - Delimiter is :
69 // - Entry can start with the flag ! to avoid printing a warning
70 // about the file being ignored.
71 // - Entry can have the flag "<dir>" to match only directories
72 // or <file> to match only files. Default is to match both.
73 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
74 // where prefix/suffix must have at least 1 character (so that
75 // we don't match a '*' catch-all pattern.)
76 // - The special filenames "." and ".." are always ignored.
77 // - Otherwise the full string is matched.
78 // - match is not case-sensitive.
79
80 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
81 return true;
82 }
83
84 const char *delim = ":";
85 const char *p = gUserIgnoreAssets;
86 if (!p || !p[0]) {
87 p = getenv("ANDROID_AAPT_IGNORE");
88 }
89 if (!p || !p[0]) {
90 p = gDefaultIgnoreAssets;
91 }
92 char *patterns = strdup(p);
93
94 bool ignore = false;
95 bool chatty = true;
96 char *matchedPattern = NULL;
97
98 String8 fullPath(root);
99 fullPath.appendPath(path);
100 FileType type = getFileType(fullPath);
101
102 int plen = strlen(path);
103
104 // Note: we don't have strtok_r under mingw.
105 for(char *token = strtok(patterns, delim);
106 !ignore && token != NULL;
107 token = strtok(NULL, delim)) {
108 chatty = token[0] != '!';
109 if (!chatty) token++; // skip !
110 if (strncasecmp(token, "<dir>" , 5) == 0) {
111 if (type != kFileTypeDirectory) continue;
112 token += 5;
113 }
114 if (strncasecmp(token, "<file>", 6) == 0) {
115 if (type != kFileTypeRegular) continue;
116 token += 6;
117 }
118
119 matchedPattern = token;
120 int n = strlen(token);
121
122 if (token[0] == '*') {
123 // Match *suffix
124 token++;
125 n--;
126 if (n <= plen) {
127 ignore = strncasecmp(token, path + plen - n, n) == 0;
128 }
129 } else if (n > 1 && token[n - 1] == '*') {
130 // Match prefix*
131 ignore = strncasecmp(token, path, n - 1) == 0;
132 } else {
133 ignore = strcasecmp(token, path) == 0;
134 }
135 }
136
137 if (ignore && chatty) {
138 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
139 type == kFileTypeDirectory ? "dir" : "file",
140 path,
141 matchedPattern ? matchedPattern : "");
142 }
143
144 free(patterns);
145 return ignore;
146 }
147
148 // =========================================================================
149 // =========================================================================
150 // =========================================================================
151
152 status_t
153 AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
154 {
155 ResTable_config config;
156
157 // IMSI - MCC
158 if (getMccName(part.string(), &config)) {
159 *axis = AXIS_MCC;
160 *value = config.mcc;
161 return 0;
162 }
163
164 // IMSI - MNC
165 if (getMncName(part.string(), &config)) {
166 *axis = AXIS_MNC;
167 *value = config.mnc;
168 return 0;
169 }
170
171 // locale - language
172 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
173 *axis = AXIS_LANGUAGE;
174 *value = part[1] << 8 | part[0];
175 return 0;
176 }
177
178 // locale - language_REGION
179 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
180 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
181 *axis = AXIS_LANGUAGE;
182 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
183 return 0;
184 }
185
186 // layout direction
187 if (getLayoutDirectionName(part.string(), &config)) {
188 *axis = AXIS_LAYOUTDIR;
189 *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
190 return 0;
191 }
192
193 // smallest screen dp width
194 if (getSmallestScreenWidthDpName(part.string(), &config)) {
195 *axis = AXIS_SMALLESTSCREENWIDTHDP;
196 *value = config.smallestScreenWidthDp;
197 return 0;
198 }
199
200 // screen dp width
201 if (getScreenWidthDpName(part.string(), &config)) {
202 *axis = AXIS_SCREENWIDTHDP;
203 *value = config.screenWidthDp;
204 return 0;
205 }
206
207 // screen dp height
208 if (getScreenHeightDpName(part.string(), &config)) {
209 *axis = AXIS_SCREENHEIGHTDP;
210 *value = config.screenHeightDp;
211 return 0;
212 }
213
214 // screen layout size
215 if (getScreenLayoutSizeName(part.string(), &config)) {
216 *axis = AXIS_SCREENLAYOUTSIZE;
217 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
218 return 0;
219 }
220
221 // screen layout long
222 if (getScreenLayoutLongName(part.string(), &config)) {
223 *axis = AXIS_SCREENLAYOUTLONG;
224 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
225 return 0;
226 }
227
228 // orientation
229 if (getOrientationName(part.string(), &config)) {
230 *axis = AXIS_ORIENTATION;
231 *value = config.orientation;
232 return 0;
233 }
234
235 // ui mode type
236 if (getUiModeTypeName(part.string(), &config)) {
237 *axis = AXIS_UIMODETYPE;
238 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
239 return 0;
240 }
241
242 // ui mode night
243 if (getUiModeNightName(part.string(), &config)) {
244 *axis = AXIS_UIMODENIGHT;
245 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
246 return 0;
247 }
248
249 // density
250 if (getDensityName(part.string(), &config)) {
251 *axis = AXIS_DENSITY;
252 *value = config.density;
253 return 0;
254 }
255
256 // touchscreen
257 if (getTouchscreenName(part.string(), &config)) {
258 *axis = AXIS_TOUCHSCREEN;
259 *value = config.touchscreen;
260 return 0;
261 }
262
263 // keyboard hidden
264 if (getKeysHiddenName(part.string(), &config)) {
265 *axis = AXIS_KEYSHIDDEN;
266 *value = config.inputFlags;
267 return 0;
268 }
269
270 // keyboard
271 if (getKeyboardName(part.string(), &config)) {
272 *axis = AXIS_KEYBOARD;
273 *value = config.keyboard;
274 return 0;
275 }
276
277 // navigation hidden
278 if (getNavHiddenName(part.string(), &config)) {
279 *axis = AXIS_NAVHIDDEN;
280 *value = config.inputFlags;
281 return 0;
282 }
283
284 // navigation
285 if (getNavigationName(part.string(), &config)) {
286 *axis = AXIS_NAVIGATION;
287 *value = config.navigation;
288 return 0;
289 }
290
291 // screen size
292 if (getScreenSizeName(part.string(), &config)) {
293 *axis = AXIS_SCREENSIZE;
294 *value = config.screenSize;
295 return 0;
296 }
297
298 // version
299 if (getVersionName(part.string(), &config)) {
300 *axis = AXIS_VERSION;
301 *value = config.version;
302 return 0;
303 }
304
305 return 1;
306 }
307
308 uint32_t
309 AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
310 {
311 switch (axis) {
312 case AXIS_MCC:
313 return config.mcc;
314 case AXIS_MNC:
315 return config.mnc;
316 case AXIS_LANGUAGE:
317 return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
318 | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
319 case AXIS_LAYOUTDIR:
320 return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
321 case AXIS_SCREENLAYOUTSIZE:
322 return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
323 case AXIS_ORIENTATION:
324 return config.orientation;
325 case AXIS_UIMODETYPE:
326 return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
327 case AXIS_UIMODENIGHT:
328 return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
329 case AXIS_DENSITY:
330 return config.density;
331 case AXIS_TOUCHSCREEN:
332 return config.touchscreen;
333 case AXIS_KEYSHIDDEN:
334 return config.inputFlags;
335 case AXIS_KEYBOARD:
336 return config.keyboard;
337 case AXIS_NAVIGATION:
338 return config.navigation;
339 case AXIS_SCREENSIZE:
340 return config.screenSize;
341 case AXIS_SMALLESTSCREENWIDTHDP:
342 return config.smallestScreenWidthDp;
343 case AXIS_SCREENWIDTHDP:
344 return config.screenWidthDp;
345 case AXIS_SCREENHEIGHTDP:
346 return config.screenHeightDp;
347 case AXIS_VERSION:
348 return config.version;
349 }
350 return 0;
351 }
352
353 bool
354 AaptGroupEntry::configSameExcept(const ResTable_config& config,
355 const ResTable_config& otherConfig, int axis)
356 {
357 for (int i=AXIS_START; i<=AXIS_END; i++) {
358 if (i == axis) {
359 continue;
360 }
361 if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
362 return false;
363 }
364 }
365 return true;
366 }
367
368 bool
369 AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
370 {
371 mParamsChanged = true;
372
373 Vector<String8> parts;
374
375 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
376 String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
377 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
378
379 const char *p = dir;
380 const char *q;
381 while (NULL != (q = strchr(p, '-'))) {
382 String8 val(p, q-p);
383 val.toLower();
384 parts.add(val);
385 //printf("part: %s\n", parts[parts.size()-1].string());
386 p = q+1;
387 }
388 String8 val(p);
389 val.toLower();
390 parts.add(val);
391 //printf("part: %s\n", parts[parts.size()-1].string());
392
393 const int N = parts.size();
394 int index = 0;
395 String8 part = parts[index];
396
397 // resource type
398 if (!isValidResourceType(part)) {
399 return false;
400 }
401 *resType = part;
402
403 index++;
404 if (index == N) {
405 goto success;
406 }
407 part = parts[index];
408
409 // imsi - mcc
410 if (getMccName(part.string())) {
411 mcc = part;
412
413 index++;
414 if (index == N) {
415 goto success;
416 }
417 part = parts[index];
418 } else {
419 //printf("not mcc: %s\n", part.string());
420 }
421
422 // imsi - mnc
423 if (getMncName(part.string())) {
424 mnc = part;
425
426 index++;
427 if (index == N) {
428 goto success;
429 }
430 part = parts[index];
431 } else {
432 //printf("not mcc: %s\n", part.string());
433 }
434
435 // locale - language
436 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
437 loc = part;
438
439 index++;
440 if (index == N) {
441 goto success;
442 }
443 part = parts[index];
444 } else {
445 //printf("not language: %s\n", part.string());
446 }
447
448 // locale - region
449 if (loc.length() > 0
450 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
451 loc += "-";
452 part.toUpper();
453 loc += part.string() + 1;
454
455 index++;
456 if (index == N) {
457 goto success;
458 }
459 part = parts[index];
460 } else {
461 //printf("not region: %s\n", part.string());
462 }
463
464 if (getLayoutDirectionName(part.string())) {
465 layoutDir = part;
466
467 index++;
468 if (index == N) {
469 goto success;
470 }
471 part = parts[index];
472 } else {
473 //printf("not layout direction: %s\n", part.string());
474 }
475
476 if (getSmallestScreenWidthDpName(part.string())) {
477 smallestwidthdp = part;
478
479 index++;
480 if (index == N) {
481 goto success;
482 }
483 part = parts[index];
484 } else {
485 //printf("not smallest screen width dp: %s\n", part.string());
486 }
487
488 if (getScreenWidthDpName(part.string())) {
489 widthdp = part;
490
491 index++;
492 if (index == N) {
493 goto success;
494 }
495 part = parts[index];
496 } else {
497 //printf("not screen width dp: %s\n", part.string());
498 }
499
500 if (getScreenHeightDpName(part.string())) {
501 heightdp = part;
502
503 index++;
504 if (index == N) {
505 goto success;
506 }
507 part = parts[index];
508 } else {
509 //printf("not screen height dp: %s\n", part.string());
510 }
511
512 if (getScreenLayoutSizeName(part.string())) {
513 layoutsize = part;
514
515 index++;
516 if (index == N) {
517 goto success;
518 }
519 part = parts[index];
520 } else {
521 //printf("not screen layout size: %s\n", part.string());
522 }
523
524 if (getScreenLayoutLongName(part.string())) {
525 layoutlong = part;
526
527 index++;
528 if (index == N) {
529 goto success;
530 }
531 part = parts[index];
532 } else {
533 //printf("not screen layout long: %s\n", part.string());
534 }
535
536 // orientation
537 if (getOrientationName(part.string())) {
538 orient = part;
539
540 index++;
541 if (index == N) {
542 goto success;
543 }
544 part = parts[index];
545 } else {
546 //printf("not orientation: %s\n", part.string());
547 }
548
549 // ui mode type
550 if (getUiModeTypeName(part.string())) {
551 uiModeType = part;
552
553 index++;
554 if (index == N) {
555 goto success;
556 }
557 part = parts[index];
558 } else {
559 //printf("not ui mode type: %s\n", part.string());
560 }
561
562 // ui mode night
563 if (getUiModeNightName(part.string())) {
564 uiModeNight = part;
565
566 index++;
567 if (index == N) {
568 goto success;
569 }
570 part = parts[index];
571 } else {
572 //printf("not ui mode night: %s\n", part.string());
573 }
574
575 // density
576 if (getDensityName(part.string())) {
577 den = part;
578
579 index++;
580 if (index == N) {
581 goto success;
582 }
583 part = parts[index];
584 } else {
585 //printf("not density: %s\n", part.string());
586 }
587
588 // touchscreen
589 if (getTouchscreenName(part.string())) {
590 touch = part;
591
592 index++;
593 if (index == N) {
594 goto success;
595 }
596 part = parts[index];
597 } else {
598 //printf("not touchscreen: %s\n", part.string());
599 }
600
601 // keyboard hidden
602 if (getKeysHiddenName(part.string())) {
603 keysHidden = part;
604
605 index++;
606 if (index == N) {
607 goto success;
608 }
609 part = parts[index];
610 } else {
611 //printf("not keysHidden: %s\n", part.string());
612 }
613
614 // keyboard
615 if (getKeyboardName(part.string())) {
616 key = part;
617
618 index++;
619 if (index == N) {
620 goto success;
621 }
622 part = parts[index];
623 } else {
624 //printf("not keyboard: %s\n", part.string());
625 }
626
627 // navigation hidden
628 if (getNavHiddenName(part.string())) {
629 navHidden = part;
630
631 index++;
632 if (index == N) {
633 goto success;
634 }
635 part = parts[index];
636 } else {
637 //printf("not navHidden: %s\n", part.string());
638 }
639
640 if (getNavigationName(part.string())) {
641 nav = part;
642
643 index++;
644 if (index == N) {
645 goto success;
646 }
647 part = parts[index];
648 } else {
649 //printf("not navigation: %s\n", part.string());
650 }
651
652 if (getScreenSizeName(part.string())) {
653 size = part;
654
655 index++;
656 if (index == N) {
657 goto success;
658 }
659 part = parts[index];
660 } else {
661 //printf("not screen size: %s\n", part.string());
662 }
663
664 if (getVersionName(part.string())) {
665 vers = part;
666
667 index++;
668 if (index == N) {
669 goto success;
670 }
671 part = parts[index];
672 } else {
673 //printf("not version: %s\n", part.string());
674 }
675
676 // if there are extra parts, it doesn't match
677 return false;
678
679 success:
680 this->mcc = mcc;
681 this->mnc = mnc;
682 this->locale = loc;
683 this->screenLayoutSize = layoutsize;
684 this->screenLayoutLong = layoutlong;
685 this->smallestScreenWidthDp = smallestwidthdp;
686 this->screenWidthDp = widthdp;
687 this->screenHeightDp = heightdp;
688 this->orientation = orient;
689 this->uiModeType = uiModeType;
690 this->uiModeNight = uiModeNight;
691 this->density = den;
692 this->touchscreen = touch;
693 this->keysHidden = keysHidden;
694 this->keyboard = key;
695 this->navHidden = navHidden;
696 this->navigation = nav;
697 this->screenSize = size;
698 this->layoutDirection = layoutDir;
699 this->version = vers;
700
701 // what is this anyway?
702 this->vendor = "";
703
704 return true;
705 }
706
707 String8
708 AaptGroupEntry::toString() const
709 {
710 String8 s = this->mcc;
711 s += ",";
712 s += this->mnc;
713 s += ",";
714 s += this->locale;
715 s += ",";
716 s += layoutDirection;
717 s += ",";
718 s += smallestScreenWidthDp;
719 s += ",";
720 s += screenWidthDp;
721 s += ",";
722 s += screenHeightDp;
723 s += ",";
724 s += screenLayoutSize;
725 s += ",";
726 s += screenLayoutLong;
727 s += ",";
728 s += this->orientation;
729 s += ",";
730 s += uiModeType;
731 s += ",";
732 s += uiModeNight;
733 s += ",";
734 s += density;
735 s += ",";
736 s += touchscreen;
737 s += ",";
738 s += keysHidden;
739 s += ",";
740 s += keyboard;
741 s += ",";
742 s += navHidden;
743 s += ",";
744 s += navigation;
745 s += ",";
746 s += screenSize;
747 s += ",";
748 s += version;
749 return s;
750 }
751
752 String8
753 AaptGroupEntry::toDirName(const String8& resType) const
754 {
755 String8 s = resType;
756 if (this->mcc != "") {
757 if (s.length() > 0) {
758 s += "-";
759 }
760 s += mcc;
761 }
762 if (this->mnc != "") {
763 if (s.length() > 0) {
764 s += "-";
765 }
766 s += mnc;
767 }
768 if (this->locale != "") {
769 if (s.length() > 0) {
770 s += "-";
771 }
772 s += locale;
773 }
774 if (this->layoutDirection != "") {
775 if (s.length() > 0) {
776 s += "-";
777 }
778 s += layoutDirection;
779 }
780 if (this->smallestScreenWidthDp != "") {
781 if (s.length() > 0) {
782 s += "-";
783 }
784 s += smallestScreenWidthDp;
785 }
786 if (this->screenWidthDp != "") {
787 if (s.length() > 0) {
788 s += "-";
789 }
790 s += screenWidthDp;
791 }
792 if (this->screenHeightDp != "") {
793 if (s.length() > 0) {
794 s += "-";
795 }
796 s += screenHeightDp;
797 }
798 if (this->screenLayoutSize != "") {
799 if (s.length() > 0) {
800 s += "-";
801 }
802 s += screenLayoutSize;
803 }
804 if (this->screenLayoutLong != "") {
805 if (s.length() > 0) {
806 s += "-";
807 }
808 s += screenLayoutLong;
809 }
810 if (this->orientation != "") {
811 if (s.length() > 0) {
812 s += "-";
813 }
814 s += orientation;
815 }
816 if (this->uiModeType != "") {
817 if (s.length() > 0) {
818 s += "-";
819 }
820 s += uiModeType;
821 }
822 if (this->uiModeNight != "") {
823 if (s.length() > 0) {
824 s += "-";
825 }
826 s += uiModeNight;
827 }
828 if (this->density != "") {
829 if (s.length() > 0) {
830 s += "-";
831 }
832 s += density;
833 }
834 if (this->touchscreen != "") {
835 if (s.length() > 0) {
836 s += "-";
837 }
838 s += touchscreen;
839 }
840 if (this->keysHidden != "") {
841 if (s.length() > 0) {
842 s += "-";
843 }
844 s += keysHidden;
845 }
846 if (this->keyboard != "") {
847 if (s.length() > 0) {
848 s += "-";
849 }
850 s += keyboard;
851 }
852 if (this->navHidden != "") {
853 if (s.length() > 0) {
854 s += "-";
855 }
856 s += navHidden;
857 }
858 if (this->navigation != "") {
859 if (s.length() > 0) {
860 s += "-";
861 }
862 s += navigation;
863 }
864 if (this->screenSize != "") {
865 if (s.length() > 0) {
866 s += "-";
867 }
868 s += screenSize;
869 }
870 if (this->version != "") {
871 if (s.length() > 0) {
872 s += "-";
873 }
874 s += version;
875 }
876
877 return s;
878 }
879
880 bool AaptGroupEntry::getMccName(const char* name,
881 ResTable_config* out)
882 {
883 if (strcmp(name, kWildcardName) == 0) {
884 if (out) out->mcc = 0;
885 return true;
886 }
887 const char* c = name;
888 if (tolower(*c) != 'm') return false;
889 c++;
890 if (tolower(*c) != 'c') return false;
891 c++;
892 if (tolower(*c) != 'c') return false;
893 c++;
894
895 const char* val = c;
896
897 while (*c >= '0' && *c <= '9') {
898 c++;
899 }
900 if (*c != 0) return false;
901 if (c-val != 3) return false;
902
903 int d = atoi(val);
904 if (d != 0) {
905 if (out) out->mcc = d;
906 return true;
907 }
908
909 return false;
910 }
911
912 bool AaptGroupEntry::getMncName(const char* name,
913 ResTable_config* out)
914 {
915 if (strcmp(name, kWildcardName) == 0) {
916 if (out) out->mcc = 0;
917 return true;
918 }
919 const char* c = name;
920 if (tolower(*c) != 'm') return false;
921 c++;
922 if (tolower(*c) != 'n') return false;
923 c++;
924 if (tolower(*c) != 'c') return false;
925 c++;
926
927 const char* val = c;
928
929 while (*c >= '0' && *c <= '9') {
930 c++;
931 }
932 if (*c != 0) return false;
933 if (c-val == 0 || c-val > 3) return false;
934
935 if (out) {
936 out->mnc = atoi(val);
937 }
938
939 return true;
940 }
941
942 /*
943 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
944 * "default")?
945 *
946 * TODO: Should insist that the first two letters are lower case, and the
947 * second two are upper.
948 */
949 bool AaptGroupEntry::getLocaleName(const char* fileName,
950 ResTable_config* out)
951 {
952 if (strcmp(fileName, kWildcardName) == 0
953 || strcmp(fileName, kDefaultLocale) == 0) {
954 if (out) {
955 out->language[0] = 0;
956 out->language[1] = 0;
957 out->country[0] = 0;
958 out->country[1] = 0;
959 }
960 return true;
961 }
962
963 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
964 if (out) {
965 out->language[0] = fileName[0];
966 out->language[1] = fileName[1];
967 out->country[0] = 0;
968 out->country[1] = 0;
969 }
970 return true;
971 }
972
973 if (strlen(fileName) == 5 &&
974 isalpha(fileName[0]) &&
975 isalpha(fileName[1]) &&
976 fileName[2] == '-' &&
977 isalpha(fileName[3]) &&
978 isalpha(fileName[4])) {
979 if (out) {
980 out->language[0] = fileName[0];
981 out->language[1] = fileName[1];
982 out->country[0] = fileName[3];
983 out->country[1] = fileName[4];
984 }
985 return true;
986 }
987
988 return false;
989 }
990
991 bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
992 {
993 if (strcmp(name, kWildcardName) == 0) {
994 if (out) out->screenLayout =
995 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
996 | ResTable_config::LAYOUTDIR_ANY;
997 return true;
998 } else if (strcmp(name, "ltr") == 0) {
999 if (out) out->screenLayout =
1000 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1001 | ResTable_config::LAYOUTDIR_LTR;
1002 return true;
1003 } else if (strcmp(name, "rtl") == 0) {
1004 if (out) out->screenLayout =
1005 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1006 | ResTable_config::LAYOUTDIR_RTL;
1007 return true;
1008 }
1009
1010 return false;
1011 }
1012
1013 bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
1014 ResTable_config* out)
1015 {
1016 if (strcmp(name, kWildcardName) == 0) {
1017 if (out) out->screenLayout =
1018 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1019 | ResTable_config::SCREENSIZE_ANY;
1020 return true;
1021 } else if (strcmp(name, "small") == 0) {
1022 if (out) out->screenLayout =
1023 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1024 | ResTable_config::SCREENSIZE_SMALL;
1025 return true;
1026 } else if (strcmp(name, "normal") == 0) {
1027 if (out) out->screenLayout =
1028 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1029 | ResTable_config::SCREENSIZE_NORMAL;
1030 return true;
1031 } else if (strcmp(name, "large") == 0) {
1032 if (out) out->screenLayout =
1033 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1034 | ResTable_config::SCREENSIZE_LARGE;
1035 return true;
1036 } else if (strcmp(name, "xlarge") == 0) {
1037 if (out) out->screenLayout =
1038 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1039 | ResTable_config::SCREENSIZE_XLARGE;
1040 return true;
1041 }
1042
1043 return false;
1044 }
1045
1046 bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
1047 ResTable_config* out)
1048 {
1049 if (strcmp(name, kWildcardName) == 0) {
1050 if (out) out->screenLayout =
1051 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1052 | ResTable_config::SCREENLONG_ANY;
1053 return true;
1054 } else if (strcmp(name, "long") == 0) {
1055 if (out) out->screenLayout =
1056 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1057 | ResTable_config::SCREENLONG_YES;
1058 return true;
1059 } else if (strcmp(name, "notlong") == 0) {
1060 if (out) out->screenLayout =
1061 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1062 | ResTable_config::SCREENLONG_NO;
1063 return true;
1064 }
1065
1066 return false;
1067 }
1068
1069 bool AaptGroupEntry::getOrientationName(const char* name,
1070 ResTable_config* out)
1071 {
1072 if (strcmp(name, kWildcardName) == 0) {
1073 if (out) out->orientation = out->ORIENTATION_ANY;
1074 return true;
1075 } else if (strcmp(name, "port") == 0) {
1076 if (out) out->orientation = out->ORIENTATION_PORT;
1077 return true;
1078 } else if (strcmp(name, "land") == 0) {
1079 if (out) out->orientation = out->ORIENTATION_LAND;
1080 return true;
1081 } else if (strcmp(name, "square") == 0) {
1082 if (out) out->orientation = out->ORIENTATION_SQUARE;
1083 return true;
1084 }
1085
1086 return false;
1087 }
1088
1089 bool AaptGroupEntry::getUiModeTypeName(const char* name,
1090 ResTable_config* out)
1091 {
1092 if (strcmp(name, kWildcardName) == 0) {
1093 if (out) out->uiMode =
1094 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1095 | ResTable_config::UI_MODE_TYPE_ANY;
1096 return true;
1097 } else if (strcmp(name, "desk") == 0) {
1098 if (out) out->uiMode =
1099 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1100 | ResTable_config::UI_MODE_TYPE_DESK;
1101 return true;
1102 } else if (strcmp(name, "car") == 0) {
1103 if (out) out->uiMode =
1104 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1105 | ResTable_config::UI_MODE_TYPE_CAR;
1106 return true;
1107 } else if (strcmp(name, "television") == 0) {
1108 if (out) out->uiMode =
1109 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1110 | ResTable_config::UI_MODE_TYPE_TELEVISION;
1111 return true;
1112 } else if (strcmp(name, "appliance") == 0) {
1113 if (out) out->uiMode =
1114 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1115 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1116 return true;
1117 }
1118
1119 return false;
1120 }
1121
1122 bool AaptGroupEntry::getUiModeNightName(const char* name,
1123 ResTable_config* out)
1124 {
1125 if (strcmp(name, kWildcardName) == 0) {
1126 if (out) out->uiMode =
1127 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1128 | ResTable_config::UI_MODE_NIGHT_ANY;
1129 return true;
1130 } else if (strcmp(name, "night") == 0) {
1131 if (out) out->uiMode =
1132 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1133 | ResTable_config::UI_MODE_NIGHT_YES;
1134 return true;
1135 } else if (strcmp(name, "notnight") == 0) {
1136 if (out) out->uiMode =
1137 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1138 | ResTable_config::UI_MODE_NIGHT_NO;
1139 return true;
1140 }
1141
1142 return false;
1143 }
1144
1145 bool AaptGroupEntry::getDensityName(const char* name,
1146 ResTable_config* out)
1147 {
1148 if (strcmp(name, kWildcardName) == 0) {
1149 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
1150 return true;
1151 }
1152
1153 if (strcmp(name, "nodpi") == 0) {
1154 if (out) out->density = ResTable_config::DENSITY_NONE;
1155 return true;
1156 }
1157
1158 if (strcmp(name, "ldpi") == 0) {
1159 if (out) out->density = ResTable_config::DENSITY_LOW;
1160 return true;
1161 }
1162
1163 if (strcmp(name, "mdpi") == 0) {
1164 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1165 return true;
1166 }
1167
1168 if (strcmp(name, "tvdpi") == 0) {
1169 if (out) out->density = ResTable_config::DENSITY_TV;
1170 return true;
1171 }
1172
1173 if (strcmp(name, "hdpi") == 0) {
1174 if (out) out->density = ResTable_config::DENSITY_HIGH;
1175 return true;
1176 }
1177
1178 if (strcmp(name, "xhdpi") == 0) {
1179 if (out) out->density = ResTable_config::DENSITY_XHIGH;
1180 return true;
1181 }
1182
1183 if (strcmp(name, "xxhdpi") == 0) {
1184 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1185 return true;
1186 }
1187
1188 char* c = (char*)name;
1189 while (*c >= '0' && *c <= '9') {
1190 c++;
1191 }
1192
1193 // check that we have 'dpi' after the last digit.
1194 if (toupper(c[0]) != 'D' ||
1195 toupper(c[1]) != 'P' ||
1196 toupper(c[2]) != 'I' ||
1197 c[3] != 0) {
1198 return false;
1199 }
1200
1201 // temporarily replace the first letter with \0 to
1202 // use atoi.
1203 char tmp = c[0];
1204 c[0] = '\0';
1205
1206 int d = atoi(name);
1207 c[0] = tmp;
1208
1209 if (d != 0) {
1210 if (out) out->density = d;
1211 return true;
1212 }
1213
1214 return false;
1215 }
1216
1217 bool AaptGroupEntry::getTouchscreenName(const char* name,
1218 ResTable_config* out)
1219 {
1220 if (strcmp(name, kWildcardName) == 0) {
1221 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1222 return true;
1223 } else if (strcmp(name, "notouch") == 0) {
1224 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1225 return true;
1226 } else if (strcmp(name, "stylus") == 0) {
1227 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1228 return true;
1229 } else if (strcmp(name, "finger") == 0) {
1230 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1231 return true;
1232 }
1233
1234 return false;
1235 }
1236
1237 bool AaptGroupEntry::getKeysHiddenName(const char* name,
1238 ResTable_config* out)
1239 {
1240 uint8_t mask = 0;
1241 uint8_t value = 0;
1242 if (strcmp(name, kWildcardName) == 0) {
1243 mask = ResTable_config::MASK_KEYSHIDDEN;
1244 value = ResTable_config::KEYSHIDDEN_ANY;
1245 } else if (strcmp(name, "keysexposed") == 0) {
1246 mask = ResTable_config::MASK_KEYSHIDDEN;
1247 value = ResTable_config::KEYSHIDDEN_NO;
1248 } else if (strcmp(name, "keyshidden") == 0) {
1249 mask = ResTable_config::MASK_KEYSHIDDEN;
1250 value = ResTable_config::KEYSHIDDEN_YES;
1251 } else if (strcmp(name, "keyssoft") == 0) {
1252 mask = ResTable_config::MASK_KEYSHIDDEN;
1253 value = ResTable_config::KEYSHIDDEN_SOFT;
1254 }
1255
1256 if (mask != 0) {
1257 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1258 return true;
1259 }
1260
1261 return false;
1262 }
1263
1264 bool AaptGroupEntry::getKeyboardName(const char* name,
1265 ResTable_config* out)
1266 {
1267 if (strcmp(name, kWildcardName) == 0) {
1268 if (out) out->keyboard = out->KEYBOARD_ANY;
1269 return true;
1270 } else if (strcmp(name, "nokeys") == 0) {
1271 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1272 return true;
1273 } else if (strcmp(name, "qwerty") == 0) {
1274 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1275 return true;
1276 } else if (strcmp(name, "12key") == 0) {
1277 if (out) out->keyboard = out->KEYBOARD_12KEY;
1278 return true;
1279 }
1280
1281 return false;
1282 }
1283
1284 bool AaptGroupEntry::getNavHiddenName(const char* name,
1285 ResTable_config* out)
1286 {
1287 uint8_t mask = 0;
1288 uint8_t value = 0;
1289 if (strcmp(name, kWildcardName) == 0) {
1290 mask = ResTable_config::MASK_NAVHIDDEN;
1291 value = ResTable_config::NAVHIDDEN_ANY;
1292 } else if (strcmp(name, "navexposed") == 0) {
1293 mask = ResTable_config::MASK_NAVHIDDEN;
1294 value = ResTable_config::NAVHIDDEN_NO;
1295 } else if (strcmp(name, "navhidden") == 0) {
1296 mask = ResTable_config::MASK_NAVHIDDEN;
1297 value = ResTable_config::NAVHIDDEN_YES;
1298 }
1299
1300 if (mask != 0) {
1301 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1302 return true;
1303 }
1304
1305 return false;
1306 }
1307
1308 bool AaptGroupEntry::getNavigationName(const char* name,
1309 ResTable_config* out)
1310 {
1311 if (strcmp(name, kWildcardName) == 0) {
1312 if (out) out->navigation = out->NAVIGATION_ANY;
1313 return true;
1314 } else if (strcmp(name, "nonav") == 0) {
1315 if (out) out->navigation = out->NAVIGATION_NONAV;
1316 return true;
1317 } else if (strcmp(name, "dpad") == 0) {
1318 if (out) out->navigation = out->NAVIGATION_DPAD;
1319 return true;
1320 } else if (strcmp(name, "trackball") == 0) {
1321 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1322 return true;
1323 } else if (strcmp(name, "wheel") == 0) {
1324 if (out) out->navigation = out->NAVIGATION_WHEEL;
1325 return true;
1326 }
1327
1328 return false;
1329 }
1330
1331 bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
1332 {
1333 if (strcmp(name, kWildcardName) == 0) {
1334 if (out) {
1335 out->screenWidth = out->SCREENWIDTH_ANY;
1336 out->screenHeight = out->SCREENHEIGHT_ANY;
1337 }
1338 return true;
1339 }
1340
1341 const char* x = name;
1342 while (*x >= '0' && *x <= '9') x++;
1343 if (x == name || *x != 'x') return false;
1344 String8 xName(name, x-name);
1345 x++;
1346
1347 const char* y = x;
1348 while (*y >= '0' && *y <= '9') y++;
1349 if (y == name || *y != 0) return false;
1350 String8 yName(x, y-x);
1351
1352 uint16_t w = (uint16_t)atoi(xName.string());
1353 uint16_t h = (uint16_t)atoi(yName.string());
1354 if (w < h) {
1355 return false;
1356 }
1357
1358 if (out) {
1359 out->screenWidth = w;
1360 out->screenHeight = h;
1361 }
1362
1363 return true;
1364 }
1365
1366 bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1367 {
1368 if (strcmp(name, kWildcardName) == 0) {
1369 if (out) {
1370 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1371 }
1372 return true;
1373 }
1374
1375 if (*name != 's') return false;
1376 name++;
1377 if (*name != 'w') return false;
1378 name++;
1379 const char* x = name;
1380 while (*x >= '0' && *x <= '9') x++;
1381 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1382 String8 xName(name, x-name);
1383
1384 if (out) {
1385 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1386 }
1387
1388 return true;
1389 }
1390
1391 bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1392 {
1393 if (strcmp(name, kWildcardName) == 0) {
1394 if (out) {
1395 out->screenWidthDp = out->SCREENWIDTH_ANY;
1396 }
1397 return true;
1398 }
1399
1400 if (*name != 'w') return false;
1401 name++;
1402 const char* x = name;
1403 while (*x >= '0' && *x <= '9') x++;
1404 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1405 String8 xName(name, x-name);
1406
1407 if (out) {
1408 out->screenWidthDp = (uint16_t)atoi(xName.string());
1409 }
1410
1411 return true;
1412 }
1413
1414 bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1415 {
1416 if (strcmp(name, kWildcardName) == 0) {
1417 if (out) {
1418 out->screenHeightDp = out->SCREENWIDTH_ANY;
1419 }
1420 return true;
1421 }
1422
1423 if (*name != 'h') return false;
1424 name++;
1425 const char* x = name;
1426 while (*x >= '0' && *x <= '9') x++;
1427 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1428 String8 xName(name, x-name);
1429
1430 if (out) {
1431 out->screenHeightDp = (uint16_t)atoi(xName.string());
1432 }
1433
1434 return true;
1435 }
1436
1437 bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
1438 {
1439 if (strcmp(name, kWildcardName) == 0) {
1440 if (out) {
1441 out->sdkVersion = out->SDKVERSION_ANY;
1442 out->minorVersion = out->MINORVERSION_ANY;
1443 }
1444 return true;
1445 }
1446
1447 if (*name != 'v') {
1448 return false;
1449 }
1450
1451 name++;
1452 const char* s = name;
1453 while (*s >= '0' && *s <= '9') s++;
1454 if (s == name || *s != 0) return false;
1455 String8 sdkName(name, s-name);
1456
1457 if (out) {
1458 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1459 out->minorVersion = 0;
1460 }
1461
1462 return true;
1463 }
1464
1465 int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1466 {
1467 int v = mcc.compare(o.mcc);
1468 if (v == 0) v = mnc.compare(o.mnc);
1469 if (v == 0) v = locale.compare(o.locale);
1470 if (v == 0) v = layoutDirection.compare(o.layoutDirection);
1471 if (v == 0) v = vendor.compare(o.vendor);
1472 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
1473 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1474 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
1475 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1476 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1477 if (v == 0) v = orientation.compare(o.orientation);
1478 if (v == 0) v = uiModeType.compare(o.uiModeType);
1479 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1480 if (v == 0) v = density.compare(o.density);
1481 if (v == 0) v = touchscreen.compare(o.touchscreen);
1482 if (v == 0) v = keysHidden.compare(o.keysHidden);
1483 if (v == 0) v = keyboard.compare(o.keyboard);
1484 if (v == 0) v = navHidden.compare(o.navHidden);
1485 if (v == 0) v = navigation.compare(o.navigation);
1486 if (v == 0) v = screenSize.compare(o.screenSize);
1487 if (v == 0) v = version.compare(o.version);
1488 return v;
1489 }
1490
1491 const ResTable_config& AaptGroupEntry::toParams() const
1492 {
1493 if (!mParamsChanged) {
1494 return mParams;
1495 }
1496
1497 mParamsChanged = false;
1498 ResTable_config& params(mParams);
1499 memset(&params, 0, sizeof(params));
1500 getMccName(mcc.string(), &params);
1501 getMncName(mnc.string(), &params);
1502 getLocaleName(locale.string(), &params);
1503 getLayoutDirectionName(layoutDirection.string(), &params);
1504 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
1505 getScreenWidthDpName(screenWidthDp.string(), &params);
1506 getScreenHeightDpName(screenHeightDp.string(), &params);
1507 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1508 getScreenLayoutLongName(screenLayoutLong.string(), &params);
1509 getOrientationName(orientation.string(), &params);
1510 getUiModeTypeName(uiModeType.string(), &params);
1511 getUiModeNightName(uiModeNight.string(), &params);
1512 getDensityName(density.string(), &params);
1513 getTouchscreenName(touchscreen.string(), &params);
1514 getKeysHiddenName(keysHidden.string(), &params);
1515 getKeyboardName(keyboard.string(), &params);
1516 getNavHiddenName(navHidden.string(), &params);
1517 getNavigationName(navigation.string(), &params);
1518 getScreenSizeName(screenSize.string(), &params);
1519 getVersionName(version.string(), &params);
1520
1521 // Fix up version number based on specified parameters.
1522 int minSdk = 0;
1523 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1524 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
1525 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
1526 minSdk = SDK_HONEYCOMB_MR2;
1527 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1528 != ResTable_config::UI_MODE_TYPE_ANY
1529 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1530 != ResTable_config::UI_MODE_NIGHT_ANY) {
1531 minSdk = SDK_FROYO;
1532 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1533 != ResTable_config::SCREENSIZE_ANY
1534 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1535 != ResTable_config::SCREENLONG_ANY
1536 || params.density != ResTable_config::DENSITY_DEFAULT) {
1537 minSdk = SDK_DONUT;
1538 }
1539
1540 if (minSdk > params.sdkVersion) {
1541 params.sdkVersion = minSdk;
1542 }
1543
1544 return params;
1545 }
1546
1547 // =========================================================================
1548 // =========================================================================
1549 // =========================================================================
1550
1551 void* AaptFile::editData(size_t size)
1552 {
1553 if (size <= mBufferSize) {
1554 mDataSize = size;
1555 return mData;
1556 }
1557 size_t allocSize = (size*3)/2;
1558 void* buf = realloc(mData, allocSize);
1559 if (buf == NULL) {
1560 return NULL;
1561 }
1562 mData = buf;
1563 mDataSize = size;
1564 mBufferSize = allocSize;
1565 return buf;
1566 }
1567
1568 void* AaptFile::editData(size_t* outSize)
1569 {
1570 if (outSize) {
1571 *outSize = mDataSize;
1572 }
1573 return mData;
1574 }
1575
1576 void* AaptFile::padData(size_t wordSize)
1577 {
1578 const size_t extra = mDataSize%wordSize;
1579 if (extra == 0) {
1580 return mData;
1581 }
1582
1583 size_t initial = mDataSize;
1584 void* data = editData(initial+(wordSize-extra));
1585 if (data != NULL) {
1586 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1587 }
1588 return data;
1589 }
1590
1591 status_t AaptFile::writeData(const void* data, size_t size)
1592 {
1593 size_t end = mDataSize;
1594 size_t total = size + end;
1595 void* buf = editData(total);
1596 if (buf == NULL) {
1597 return UNKNOWN_ERROR;
1598 }
1599 memcpy(((char*)buf)+end, data, size);
1600 return NO_ERROR;
1601 }
1602
1603 void AaptFile::clearData()
1604 {
1605 if (mData != NULL) free(mData);
1606 mData = NULL;
1607 mDataSize = 0;
1608 mBufferSize = 0;
1609 }
1610
1611 String8 AaptFile::getPrintableSource() const
1612 {
1613 if (hasData()) {
1614 String8 name(mGroupEntry.toDirName(String8()));
1615 name.appendPath(mPath);
1616 name.append(" #generated");
1617 return name;
1618 }
1619 return mSourceFile;
1620 }
1621
1622 // =========================================================================
1623 // =========================================================================
1624 // =========================================================================
1625
1626 status_t AaptGroup::addFile(const sp<AaptFile>& file)
1627 {
1628 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1629 file->mPath = mPath;
1630 mFiles.add(file->getGroupEntry(), file);
1631 return NO_ERROR;
1632 }
1633
1634 #if 0
1635 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1636 file->getSourceFile().string(),
1637 file->getGroupEntry().toDirName(String8()).string(),
1638 mLeaf.string(), mPath.string());
1639 #endif
1640
1641 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1642 getPrintableSource().string());
1643 return UNKNOWN_ERROR;
1644 }
1645
1646 void AaptGroup::removeFile(size_t index)
1647 {
1648 mFiles.removeItemsAt(index);
1649 }
1650
1651 void AaptGroup::print(const String8& prefix) const
1652 {
1653 printf("%s%s\n", prefix.string(), getPath().string());
1654 const size_t N=mFiles.size();
1655 size_t i;
1656 for (i=0; i<N; i++) {
1657 sp<AaptFile> file = mFiles.valueAt(i);
1658 const AaptGroupEntry& e = file->getGroupEntry();
1659 if (file->hasData()) {
1660 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
1661 (int)file->getSize());
1662 } else {
1663 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1664 file->getPrintableSource().string());
1665 }
1666 //printf("%s File Group Entry: %s\n", prefix.string(),
1667 // file->getGroupEntry().toDirName(String8()).string());
1668 }
1669 }
1670
1671 String8 AaptGroup::getPrintableSource() const
1672 {
1673 if (mFiles.size() > 0) {
1674 // Arbitrarily pull the first source file out of the list.
1675 return mFiles.valueAt(0)->getPrintableSource();
1676 }
1677
1678 // Should never hit this case, but to be safe...
1679 return getPath();
1680
1681 }
1682
1683 // =========================================================================
1684 // =========================================================================
1685 // =========================================================================
1686
1687 status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1688 {
1689 if (mFiles.indexOfKey(name) >= 0) {
1690 return ALREADY_EXISTS;
1691 }
1692 mFiles.add(name, file);
1693 return NO_ERROR;
1694 }
1695
1696 status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1697 {
1698 if (mDirs.indexOfKey(name) >= 0) {
1699 return ALREADY_EXISTS;
1700 }
1701 mDirs.add(name, dir);
1702 return NO_ERROR;
1703 }
1704
1705 sp<AaptDir> AaptDir::makeDir(const String8& path)
1706 {
1707 String8 name;
1708 String8 remain = path;
1709
1710 sp<AaptDir> subdir = this;
1711 while (name = remain.walkPath(&remain), remain != "") {
1712 subdir = subdir->makeDir(name);
1713 }
1714
1715 ssize_t i = subdir->mDirs.indexOfKey(name);
1716 if (i >= 0) {
1717 return subdir->mDirs.valueAt(i);
1718 }
1719 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1720 subdir->mDirs.add(name, dir);
1721 return dir;
1722 }
1723
1724 void AaptDir::removeFile(const String8& name)
1725 {
1726 mFiles.removeItem(name);
1727 }
1728
1729 void AaptDir::removeDir(const String8& name)
1730 {
1731 mDirs.removeItem(name);
1732 }
1733
1734 status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1735 {
1736 sp<AaptGroup> group;
1737 if (mFiles.indexOfKey(leafName) >= 0) {
1738 group = mFiles.valueFor(leafName);
1739 } else {
1740 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1741 mFiles.add(leafName, group);
1742 }
1743
1744 return group->addFile(file);
1745 }
1746
1747 ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1748 const AaptGroupEntry& kind, const String8& resType,
1749 sp<FilePathStore>& fullResPaths)
1750 {
1751 Vector<String8> fileNames;
1752 {
1753 DIR* dir = NULL;
1754
1755 dir = opendir(srcDir.string());
1756 if (dir == NULL) {
1757 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1758 return UNKNOWN_ERROR;
1759 }
1760
1761 /*
1762 * Slurp the filenames out of the directory.
1763 */
1764 while (1) {
1765 struct dirent* entry;
1766
1767 entry = readdir(dir);
1768 if (entry == NULL)
1769 break;
1770
1771 if (isHidden(srcDir.string(), entry->d_name))
1772 continue;
1773
1774 String8 name(entry->d_name);
1775 fileNames.add(name);
1776 // Add fully qualified path for dependency purposes
1777 // if we're collecting them
1778 if (fullResPaths != NULL) {
1779 fullResPaths->add(srcDir.appendPathCopy(name));
1780 }
1781 }
1782 closedir(dir);
1783 }
1784
1785 ssize_t count = 0;
1786
1787 /*
1788 * Stash away the files and recursively descend into subdirectories.
1789 */
1790 const size_t N = fileNames.size();
1791 size_t i;
1792 for (i = 0; i < N; i++) {
1793 String8 pathName(srcDir);
1794 FileType type;
1795
1796 pathName.appendPath(fileNames[i].string());
1797 type = getFileType(pathName.string());
1798 if (type == kFileTypeDirectory) {
1799 sp<AaptDir> subdir;
1800 bool notAdded = false;
1801 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1802 subdir = mDirs.valueFor(fileNames[i]);
1803 } else {
1804 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1805 notAdded = true;
1806 }
1807 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1808 resType, fullResPaths);
1809 if (res < NO_ERROR) {
1810 return res;
1811 }
1812 if (res > 0 && notAdded) {
1813 mDirs.add(fileNames[i], subdir);
1814 }
1815 count += res;
1816 } else if (type == kFileTypeRegular) {
1817 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1818 status_t err = addLeafFile(fileNames[i], file);
1819 if (err != NO_ERROR) {
1820 return err;
1821 }
1822
1823 count++;
1824
1825 } else {
1826 if (bundle->getVerbose())
1827 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1828 }
1829 }
1830
1831 return count;
1832 }
1833
1834 status_t AaptDir::validate() const
1835 {
1836 const size_t NF = mFiles.size();
1837 const size_t ND = mDirs.size();
1838 size_t i;
1839 for (i = 0; i < NF; i++) {
1840 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1841 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1842 "Invalid filename. Unable to add.");
1843 return UNKNOWN_ERROR;
1844 }
1845
1846 size_t j;
1847 for (j = i+1; j < NF; j++) {
1848 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1849 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1850 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1851 "File is case-insensitive equivalent to: %s",
1852 mFiles.valueAt(j)->getPrintableSource().string());
1853 return UNKNOWN_ERROR;
1854 }
1855
1856 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1857 // (this is mostly caught by the "marked" stuff, below)
1858 }
1859
1860 for (j = 0; j < ND; j++) {
1861 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1862 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1863 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1864 "File conflicts with dir from: %s",
1865 mDirs.valueAt(j)->getPrintableSource().string());
1866 return UNKNOWN_ERROR;
1867 }
1868 }
1869 }
1870
1871 for (i = 0; i < ND; i++) {
1872 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1873 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1874 "Invalid directory name, unable to add.");
1875 return UNKNOWN_ERROR;
1876 }
1877
1878 size_t j;
1879 for (j = i+1; j < ND; j++) {
1880 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1881 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1882 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1883 "Directory is case-insensitive equivalent to: %s",
1884 mDirs.valueAt(j)->getPrintableSource().string());
1885 return UNKNOWN_ERROR;
1886 }
1887 }
1888
1889 status_t err = mDirs.valueAt(i)->validate();
1890 if (err != NO_ERROR) {
1891 return err;
1892 }
1893 }
1894
1895 return NO_ERROR;
1896 }
1897
1898 void AaptDir::print(const String8& prefix) const
1899 {
1900 const size_t ND=getDirs().size();
1901 size_t i;
1902 for (i=0; i<ND; i++) {
1903 getDirs().valueAt(i)->print(prefix);
1904 }
1905
1906 const size_t NF=getFiles().size();
1907 for (i=0; i<NF; i++) {
1908 getFiles().valueAt(i)->print(prefix);
1909 }
1910 }
1911
1912 String8 AaptDir::getPrintableSource() const
1913 {
1914 if (mFiles.size() > 0) {
1915 // Arbitrarily pull the first file out of the list as the source dir.
1916 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1917 }
1918 if (mDirs.size() > 0) {
1919 // Or arbitrarily pull the first dir out of the list as the source dir.
1920 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1921 }
1922
1923 // Should never hit this case, but to be safe...
1924 return mPath;
1925
1926 }
1927
1928 // =========================================================================
1929 // =========================================================================
1930 // =========================================================================
1931
1932 status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
1933 {
1934 status_t err = NO_ERROR;
1935 size_t N = javaSymbols->mSymbols.size();
1936 for (size_t i=0; i<N; i++) {
1937 const String8& name = javaSymbols->mSymbols.keyAt(i);
1938 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
1939 ssize_t pos = mSymbols.indexOfKey(name);
1940 if (pos < 0) {
1941 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
1942 err = UNKNOWN_ERROR;
1943 continue;
1944 }
1945 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
1946 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
1947 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
1948 }
1949
1950 N = javaSymbols->mNestedSymbols.size();
1951 for (size_t i=0; i<N; i++) {
1952 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
1953 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
1954 ssize_t pos = mNestedSymbols.indexOfKey(name);
1955 if (pos < 0) {
1956 SourcePos pos;
1957 pos.error("Java symbol dir %s not defined\n", name.string());
1958 err = UNKNOWN_ERROR;
1959 continue;
1960 }
1961 //printf("**** applying java symbols in dir %s\n", name.string());
1962 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
1963 if (myerr != NO_ERROR) {
1964 err = myerr;
1965 }
1966 }
1967
1968 return err;
1969 }
1970
1971 // =========================================================================
1972 // =========================================================================
1973 // =========================================================================
1974
1975 AaptAssets::AaptAssets()
1976 : AaptDir(String8(), String8()),
1977 mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
1978 {
1979 }
1980
1981 const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
1982 if (mChanged) {
1983 }
1984 return mGroupEntries;
1985 }
1986
1987 status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
1988 {
1989 mChanged = true;
1990 return AaptDir::addFile(name, file);
1991 }
1992
1993 sp<AaptFile> AaptAssets::addFile(
1994 const String8& filePath, const AaptGroupEntry& entry,
1995 const String8& srcDir, sp<AaptGroup>* outGroup,
1996 const String8& resType)
1997 {
1998 sp<AaptDir> dir = this;
1999 sp<AaptGroup> group;
2000 sp<AaptFile> file;
2001 String8 root, remain(filePath), partialPath;
2002 while (remain.length() > 0) {
2003 root = remain.walkPath(&remain);
2004 partialPath.appendPath(root);
2005
2006 const String8 rootStr(root);
2007
2008 if (remain.length() == 0) {
2009 ssize_t i = dir->getFiles().indexOfKey(rootStr);
2010 if (i >= 0) {
2011 group = dir->getFiles().valueAt(i);
2012 } else {
2013 group = new AaptGroup(rootStr, filePath);
2014 status_t res = dir->addFile(rootStr, group);
2015 if (res != NO_ERROR) {
2016 return NULL;
2017 }
2018 }
2019 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2020 status_t res = group->addFile(file);
2021 if (res != NO_ERROR) {
2022 return NULL;
2023 }
2024 break;
2025
2026 } else {
2027 ssize_t i = dir->getDirs().indexOfKey(rootStr);
2028 if (i >= 0) {
2029 dir = dir->getDirs().valueAt(i);
2030 } else {
2031 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2032 status_t res = dir->addDir(rootStr, subdir);
2033 if (res != NO_ERROR) {
2034 return NULL;
2035 }
2036 dir = subdir;
2037 }
2038 }
2039 }
2040
2041 mGroupEntries.add(entry);
2042 if (outGroup) *outGroup = group;
2043 return file;
2044 }
2045
2046 void AaptAssets::addResource(const String8& leafName, const String8& path,
2047 const sp<AaptFile>& file, const String8& resType)
2048 {
2049 sp<AaptDir> res = AaptDir::makeDir(kResString);
2050 String8 dirname = file->getGroupEntry().toDirName(resType);
2051 sp<AaptDir> subdir = res->makeDir(dirname);
2052 sp<AaptGroup> grr = new AaptGroup(leafName, path);
2053 grr->addFile(file);
2054
2055 subdir->addFile(leafName, grr);
2056 }
2057
2058
2059 ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2060 {
2061 int count;
2062 int totalCount = 0;
2063 FileType type;
2064 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2065 const size_t dirCount =resDirs.size();
2066 sp<AaptAssets> current = this;
2067
2068 const int N = bundle->getFileSpecCount();
2069
2070 /*
2071 * If a package manifest was specified, include that first.
2072 */
2073 if (bundle->getAndroidManifestFile() != NULL) {
2074 // place at root of zip.
2075 String8 srcFile(bundle->getAndroidManifestFile());
2076 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2077 NULL, String8());
2078 totalCount++;
2079 }
2080
2081 /*
2082 * If a directory of custom assets was supplied, slurp 'em up.
2083 */
2084 if (bundle->getAssetSourceDir()) {
2085 const char* assetDir = bundle->getAssetSourceDir();
2086
2087 FileType type = getFileType(assetDir);
2088 if (type == kFileTypeNonexistent) {
2089 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
2090 return UNKNOWN_ERROR;
2091 }
2092 if (type != kFileTypeDirectory) {
2093 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2094 return UNKNOWN_ERROR;
2095 }
2096
2097 String8 assetRoot(assetDir);
2098 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2099 AaptGroupEntry group;
2100 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
2101 String8(), mFullAssetPaths);
2102 if (count < 0) {
2103 totalCount = count;
2104 goto bail;
2105 }
2106 if (count > 0) {
2107 mGroupEntries.add(group);
2108 }
2109 totalCount += count;
2110
2111 if (bundle->getVerbose())
2112 printf("Found %d custom asset file%s in %s\n",
2113 count, (count==1) ? "" : "s", assetDir);
2114 }
2115
2116 /*
2117 * If a directory of resource-specific assets was supplied, slurp 'em up.
2118 */
2119 for (size_t i=0; i<dirCount; i++) {
2120 const char *res = resDirs[i];
2121 if (res) {
2122 type = getFileType(res);
2123 if (type == kFileTypeNonexistent) {
2124 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2125 return UNKNOWN_ERROR;
2126 }
2127 if (type == kFileTypeDirectory) {
2128 if (i>0) {
2129 sp<AaptAssets> nextOverlay = new AaptAssets();
2130 current->setOverlay(nextOverlay);
2131 current = nextOverlay;
2132 current->setFullResPaths(mFullResPaths);
2133 }
2134 count = current->slurpResourceTree(bundle, String8(res));
2135
2136 if (count < 0) {
2137 totalCount = count;
2138 goto bail;
2139 }
2140 totalCount += count;
2141 }
2142 else {
2143 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2144 return UNKNOWN_ERROR;
2145 }
2146 }
2147
2148 }
2149 /*
2150 * Now do any additional raw files.
2151 */
2152 for (int arg=0; arg<N; arg++) {
2153 const char* assetDir = bundle->getFileSpecEntry(arg);
2154
2155 FileType type = getFileType(assetDir);
2156 if (type == kFileTypeNonexistent) {
2157 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2158 return UNKNOWN_ERROR;
2159 }
2160 if (type != kFileTypeDirectory) {
2161 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2162 return UNKNOWN_ERROR;
2163 }
2164
2165 String8 assetRoot(assetDir);
2166
2167 if (bundle->getVerbose())
2168 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2169
2170 /*
2171 * Do a recursive traversal of subdir tree. We don't make any
2172 * guarantees about ordering, so we're okay with an inorder search
2173 * using whatever order the OS happens to hand back to us.
2174 */
2175 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
2176 if (count < 0) {
2177 /* failure; report error and remove archive */
2178 totalCount = count;
2179 goto bail;
2180 }
2181 totalCount += count;
2182
2183 if (bundle->getVerbose())
2184 printf("Found %d asset file%s in %s\n",
2185 count, (count==1) ? "" : "s", assetDir);
2186 }
2187
2188 count = validate();
2189 if (count != NO_ERROR) {
2190 totalCount = count;
2191 goto bail;
2192 }
2193
2194 count = filter(bundle);
2195 if (count != NO_ERROR) {
2196 totalCount = count;
2197 goto bail;
2198 }
2199
2200 bail:
2201 return totalCount;
2202 }
2203
2204 ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2205 const AaptGroupEntry& kind,
2206 const String8& resType,
2207 sp<FilePathStore>& fullResPaths)
2208 {
2209 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
2210 if (res > 0) {
2211 mGroupEntries.add(kind);
2212 }
2213
2214 return res;
2215 }
2216
2217 ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2218 {
2219 ssize_t err = 0;
2220
2221 DIR* dir = opendir(srcDir.string());
2222 if (dir == NULL) {
2223 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2224 return UNKNOWN_ERROR;
2225 }
2226
2227 status_t count = 0;
2228
2229 /*
2230 * Run through the directory, looking for dirs that match the
2231 * expected pattern.
2232 */
2233 while (1) {
2234 struct dirent* entry = readdir(dir);
2235 if (entry == NULL) {
2236 break;
2237 }
2238
2239 if (isHidden(srcDir.string(), entry->d_name)) {
2240 continue;
2241 }
2242
2243 String8 subdirName(srcDir);
2244 subdirName.appendPath(entry->d_name);
2245
2246 AaptGroupEntry group;
2247 String8 resType;
2248 bool b = group.initFromDirName(entry->d_name, &resType);
2249 if (!b) {
2250 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2251 entry->d_name);
2252 err = -1;
2253 continue;
2254 }
2255
2256 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
2257 int maxResInt = atoi(bundle->getMaxResVersion());
2258 const char *verString = group.getVersionString().string();
2259 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2260 if (dirVersionInt > maxResInt) {
2261 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2262 continue;
2263 }
2264 }
2265
2266 FileType type = getFileType(subdirName.string());
2267
2268 if (type == kFileTypeDirectory) {
2269 sp<AaptDir> dir = makeDir(resType);
2270 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
2271 resType, mFullResPaths);
2272 if (res < 0) {
2273 count = res;
2274 goto bail;
2275 }
2276 if (res > 0) {
2277 mGroupEntries.add(group);
2278 count += res;
2279 }
2280
2281 // Only add this directory if we don't already have a resource dir
2282 // for the current type. This ensures that we only add the dir once
2283 // for all configs.
2284 sp<AaptDir> rdir = resDir(resType);
2285 if (rdir == NULL) {
2286 mResDirs.add(dir);
2287 }
2288 } else {
2289 if (bundle->getVerbose()) {
2290 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2291 }
2292 }
2293 }
2294
2295 bail:
2296 closedir(dir);
2297 dir = NULL;
2298
2299 if (err != 0) {
2300 return err;
2301 }
2302 return count;
2303 }
2304
2305 ssize_t
2306 AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2307 {
2308 int count = 0;
2309 SortedVector<AaptGroupEntry> entries;
2310
2311 ZipFile* zip = new ZipFile;
2312 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2313 if (err != NO_ERROR) {
2314 fprintf(stderr, "error opening zip file %s\n", filename);
2315 count = err;
2316 delete zip;
2317 return -1;
2318 }
2319
2320 const int N = zip->getNumEntries();
2321 for (int i=0; i<N; i++) {
2322 ZipEntry* entry = zip->getEntryByIndex(i);
2323 if (entry->getDeleted()) {
2324 continue;
2325 }
2326
2327 String8 entryName(entry->getFileName());
2328
2329 String8 dirName = entryName.getPathDir();
2330 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2331
2332 String8 resType;
2333 AaptGroupEntry kind;
2334
2335 String8 remain;
2336 if (entryName.walkPath(&remain) == kResourceDir) {
2337 // these are the resources, pull their type out of the directory name
2338 kind.initFromDirName(remain.walkPath().string(), &resType);
2339 } else {
2340 // these are untyped and don't have an AaptGroupEntry
2341 }
2342 if (entries.indexOf(kind) < 0) {
2343 entries.add(kind);
2344 mGroupEntries.add(kind);
2345 }
2346
2347 // use the one from the zip file if they both exist.
2348 dir->removeFile(entryName.getPathLeaf());
2349
2350 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2351 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2352 if (err != NO_ERROR) {
2353 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2354 count = err;
2355 goto bail;
2356 }
2357 file->setCompressionMethod(entry->getCompressionMethod());
2358
2359 #if 0
2360 if (entryName == "AndroidManifest.xml") {
2361 printf("AndroidManifest.xml\n");
2362 }
2363 printf("\n\nfile: %s\n", entryName.string());
2364 #endif
2365
2366 size_t len = entry->getUncompressedLen();
2367 void* data = zip->uncompress(entry);
2368 void* buf = file->editData(len);
2369 memcpy(buf, data, len);
2370
2371 #if 0
2372 const int OFF = 0;
2373 const unsigned char* p = (unsigned char*)data;
2374 const unsigned char* end = p+len;
2375 p += OFF;
2376 for (int i=0; i<32 && p < end; i++) {
2377 printf("0x%03x ", i*0x10 + OFF);
2378 for (int j=0; j<0x10 && p < end; j++) {
2379 printf(" %02x", *p);
2380 p++;
2381 }
2382 printf("\n");
2383 }
2384 #endif
2385
2386 free(data);
2387
2388 count++;
2389 }
2390
2391 bail:
2392 delete zip;
2393 return count;
2394 }
2395
2396 status_t AaptAssets::filter(Bundle* bundle)
2397 {
2398 ResourceFilter reqFilter;
2399 status_t err = reqFilter.parse(bundle->getConfigurations());
2400 if (err != NO_ERROR) {
2401 return err;
2402 }
2403
2404 ResourceFilter prefFilter;
2405 err = prefFilter.parse(bundle->getPreferredConfigurations());
2406 if (err != NO_ERROR) {
2407 return err;
2408 }
2409
2410 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2411 return NO_ERROR;
2412 }
2413
2414 if (bundle->getVerbose()) {
2415 if (!reqFilter.isEmpty()) {
2416 printf("Applying required filter: %s\n",
2417 bundle->getConfigurations());
2418 }
2419 if (!prefFilter.isEmpty()) {
2420 printf("Applying preferred filter: %s\n",
2421 bundle->getPreferredConfigurations());
2422 }
2423 }
2424
2425 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2426 const size_t ND = resdirs.size();
2427 for (size_t i=0; i<ND; i++) {
2428 const sp<AaptDir>& dir = resdirs.itemAt(i);
2429 if (dir->getLeaf() == kValuesDir) {
2430 // The "value" dir is special since a single file defines
2431 // multiple resources, so we can not do filtering on the
2432 // files themselves.
2433 continue;
2434 }
2435 if (dir->getLeaf() == kMipmapDir) {
2436 // We also skip the "mipmap" directory, since the point of this
2437 // is to include all densities without stripping. If you put
2438 // other configurations in here as well they won't be stripped
2439 // either... So don't do that. Seriously. What is wrong with you?
2440 continue;
2441 }
2442
2443 const size_t NG = dir->getFiles().size();
2444 for (size_t j=0; j<NG; j++) {
2445 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2446
2447 // First remove any configurations we know we don't need.
2448 for (size_t k=0; k<grp->getFiles().size(); k++) {
2449 sp<AaptFile> file = grp->getFiles().valueAt(k);
2450 if (k == 0 && grp->getFiles().size() == 1) {
2451 // If this is the only file left, we need to keep it.
2452 // Otherwise the resource IDs we are using will be inconsistent
2453 // with what we get when not stripping. Sucky, but at least
2454 // for now we can rely on the back-end doing another filtering
2455 // pass to take this out and leave us with this resource name
2456 // containing no entries.
2457 continue;
2458 }
2459 if (file->getPath().getPathExtension() == ".xml") {
2460 // We can't remove .xml files at this point, because when
2461 // we parse them they may add identifier resources, so
2462 // removing them can cause our resource identifiers to
2463 // become inconsistent.
2464 continue;
2465 }
2466 const ResTable_config& config(file->getGroupEntry().toParams());
2467 if (!reqFilter.match(config)) {
2468 if (bundle->getVerbose()) {
2469 printf("Pruning unneeded resource: %s\n",
2470 file->getPrintableSource().string());
2471 }
2472 grp->removeFile(k);
2473 k--;
2474 }
2475 }
2476
2477 // Quick check: no preferred filters, nothing more to do.
2478 if (prefFilter.isEmpty()) {
2479 continue;
2480 }
2481
2482 // Now deal with preferred configurations.
2483 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2484 for (size_t k=0; k<grp->getFiles().size(); k++) {
2485 sp<AaptFile> file = grp->getFiles().valueAt(k);
2486 if (k == 0 && grp->getFiles().size() == 1) {
2487 // If this is the only file left, we need to keep it.
2488 // Otherwise the resource IDs we are using will be inconsistent
2489 // with what we get when not stripping. Sucky, but at least
2490 // for now we can rely on the back-end doing another filtering
2491 // pass to take this out and leave us with this resource name
2492 // containing no entries.
2493 continue;
2494 }
2495 if (file->getPath().getPathExtension() == ".xml") {
2496 // We can't remove .xml files at this point, because when
2497 // we parse them they may add identifier resources, so
2498 // removing them can cause our resource identifiers to
2499 // become inconsistent.
2500 continue;
2501 }
2502 const ResTable_config& config(file->getGroupEntry().toParams());
2503 if (!prefFilter.match(axis, config)) {
2504 // This is a resource we would prefer not to have. Check
2505 // to see if have a similar variation that we would like
2506 // to have and, if so, we can drop it.
2507 for (size_t m=0; m<grp->getFiles().size(); m++) {
2508 if (m == k) continue;
2509 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2510 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2511 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
2512 if (prefFilter.match(axis, mconfig)) {
2513 if (bundle->getVerbose()) {
2514 printf("Pruning unneeded resource: %s\n",
2515 file->getPrintableSource().string());
2516 }
2517 grp->removeFile(k);
2518 k--;
2519 break;
2520 }
2521 }
2522 }
2523 }
2524 }
2525 }
2526 }
2527 }
2528
2529 return NO_ERROR;
2530 }
2531
2532 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2533 {
2534 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2535 if (sym == NULL) {
2536 sym = new AaptSymbols();
2537 mSymbols.add(name, sym);
2538 }
2539 return sym;
2540 }
2541
2542 sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2543 {
2544 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2545 if (sym == NULL) {
2546 sym = new AaptSymbols();
2547 mJavaSymbols.add(name, sym);
2548 }
2549 return sym;
2550 }
2551
2552 status_t AaptAssets::applyJavaSymbols()
2553 {
2554 size_t N = mJavaSymbols.size();
2555 for (size_t i=0; i<N; i++) {
2556 const String8& name = mJavaSymbols.keyAt(i);
2557 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2558 ssize_t pos = mSymbols.indexOfKey(name);
2559 if (pos < 0) {
2560 SourcePos pos;
2561 pos.error("Java symbol dir %s not defined\n", name.string());
2562 return UNKNOWN_ERROR;
2563 }
2564 //printf("**** applying java symbols in dir %s\n", name.string());
2565 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2566 if (err != NO_ERROR) {
2567 return err;
2568 }
2569 }
2570
2571 return NO_ERROR;
2572 }
2573
2574 bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2575 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2576 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2577 // sym.isJavaSymbol ? 1 : 0);
2578 if (!mHavePrivateSymbols) return true;
2579 if (sym.isPublic) return true;
2580 if (includePrivate && sym.isJavaSymbol) return true;
2581 return false;
2582 }
2583
2584 status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2585 {
2586 if (!mHaveIncludedAssets) {
2587 // Add in all includes.
2588 const Vector<const char*>& incl = bundle->getPackageIncludes();
2589 const size_t N=incl.size();
2590 for (size_t i=0; i<N; i++) {
2591 if (bundle->getVerbose())
2592 printf("Including resources from package: %s\n", incl[i]);
2593 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2594 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2595 incl[i]);
2596 return UNKNOWN_ERROR;
2597 }
2598 }
2599 mHaveIncludedAssets = true;
2600 }
2601
2602 return NO_ERROR;
2603 }
2604
2605 status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2606 {
2607 const ResTable& res = getIncludedResources();
2608 // XXX dirty!
2609 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2610 }
2611
2612 const ResTable& AaptAssets::getIncludedResources() const
2613 {
2614 return mIncludedAssets.getResources(false);
2615 }
2616
2617 void AaptAssets::print(const String8& prefix) const
2618 {
2619 String8 innerPrefix(prefix);
2620 innerPrefix.append(" ");
2621 String8 innerInnerPrefix(innerPrefix);
2622 innerInnerPrefix.append(" ");
2623 printf("%sConfigurations:\n", prefix.string());
2624 const size_t N=mGroupEntries.size();
2625 for (size_t i=0; i<N; i++) {
2626 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2627 printf("%s %s\n", prefix.string(),
2628 cname != "" ? cname.string() : "(default)");
2629 }
2630
2631 printf("\n%sFiles:\n", prefix.string());
2632 AaptDir::print(innerPrefix);
2633
2634 printf("\n%sResource Dirs:\n", prefix.string());
2635 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2636 const size_t NR = resdirs.size();
2637 for (size_t i=0; i<NR; i++) {
2638 const sp<AaptDir>& d = resdirs.itemAt(i);
2639 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2640 d->print(innerInnerPrefix);
2641 }
2642 }
2643
2644 sp<AaptDir> AaptAssets::resDir(const String8& name) const
2645 {
2646 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2647 const size_t N = resdirs.size();
2648 for (size_t i=0; i<N; i++) {
2649 const sp<AaptDir>& d = resdirs.itemAt(i);
2650 if (d->getLeaf() == name) {
2651 return d;
2652 }
2653 }
2654 return NULL;
2655 }
2656
2657 bool
2658 valid_symbol_name(const String8& symbol)
2659 {
2660 static char const * const KEYWORDS[] = {
2661 "abstract", "assert", "boolean", "break",
2662 "byte", "case", "catch", "char", "class", "const", "continue",
2663 "default", "do", "double", "else", "enum", "extends", "final",
2664 "finally", "float", "for", "goto", "if", "implements", "import",
2665 "instanceof", "int", "interface", "long", "native", "new", "package",
2666 "private", "protected", "public", "return", "short", "static",
2667 "strictfp", "super", "switch", "synchronized", "this", "throw",
2668 "throws", "transient", "try", "void", "volatile", "while",
2669 "true", "false", "null",
2670 NULL
2671 };
2672 const char*const* k = KEYWORDS;
2673 const char*const s = symbol.string();
2674 while (*k) {
2675 if (0 == strcmp(s, *k)) {
2676 return false;
2677 }
2678 k++;
2679 }
2680 return true;
2681 }