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