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