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