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