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