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