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