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