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