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