]> git.saurik.com Git - android/aapt.git/blame - AaptAssets.cpp
Fix typo. It's "picasa", not "picassa".
[android/aapt.git] / AaptAssets.cpp
CommitLineData
dadd9c1f
TAOSP
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
15static const char* kDefaultLocale = "default";
16static const char* kWildcardName = "any";
17static const char* kAssetDir = "assets";
18static const char* kResourceDir = "res";
19static const char* kInvalidChars = "/\\:";
20static const char* kExcludeExtension = ".EXCLUDE";
21static const size_t kMaxAssetFileName = 100;
22
23static 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 */
35static 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
57static 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
e1c41813 81 || strcasecmp(path, "picasa.ini") == 0) {
dadd9c1f
TAOSP
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
108status_t
109AaptGroupEntry::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
201bool
202AaptGroupEntry::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
397success:
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
416String8
417AaptGroupEntry::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
443String8
444AaptGroupEntry::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
495bool 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
527bool 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 */
566bool 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
608bool 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
628bool 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
664bool 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
684bool 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 }
699
700 if (mask != 0) {
701 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
702 return true;
703 }
704
705 return false;
706}
707
708bool AaptGroupEntry::getKeyboardName(const char* name,
709 ResTable_config* out)
710{
711 if (strcmp(name, kWildcardName) == 0) {
712 if (out) out->keyboard = out->KEYBOARD_ANY;
713 return true;
714 } else if (strcmp(name, "nokeys") == 0) {
715 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
716 return true;
717 } else if (strcmp(name, "qwerty") == 0) {
718 if (out) out->keyboard = out->KEYBOARD_QWERTY;
719 return true;
720 } else if (strcmp(name, "12key") == 0) {
721 if (out) out->keyboard = out->KEYBOARD_12KEY;
722 return true;
723 }
724
725 return false;
726}
727
728bool AaptGroupEntry::getNavigationName(const char* name,
729 ResTable_config* out)
730{
731 if (strcmp(name, kWildcardName) == 0) {
732 if (out) out->navigation = out->NAVIGATION_ANY;
733 return true;
734 } else if (strcmp(name, "nonav") == 0) {
735 if (out) out->navigation = out->NAVIGATION_NONAV;
736 return true;
737 } else if (strcmp(name, "dpad") == 0) {
738 if (out) out->navigation = out->NAVIGATION_DPAD;
739 return true;
740 } else if (strcmp(name, "trackball") == 0) {
741 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
742 return true;
743 } else if (strcmp(name, "wheel") == 0) {
744 if (out) out->navigation = out->NAVIGATION_WHEEL;
745 return true;
746 }
747
748 return false;
749}
750
751bool AaptGroupEntry::getScreenSizeName(const char* name,
752 ResTable_config* out)
753{
754 if (strcmp(name, kWildcardName) == 0) {
755 if (out) {
756 out->screenWidth = out->SCREENWIDTH_ANY;
757 out->screenHeight = out->SCREENHEIGHT_ANY;
758 }
759 return true;
760 }
761
762 const char* x = name;
763 while (*x >= '0' && *x <= '9') x++;
764 if (x == name || *x != 'x') return false;
765 String8 xName(name, x-name);
766 x++;
767
768 const char* y = x;
769 while (*y >= '0' && *y <= '9') y++;
770 if (y == name || *y != 0) return false;
771 String8 yName(x, y-x);
772
773 uint16_t w = (uint16_t)atoi(xName.string());
774 uint16_t h = (uint16_t)atoi(yName.string());
775 if (w < h) {
776 return false;
777 }
778
779 if (out) {
780 out->screenWidth = w;
781 out->screenHeight = h;
782 }
783
784 return true;
785}
786
787bool AaptGroupEntry::getVersionName(const char* name,
788 ResTable_config* out)
789{
790 if (strcmp(name, kWildcardName) == 0) {
791 if (out) {
792 out->sdkVersion = out->SDKVERSION_ANY;
793 out->minorVersion = out->MINORVERSION_ANY;
794 }
795 return true;
796 }
797
798 if (*name != 'v') {
799 return false;
800 }
801
802 name++;
803 const char* s = name;
804 while (*s >= '0' && *s <= '9') s++;
805 if (s == name || *s != 0) return false;
806 String8 sdkName(name, s-name);
807
808 if (out) {
809 out->sdkVersion = (uint16_t)atoi(sdkName.string());
810 out->minorVersion = 0;
811 }
812
813 return true;
814}
815
816int AaptGroupEntry::compare(const AaptGroupEntry& o) const
817{
818 int v = mcc.compare(o.mcc);
819 if (v == 0) v = mnc.compare(o.mnc);
820 if (v == 0) v = locale.compare(o.locale);
821 if (v == 0) v = vendor.compare(o.vendor);
822 if (v == 0) v = orientation.compare(o.orientation);
823 if (v == 0) v = density.compare(o.density);
824 if (v == 0) v = touchscreen.compare(o.touchscreen);
825 if (v == 0) v = keysHidden.compare(o.keysHidden);
826 if (v == 0) v = keyboard.compare(o.keyboard);
827 if (v == 0) v = navigation.compare(o.navigation);
828 if (v == 0) v = screenSize.compare(o.screenSize);
829 if (v == 0) v = version.compare(o.version);
830 return v;
831}
832
833ResTable_config AaptGroupEntry::toParams() const
834{
835 ResTable_config params;
836 memset(&params, 0, sizeof(params));
837 getMccName(mcc.string(), &params);
838 getMncName(mnc.string(), &params);
839 getLocaleName(locale.string(), &params);
840 getOrientationName(orientation.string(), &params);
841 getDensityName(density.string(), &params);
842 getTouchscreenName(touchscreen.string(), &params);
843 getKeysHiddenName(keysHidden.string(), &params);
844 getKeyboardName(keyboard.string(), &params);
845 getNavigationName(navigation.string(), &params);
846 getScreenSizeName(screenSize.string(), &params);
847 getVersionName(version.string(), &params);
848 return params;
849}
850
851// =========================================================================
852// =========================================================================
853// =========================================================================
854
855void* AaptFile::editData(size_t size)
856{
857 if (size <= mBufferSize) {
858 mDataSize = size;
859 return mData;
860 }
861 size_t allocSize = (size*3)/2;
862 void* buf = realloc(mData, allocSize);
863 if (buf == NULL) {
864 return NULL;
865 }
866 mData = buf;
867 mDataSize = size;
868 mBufferSize = allocSize;
869 return buf;
870}
871
872void* AaptFile::editData(size_t* outSize)
873{
874 if (outSize) {
875 *outSize = mDataSize;
876 }
877 return mData;
878}
879
880void* AaptFile::padData(size_t wordSize)
881{
882 const size_t extra = mDataSize%wordSize;
883 if (extra == 0) {
884 return mData;
885 }
886
887 size_t initial = mDataSize;
888 void* data = editData(initial+(wordSize-extra));
889 if (data != NULL) {
890 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
891 }
892 return data;
893}
894
895status_t AaptFile::writeData(const void* data, size_t size)
896{
897 size_t end = mDataSize;
898 size_t total = size + end;
899 void* buf = editData(total);
900 if (buf == NULL) {
901 return UNKNOWN_ERROR;
902 }
903 memcpy(((char*)buf)+end, data, size);
904 return NO_ERROR;
905}
906
907void AaptFile::clearData()
908{
909 if (mData != NULL) free(mData);
910 mData = NULL;
911 mDataSize = 0;
912 mBufferSize = 0;
913}
914
915String8 AaptFile::getPrintableSource() const
916{
917 if (hasData()) {
918 String8 name(mGroupEntry.locale.string());
919 name.appendPath(mGroupEntry.vendor.string());
920 name.appendPath(mPath);
921 name.append(" #generated");
922 return name;
923 }
924 return mSourceFile;
925}
926
927// =========================================================================
928// =========================================================================
929// =========================================================================
930
931status_t AaptGroup::addFile(const sp<AaptFile>& file)
932{
933 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
934 file->mPath = mPath;
935 mFiles.add(file->getGroupEntry(), file);
936 return NO_ERROR;
937 }
938
939 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
940 getPrintableSource().string());
941 return UNKNOWN_ERROR;
942}
943
944void AaptGroup::removeFile(size_t index)
945{
946 mFiles.removeItemsAt(index);
947}
948
949void AaptGroup::print() const
950{
951 printf(" %s\n", getPath().string());
952 const size_t N=mFiles.size();
953 size_t i;
954 for (i=0; i<N; i++) {
955 sp<AaptFile> file = mFiles.valueAt(i);
956 const AaptGroupEntry& e = file->getGroupEntry();
957 if (file->hasData()) {
958 printf(" Gen: (%s) %d bytes\n", e.toString().string(),
959 (int)file->getSize());
960 } else {
961 printf(" Src: %s\n", file->getPrintableSource().string());
962 }
963 }
964}
965
966String8 AaptGroup::getPrintableSource() const
967{
968 if (mFiles.size() > 0) {
969 // Arbitrarily pull the first source file out of the list.
970 return mFiles.valueAt(0)->getPrintableSource();
971 }
972
973 // Should never hit this case, but to be safe...
974 return getPath();
975
976}
977
978// =========================================================================
979// =========================================================================
980// =========================================================================
981
982status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
983{
984 if (mFiles.indexOfKey(name) >= 0) {
985 return ALREADY_EXISTS;
986 }
987 mFiles.add(name, file);
988 return NO_ERROR;
989}
990
991status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
992{
993 if (mDirs.indexOfKey(name) >= 0) {
994 return ALREADY_EXISTS;
995 }
996 mDirs.add(name, dir);
997 return NO_ERROR;
998}
999
1000sp<AaptDir> AaptDir::makeDir(const String8& path)
1001{
1002 String8 name;
1003 String8 remain = path;
1004
1005 sp<AaptDir> subdir = this;
1006 while (name = remain.walkPath(&remain), remain != "") {
1007 subdir = subdir->makeDir(name);
1008 }
1009
1010 ssize_t i = subdir->mDirs.indexOfKey(name);
1011 if (i >= 0) {
1012 return subdir->mDirs.valueAt(i);
1013 }
1014 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1015 subdir->mDirs.add(name, dir);
1016 return dir;
1017}
1018
1019void AaptDir::removeFile(const String8& name)
1020{
1021 mFiles.removeItem(name);
1022}
1023
1024void AaptDir::removeDir(const String8& name)
1025{
1026 mDirs.removeItem(name);
1027}
1028
1029status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1030{
1031 sp<AaptGroup> origGroup;
1032
1033 // Find and remove the given file with shear, brute force!
1034 const size_t NG = mFiles.size();
1035 size_t i;
1036 for (i=0; origGroup == NULL && i<NG; i++) {
1037 sp<AaptGroup> g = mFiles.valueAt(i);
1038 const size_t NF = g->getFiles().size();
1039 for (size_t j=0; j<NF; j++) {
1040 if (g->getFiles().valueAt(j) == file) {
1041 origGroup = g;
1042 g->removeFile(j);
1043 if (NF == 1) {
1044 mFiles.removeItemsAt(i);
1045 }
1046 break;
1047 }
1048 }
1049 }
1050
1051 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1052
1053 // Place the file under its new name.
1054 if (origGroup != NULL) {
1055 return addLeafFile(newName, file);
1056 }
1057
1058 return NO_ERROR;
1059}
1060
1061status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1062{
1063 sp<AaptGroup> group;
1064 if (mFiles.indexOfKey(leafName) >= 0) {
1065 group = mFiles.valueFor(leafName);
1066 } else {
1067 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1068 mFiles.add(leafName, group);
1069 }
1070
1071 return group->addFile(file);
1072}
1073
1074ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1075 const AaptGroupEntry& kind, const String8& resType)
1076{
1077 Vector<String8> fileNames;
1078
1079 {
1080 DIR* dir = NULL;
1081
1082 dir = opendir(srcDir.string());
1083 if (dir == NULL) {
1084 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1085 return UNKNOWN_ERROR;
1086 }
1087
1088 /*
1089 * Slurp the filenames out of the directory.
1090 */
1091 while (1) {
1092 struct dirent* entry;
1093
1094 entry = readdir(dir);
1095 if (entry == NULL)
1096 break;
1097
1098 if (isHidden(srcDir.string(), entry->d_name))
1099 continue;
1100
1101 fileNames.add(String8(entry->d_name));
1102 }
1103
1104 closedir(dir);
1105 }
1106
1107 ssize_t count = 0;
1108
1109 /*
1110 * Stash away the files and recursively descend into subdirectories.
1111 */
1112 const size_t N = fileNames.size();
1113 size_t i;
1114 for (i = 0; i < N; i++) {
1115 String8 pathName(srcDir);
1116 FileType type;
1117
1118 pathName.appendPath(fileNames[i].string());
1119 type = getFileType(pathName.string());
1120 if (type == kFileTypeDirectory) {
1121 sp<AaptDir> subdir;
1122 bool notAdded = false;
1123 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1124 subdir = mDirs.valueFor(fileNames[i]);
1125 } else {
1126 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1127 notAdded = true;
1128 }
1129 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1130 resType);
1131 if (res < NO_ERROR) {
1132 return res;
1133 }
1134 if (res > 0 && notAdded) {
1135 mDirs.add(fileNames[i], subdir);
1136 }
1137 count += res;
1138 } else if (type == kFileTypeRegular) {
1139 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1140 status_t err = addLeafFile(fileNames[i], file);
1141 if (err != NO_ERROR) {
1142 return err;
1143 }
1144
1145 count++;
1146
1147 } else {
1148 if (bundle->getVerbose())
1149 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1150 }
1151 }
1152
1153 return count;
1154}
1155
1156status_t AaptDir::validate() const
1157{
1158 const size_t NF = mFiles.size();
1159 const size_t ND = mDirs.size();
1160 size_t i;
1161 for (i = 0; i < NF; i++) {
1162 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1163 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1164 "Invalid filename. Unable to add.");
1165 return UNKNOWN_ERROR;
1166 }
1167
1168 size_t j;
1169 for (j = i+1; j < NF; j++) {
1170 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1171 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1172 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1173 "File is case-insensitive equivalent to: %s",
1174 mFiles.valueAt(j)->getPrintableSource().string());
1175 return UNKNOWN_ERROR;
1176 }
1177
1178 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1179 // (this is mostly caught by the "marked" stuff, below)
1180 }
1181
1182 for (j = 0; j < ND; j++) {
1183 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1184 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1185 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1186 "File conflicts with dir from: %s",
1187 mDirs.valueAt(j)->getPrintableSource().string());
1188 return UNKNOWN_ERROR;
1189 }
1190 }
1191 }
1192
1193 for (i = 0; i < ND; i++) {
1194 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1195 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1196 "Invalid directory name, unable to add.");
1197 return UNKNOWN_ERROR;
1198 }
1199
1200 size_t j;
1201 for (j = i+1; j < ND; j++) {
1202 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1203 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1204 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1205 "Directory is case-insensitive equivalent to: %s",
1206 mDirs.valueAt(j)->getPrintableSource().string());
1207 return UNKNOWN_ERROR;
1208 }
1209 }
1210
1211 status_t err = mDirs.valueAt(i)->validate();
1212 if (err != NO_ERROR) {
1213 return err;
1214 }
1215 }
1216
1217 return NO_ERROR;
1218}
1219
1220void AaptDir::print() const
1221{
1222 const size_t ND=getDirs().size();
1223 size_t i;
1224 for (i=0; i<ND; i++) {
1225 getDirs().valueAt(i)->print();
1226 }
1227
1228 const size_t NF=getFiles().size();
1229 for (i=0; i<NF; i++) {
1230 getFiles().valueAt(i)->print();
1231 }
1232}
1233
1234String8 AaptDir::getPrintableSource() const
1235{
1236 if (mFiles.size() > 0) {
1237 // Arbitrarily pull the first file out of the list as the source dir.
1238 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1239 }
1240 if (mDirs.size() > 0) {
1241 // Or arbitrarily pull the first dir out of the list as the source dir.
1242 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1243 }
1244
1245 // Should never hit this case, but to be safe...
1246 return mPath;
1247
1248}
1249
1250// =========================================================================
1251// =========================================================================
1252// =========================================================================
1253
1254sp<AaptFile> AaptAssets::addFile(
1255 const String8& filePath, const AaptGroupEntry& entry,
1256 const String8& srcDir, sp<AaptGroup>* outGroup,
1257 const String8& resType)
1258{
1259 sp<AaptDir> dir = this;
1260 sp<AaptGroup> group;
1261 sp<AaptFile> file;
1262 String8 root, remain(filePath), partialPath;
1263 while (remain.length() > 0) {
1264 root = remain.walkPath(&remain);
1265 partialPath.appendPath(root);
1266
1267 const String8 rootStr(root);
1268
1269 if (remain.length() == 0) {
1270 ssize_t i = dir->getFiles().indexOfKey(rootStr);
1271 if (i >= 0) {
1272 group = dir->getFiles().valueAt(i);
1273 } else {
1274 group = new AaptGroup(rootStr, filePath);
1275 status_t res = dir->addFile(rootStr, group);
1276 if (res != NO_ERROR) {
1277 return NULL;
1278 }
1279 }
1280 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1281 status_t res = group->addFile(file);
1282 if (res != NO_ERROR) {
1283 return NULL;
1284 }
1285 break;
1286
1287 } else {
1288 ssize_t i = dir->getDirs().indexOfKey(rootStr);
1289 if (i >= 0) {
1290 dir = dir->getDirs().valueAt(i);
1291 } else {
1292 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1293 status_t res = dir->addDir(rootStr, subdir);
1294 if (res != NO_ERROR) {
1295 return NULL;
1296 }
1297 dir = subdir;
1298 }
1299 }
1300 }
1301
1302 mGroupEntries.add(entry);
1303 if (outGroup) *outGroup = group;
1304 return file;
1305}
1306
1307void AaptAssets::addResource(const String8& leafName, const String8& path,
1308 const sp<AaptFile>& file, const String8& resType)
1309{
1310 sp<AaptDir> res = AaptDir::makeDir(kResString);
1311 String8 dirname = file->getGroupEntry().toDirName(resType);
1312 sp<AaptDir> subdir = res->makeDir(dirname);
1313 sp<AaptGroup> grr = new AaptGroup(leafName, path);
1314 grr->addFile(file);
1315
1316 subdir->addFile(leafName, grr);
1317}
1318
1319
1320ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1321{
1322 int count;
1323 int totalCount = 0;
1324 int i;
1325 int arg = 0;
1326 FileType type;
1327 const char* res;
1328
1329 const int N = bundle->getFileSpecCount();
1330
1331 /*
1332 * If a package manifest was specified, include that first.
1333 */
1334 if (bundle->getAndroidManifestFile() != NULL) {
1335 // place at root of zip.
1336 String8 srcFile(bundle->getAndroidManifestFile());
1337 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1338 NULL, String8());
1339 totalCount++;
1340 }
1341
1342 /*
1343 * If a directory of custom assets was supplied, slurp 'em up.
1344 */
1345 if (bundle->getAssetSourceDir()) {
1346 const char* assetDir = bundle->getAssetSourceDir();
1347
1348 FileType type = getFileType(assetDir);
1349 if (type == kFileTypeNonexistent) {
1350 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1351 return UNKNOWN_ERROR;
1352 }
1353 if (type != kFileTypeDirectory) {
1354 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1355 return UNKNOWN_ERROR;
1356 }
1357
1358 String8 assetRoot(assetDir);
1359 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1360 AaptGroupEntry group;
1361 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1362 String8());
1363 if (count < 0) {
1364 totalCount = count;
1365 goto bail;
1366 }
1367 if (count > 0) {
1368 mGroupEntries.add(group);
1369 }
1370 totalCount += count;
1371
1372 if (bundle->getVerbose())
1373 printf("Found %d custom asset file%s in %s\n",
1374 count, (count==1) ? "" : "s", assetDir);
1375 }
1376
1377 /*
1378 * If a directory of resource-specific assets was supplied, slurp 'em up.
1379 */
1380 res = bundle->getResourceSourceDir();
1381 if (res) {
1382 type = getFileType(res);
1383 if (type == kFileTypeNonexistent) {
1384 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1385 return UNKNOWN_ERROR;
1386 }
1387 if (type == kFileTypeDirectory) {
1388 count = slurpResourceTree(bundle, String8(res));
1389
1390 if (count < 0) {
1391 totalCount = count;
1392 goto bail;
1393 }
1394 totalCount += count;
1395 }
1396 else {
1397 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1398 return UNKNOWN_ERROR;
1399 }
1400 }
1401
1402 /*
1403 * Now do any additional raw files.
1404 */
1405 for (int arg=0; arg<N; arg++) {
1406 const char* assetDir = bundle->getFileSpecEntry(arg);
1407
1408 FileType type = getFileType(assetDir);
1409 if (type == kFileTypeNonexistent) {
1410 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1411 return UNKNOWN_ERROR;
1412 }
1413 if (type != kFileTypeDirectory) {
1414 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1415 return UNKNOWN_ERROR;
1416 }
1417
1418 String8 assetRoot(assetDir);
1419
1420 if (bundle->getVerbose())
1421 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1422
1423 /*
1424 * Do a recursive traversal of subdir tree. We don't make any
1425 * guarantees about ordering, so we're okay with an inorder search
1426 * using whatever order the OS happens to hand back to us.
1427 */
1428 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
1429 if (count < 0) {
1430 /* failure; report error and remove archive */
1431 totalCount = count;
1432 goto bail;
1433 }
1434 totalCount += count;
1435
1436 if (bundle->getVerbose())
1437 printf("Found %d asset file%s in %s\n",
1438 count, (count==1) ? "" : "s", assetDir);
1439 }
1440
1441 count = validate();
1442 if (count != NO_ERROR) {
1443 totalCount = count;
1444 goto bail;
1445 }
1446
1447
1448bail:
1449 return totalCount;
1450}
1451
1452ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1453 const AaptGroupEntry& kind,
1454 const String8& resType)
1455{
1456 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
1457 if (res > 0) {
1458 mGroupEntries.add(kind);
1459 }
1460
1461 return res;
1462}
1463
1464ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1465{
1466 ssize_t err = 0;
1467
1468 DIR* dir = opendir(srcDir.string());
1469 if (dir == NULL) {
1470 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1471 return UNKNOWN_ERROR;
1472 }
1473
1474 status_t count = 0;
1475
1476 /*
1477 * Run through the directory, looking for dirs that match the
1478 * expected pattern.
1479 */
1480 while (1) {
1481 struct dirent* entry = readdir(dir);
1482 if (entry == NULL) {
1483 break;
1484 }
1485
1486 if (isHidden(srcDir.string(), entry->d_name)) {
1487 continue;
1488 }
1489
1490 String8 subdirName(srcDir);
1491 subdirName.appendPath(entry->d_name);
1492
1493 AaptGroupEntry group;
1494 String8 resType;
1495 bool b = group.initFromDirName(entry->d_name, &resType);
1496 if (!b) {
1497 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
1498 entry->d_name);
1499 err = -1;
1500 continue;
1501 }
1502
1503 FileType type = getFileType(subdirName.string());
1504
1505 if (type == kFileTypeDirectory) {
1506 sp<AaptDir> dir = makeDir(String8(entry->d_name));
1507 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1508 resType);
1509 if (res < 0) {
1510 count = res;
1511 goto bail;
1512 }
1513 if (res > 0) {
1514 mGroupEntries.add(group);
1515 count += res;
1516 }
1517
1518 mDirs.add(dir);
1519 } else {
1520 if (bundle->getVerbose()) {
1521 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
1522 }
1523 }
1524 }
1525
1526bail:
1527 closedir(dir);
1528 dir = NULL;
1529
1530 if (err != 0) {
1531 return err;
1532 }
1533 return count;
1534}
1535
1536ssize_t
1537AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
1538{
1539 int count = 0;
1540 SortedVector<AaptGroupEntry> entries;
1541
1542 ZipFile* zip = new ZipFile;
1543 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1544 if (err != NO_ERROR) {
1545 fprintf(stderr, "error opening zip file %s\n", filename);
1546 count = err;
1547 delete zip;
1548 return -1;
1549 }
1550
1551 const int N = zip->getNumEntries();
1552 for (int i=0; i<N; i++) {
1553 ZipEntry* entry = zip->getEntryByIndex(i);
1554 if (entry->getDeleted()) {
1555 continue;
1556 }
1557
1558 String8 entryName(entry->getFileName());
1559
1560 String8 dirName = entryName.getPathDir();
1561 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1562
1563 String8 resType;
1564 AaptGroupEntry kind;
1565
1566 String8 remain;
1567 if (entryName.walkPath(&remain) == kResourceDir) {
1568 // these are the resources, pull their type out of the directory name
1569 kind.initFromDirName(remain.walkPath().string(), &resType);
1570 } else {
1571 // these are untyped and don't have an AaptGroupEntry
1572 }
1573 if (entries.indexOf(kind) < 0) {
1574 entries.add(kind);
1575 mGroupEntries.add(kind);
1576 }
1577
1578 // use the one from the zip file if they both exist.
1579 dir->removeFile(entryName.getPathLeaf());
1580
1581 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1582 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1583 if (err != NO_ERROR) {
1584 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1585 count = err;
1586 goto bail;
1587 }
1588 file->setCompressionMethod(entry->getCompressionMethod());
1589
1590#if 0
1591 if (entryName == "AndroidManifest.xml") {
1592 printf("AndroidManifest.xml\n");
1593 }
1594 printf("\n\nfile: %s\n", entryName.string());
1595#endif
1596
1597 size_t len = entry->getUncompressedLen();
1598 void* data = zip->uncompress(entry);
1599 void* buf = file->editData(len);
1600 memcpy(buf, data, len);
1601
1602#if 0
1603 const int OFF = 0;
1604 const unsigned char* p = (unsigned char*)data;
1605 const unsigned char* end = p+len;
1606 p += OFF;
1607 for (int i=0; i<32 && p < end; i++) {
1608 printf("0x%03x ", i*0x10 + OFF);
1609 for (int j=0; j<0x10 && p < end; j++) {
1610 printf(" %02x", *p);
1611 p++;
1612 }
1613 printf("\n");
1614 }
1615#endif
1616
1617 free(data);
1618
1619 count++;
1620 }
1621
1622bail:
1623 delete zip;
1624 return count;
1625}
1626
1627sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1628{
1629 sp<AaptSymbols> sym = mSymbols.valueFor(name);
1630 if (sym == NULL) {
1631 sym = new AaptSymbols();
1632 mSymbols.add(name, sym);
1633 }
1634 return sym;
1635}
1636
1637status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1638{
1639 if (!mHaveIncludedAssets) {
1640 // Add in all includes.
1641 const Vector<const char*>& incl = bundle->getPackageIncludes();
1642 const size_t N=incl.size();
1643 for (size_t i=0; i<N; i++) {
1644 if (bundle->getVerbose())
1645 printf("Including resources from package: %s\n", incl[i]);
1646 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
1647 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1648 incl[i]);
1649 return UNKNOWN_ERROR;
1650 }
1651 }
1652 mHaveIncludedAssets = true;
1653 }
1654
1655 return NO_ERROR;
1656}
1657
1658status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1659{
1660 const ResTable& res = getIncludedResources();
1661 // XXX dirty!
1662 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
1663}
1664
1665const ResTable& AaptAssets::getIncludedResources() const
1666{
1667 return mIncludedAssets.getResources(false);
1668}
1669
1670void AaptAssets::print() const
1671{
1672 printf("Locale/Vendor pairs:\n");
1673 const size_t N=mGroupEntries.size();
1674 for (size_t i=0; i<N; i++) {
1675 printf(" %s/%s\n",
1676 mGroupEntries.itemAt(i).locale.string(),
1677 mGroupEntries.itemAt(i).vendor.string());
1678 }
1679
1680 printf("\nFiles:\n");
1681 AaptDir::print();
1682}
1683
1684bool
1685valid_symbol_name(const String8& symbol)
1686{
1687 static char const * const KEYWORDS[] = {
1688 "abstract", "assert", "boolean", "break",
1689 "byte", "case", "catch", "char", "class", "const", "continue",
1690 "default", "do", "double", "else", "enum", "extends", "final",
1691 "finally", "float", "for", "goto", "if", "implements", "import",
1692 "instanceof", "int", "interface", "long", "native", "new", "package",
1693 "private", "protected", "public", "return", "short", "static",
1694 "strictfp", "super", "switch", "synchronized", "this", "throw",
1695 "throws", "transient", "try", "void", "volatile", "while",
1696 "true", "false", "null",
1697 NULL
1698 };
1699 const char*const* k = KEYWORDS;
1700 const char*const s = symbol.string();
1701 while (*k) {
1702 if (0 == strcmp(s, *k)) {
1703 return false;
1704 }
1705 k++;
1706 }
1707 return true;
1708}