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