blob: 2b2ec7b3e9a7c1c6ec458c4e5a60c279ebd106ba [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
Johan Redestig5ef0b9d2010-11-09 14:13:31 +0100688 if (out) {
689 out->mnc = atoi(val);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 }
691
Johan Redestig5ef0b9d2010-11-09 14:13:31 +0100692 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693}
694
695/*
696 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
697 * "default")?
698 *
699 * TODO: Should insist that the first two letters are lower case, and the
700 * second two are upper.
701 */
702bool AaptGroupEntry::getLocaleName(const char* fileName,
703 ResTable_config* out)
704{
705 if (strcmp(fileName, kWildcardName) == 0
706 || strcmp(fileName, kDefaultLocale) == 0) {
707 if (out) {
708 out->language[0] = 0;
709 out->language[1] = 0;
710 out->country[0] = 0;
711 out->country[1] = 0;
712 }
713 return true;
714 }
715
716 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
717 if (out) {
718 out->language[0] = fileName[0];
719 out->language[1] = fileName[1];
720 out->country[0] = 0;
721 out->country[1] = 0;
722 }
723 return true;
724 }
725
726 if (strlen(fileName) == 5 &&
727 isalpha(fileName[0]) &&
728 isalpha(fileName[1]) &&
729 fileName[2] == '-' &&
730 isalpha(fileName[3]) &&
731 isalpha(fileName[4])) {
732 if (out) {
733 out->language[0] = fileName[0];
734 out->language[1] = fileName[1];
735 out->country[0] = fileName[3];
736 out->country[1] = fileName[4];
737 }
738 return true;
739 }
740
741 return false;
742}
743
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700744bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
745 ResTable_config* out)
746{
747 if (strcmp(name, kWildcardName) == 0) {
748 if (out) out->screenLayout =
749 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
750 | ResTable_config::SCREENSIZE_ANY;
751 return true;
752 } else if (strcmp(name, "small") == 0) {
753 if (out) out->screenLayout =
754 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
755 | ResTable_config::SCREENSIZE_SMALL;
756 return true;
757 } else if (strcmp(name, "normal") == 0) {
758 if (out) out->screenLayout =
759 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
760 | ResTable_config::SCREENSIZE_NORMAL;
761 return true;
762 } else if (strcmp(name, "large") == 0) {
763 if (out) out->screenLayout =
764 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
765 | ResTable_config::SCREENSIZE_LARGE;
766 return true;
Dianne Hackborn14cee9f2010-04-23 17:51:26 -0700767 } else if (strcmp(name, "xlarge") == 0) {
768 if (out) out->screenLayout =
769 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
770 | ResTable_config::SCREENSIZE_XLARGE;
771 return true;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700772 }
773
774 return false;
775}
776
777bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
778 ResTable_config* out)
779{
780 if (strcmp(name, kWildcardName) == 0) {
781 if (out) out->screenLayout =
782 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
783 | ResTable_config::SCREENLONG_ANY;
784 return true;
785 } else if (strcmp(name, "long") == 0) {
786 if (out) out->screenLayout =
787 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
788 | ResTable_config::SCREENLONG_YES;
789 return true;
790 } else if (strcmp(name, "notlong") == 0) {
791 if (out) out->screenLayout =
792 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
793 | ResTable_config::SCREENLONG_NO;
794 return true;
795 }
796
797 return false;
798}
799
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800bool AaptGroupEntry::getOrientationName(const char* name,
801 ResTable_config* out)
802{
803 if (strcmp(name, kWildcardName) == 0) {
804 if (out) out->orientation = out->ORIENTATION_ANY;
805 return true;
806 } else if (strcmp(name, "port") == 0) {
807 if (out) out->orientation = out->ORIENTATION_PORT;
808 return true;
809 } else if (strcmp(name, "land") == 0) {
810 if (out) out->orientation = out->ORIENTATION_LAND;
811 return true;
812 } else if (strcmp(name, "square") == 0) {
813 if (out) out->orientation = out->ORIENTATION_SQUARE;
814 return true;
815 }
816
817 return false;
818}
819
Tobias Haamel27b28b32010-02-09 23:09:17 +0100820bool AaptGroupEntry::getUiModeTypeName(const char* name,
821 ResTable_config* out)
822{
823 if (strcmp(name, kWildcardName) == 0) {
824 if (out) out->uiMode =
825 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackborn7299c412010-03-04 18:41:49 -0800826 | ResTable_config::UI_MODE_TYPE_ANY;
827 return true;
828 } else if (strcmp(name, "desk") == 0) {
829 if (out) out->uiMode =
830 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
831 | ResTable_config::UI_MODE_TYPE_DESK;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100832 return true;
833 } else if (strcmp(name, "car") == 0) {
834 if (out) out->uiMode =
835 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
836 | ResTable_config::UI_MODE_TYPE_CAR;
837 return true;
838 }
839
840 return false;
841}
842
843bool AaptGroupEntry::getUiModeNightName(const char* name,
844 ResTable_config* out)
845{
846 if (strcmp(name, kWildcardName) == 0) {
847 if (out) out->uiMode =
848 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
849 | ResTable_config::UI_MODE_NIGHT_ANY;
850 return true;
851 } else if (strcmp(name, "night") == 0) {
852 if (out) out->uiMode =
853 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
854 | ResTable_config::UI_MODE_NIGHT_YES;
855 return true;
856 } else if (strcmp(name, "notnight") == 0) {
857 if (out) out->uiMode =
858 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
859 | ResTable_config::UI_MODE_NIGHT_NO;
860 return true;
861 }
862
863 return false;
864}
865
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866bool AaptGroupEntry::getDensityName(const char* name,
867 ResTable_config* out)
868{
869 if (strcmp(name, kWildcardName) == 0) {
Dianne Hackborna53b8282009-07-17 11:13:48 -0700870 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 return true;
872 }
Dianne Hackborna53b8282009-07-17 11:13:48 -0700873
874 if (strcmp(name, "nodpi") == 0) {
875 if (out) out->density = ResTable_config::DENSITY_NONE;
876 return true;
877 }
878
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700879 if (strcmp(name, "ldpi") == 0) {
880 if (out) out->density = ResTable_config::DENSITY_LOW;
881 return true;
882 }
883
884 if (strcmp(name, "mdpi") == 0) {
885 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
886 return true;
887 }
888
889 if (strcmp(name, "hdpi") == 0) {
890 if (out) out->density = ResTable_config::DENSITY_HIGH;
891 return true;
892 }
893
Dianne Hackborn588feee2010-06-04 14:36:39 -0700894 if (strcmp(name, "xhdpi") == 0) {
895 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
896 return true;
897 }
898
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 char* c = (char*)name;
900 while (*c >= '0' && *c <= '9') {
901 c++;
902 }
903
904 // check that we have 'dpi' after the last digit.
905 if (toupper(c[0]) != 'D' ||
906 toupper(c[1]) != 'P' ||
907 toupper(c[2]) != 'I' ||
908 c[3] != 0) {
909 return false;
910 }
911
912 // temporarily replace the first letter with \0 to
913 // use atoi.
914 char tmp = c[0];
915 c[0] = '\0';
916
917 int d = atoi(name);
918 c[0] = tmp;
919
920 if (d != 0) {
921 if (out) out->density = d;
922 return true;
923 }
924
925 return false;
926}
927
928bool AaptGroupEntry::getTouchscreenName(const char* name,
929 ResTable_config* out)
930{
931 if (strcmp(name, kWildcardName) == 0) {
932 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
933 return true;
934 } else if (strcmp(name, "notouch") == 0) {
935 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
936 return true;
937 } else if (strcmp(name, "stylus") == 0) {
938 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
939 return true;
940 } else if (strcmp(name, "finger") == 0) {
941 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
942 return true;
943 }
944
945 return false;
946}
947
948bool AaptGroupEntry::getKeysHiddenName(const char* name,
949 ResTable_config* out)
950{
951 uint8_t mask = 0;
952 uint8_t value = 0;
953 if (strcmp(name, kWildcardName) == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -0800954 mask = ResTable_config::MASK_KEYSHIDDEN;
955 value = ResTable_config::KEYSHIDDEN_ANY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 } else if (strcmp(name, "keysexposed") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -0800957 mask = ResTable_config::MASK_KEYSHIDDEN;
958 value = ResTable_config::KEYSHIDDEN_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 } else if (strcmp(name, "keyshidden") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -0800960 mask = ResTable_config::MASK_KEYSHIDDEN;
961 value = ResTable_config::KEYSHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 } else if (strcmp(name, "keyssoft") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -0800963 mask = ResTable_config::MASK_KEYSHIDDEN;
964 value = ResTable_config::KEYSHIDDEN_SOFT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 }
966
967 if (mask != 0) {
968 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
969 return true;
970 }
971
972 return false;
973}
974
975bool AaptGroupEntry::getKeyboardName(const char* name,
976 ResTable_config* out)
977{
978 if (strcmp(name, kWildcardName) == 0) {
979 if (out) out->keyboard = out->KEYBOARD_ANY;
980 return true;
981 } else if (strcmp(name, "nokeys") == 0) {
982 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
983 return true;
984 } else if (strcmp(name, "qwerty") == 0) {
985 if (out) out->keyboard = out->KEYBOARD_QWERTY;
986 return true;
987 } else if (strcmp(name, "12key") == 0) {
988 if (out) out->keyboard = out->KEYBOARD_12KEY;
989 return true;
990 }
991
992 return false;
993}
994
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700995bool AaptGroupEntry::getNavHiddenName(const char* name,
996 ResTable_config* out)
997{
998 uint8_t mask = 0;
999 uint8_t value = 0;
1000 if (strcmp(name, kWildcardName) == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001001 mask = ResTable_config::MASK_NAVHIDDEN;
1002 value = ResTable_config::NAVHIDDEN_ANY;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001003 } else if (strcmp(name, "navexposed") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001004 mask = ResTable_config::MASK_NAVHIDDEN;
1005 value = ResTable_config::NAVHIDDEN_NO;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001006 } else if (strcmp(name, "navhidden") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001007 mask = ResTable_config::MASK_NAVHIDDEN;
1008 value = ResTable_config::NAVHIDDEN_YES;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001009 }
1010
1011 if (mask != 0) {
1012 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1013 return true;
1014 }
1015
1016 return false;
1017}
1018
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019bool AaptGroupEntry::getNavigationName(const char* name,
1020 ResTable_config* out)
1021{
1022 if (strcmp(name, kWildcardName) == 0) {
1023 if (out) out->navigation = out->NAVIGATION_ANY;
1024 return true;
1025 } else if (strcmp(name, "nonav") == 0) {
1026 if (out) out->navigation = out->NAVIGATION_NONAV;
1027 return true;
1028 } else if (strcmp(name, "dpad") == 0) {
1029 if (out) out->navigation = out->NAVIGATION_DPAD;
1030 return true;
1031 } else if (strcmp(name, "trackball") == 0) {
1032 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1033 return true;
1034 } else if (strcmp(name, "wheel") == 0) {
1035 if (out) out->navigation = out->NAVIGATION_WHEEL;
1036 return true;
1037 }
1038
1039 return false;
1040}
1041
1042bool AaptGroupEntry::getScreenSizeName(const char* name,
1043 ResTable_config* out)
1044{
1045 if (strcmp(name, kWildcardName) == 0) {
1046 if (out) {
1047 out->screenWidth = out->SCREENWIDTH_ANY;
1048 out->screenHeight = out->SCREENHEIGHT_ANY;
1049 }
1050 return true;
1051 }
1052
1053 const char* x = name;
1054 while (*x >= '0' && *x <= '9') x++;
1055 if (x == name || *x != 'x') return false;
1056 String8 xName(name, x-name);
1057 x++;
1058
1059 const char* y = x;
1060 while (*y >= '0' && *y <= '9') y++;
1061 if (y == name || *y != 0) return false;
1062 String8 yName(x, y-x);
1063
1064 uint16_t w = (uint16_t)atoi(xName.string());
1065 uint16_t h = (uint16_t)atoi(yName.string());
1066 if (w < h) {
1067 return false;
1068 }
1069
1070 if (out) {
1071 out->screenWidth = w;
1072 out->screenHeight = h;
1073 }
1074
1075 return true;
1076}
1077
1078bool AaptGroupEntry::getVersionName(const char* name,
1079 ResTable_config* out)
1080{
1081 if (strcmp(name, kWildcardName) == 0) {
1082 if (out) {
1083 out->sdkVersion = out->SDKVERSION_ANY;
1084 out->minorVersion = out->MINORVERSION_ANY;
1085 }
1086 return true;
1087 }
1088
1089 if (*name != 'v') {
1090 return false;
1091 }
1092
1093 name++;
1094 const char* s = name;
1095 while (*s >= '0' && *s <= '9') s++;
1096 if (s == name || *s != 0) return false;
1097 String8 sdkName(name, s-name);
1098
1099 if (out) {
1100 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1101 out->minorVersion = 0;
1102 }
1103
1104 return true;
1105}
1106
1107int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1108{
1109 int v = mcc.compare(o.mcc);
1110 if (v == 0) v = mnc.compare(o.mnc);
1111 if (v == 0) v = locale.compare(o.locale);
1112 if (v == 0) v = vendor.compare(o.vendor);
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001113 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1114 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 if (v == 0) v = orientation.compare(o.orientation);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001116 if (v == 0) v = uiModeType.compare(o.uiModeType);
1117 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 if (v == 0) v = density.compare(o.density);
1119 if (v == 0) v = touchscreen.compare(o.touchscreen);
1120 if (v == 0) v = keysHidden.compare(o.keysHidden);
1121 if (v == 0) v = keyboard.compare(o.keyboard);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001122 if (v == 0) v = navHidden.compare(o.navHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001123 if (v == 0) v = navigation.compare(o.navigation);
1124 if (v == 0) v = screenSize.compare(o.screenSize);
1125 if (v == 0) v = version.compare(o.version);
1126 return v;
1127}
1128
1129ResTable_config AaptGroupEntry::toParams() const
1130{
1131 ResTable_config params;
1132 memset(&params, 0, sizeof(params));
1133 getMccName(mcc.string(), &params);
1134 getMncName(mnc.string(), &params);
1135 getLocaleName(locale.string(), &params);
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001136 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1137 getScreenLayoutLongName(screenLayoutLong.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 getOrientationName(orientation.string(), &params);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001139 getUiModeTypeName(uiModeType.string(), &params);
1140 getUiModeNightName(uiModeNight.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 getDensityName(density.string(), &params);
1142 getTouchscreenName(touchscreen.string(), &params);
1143 getKeysHiddenName(keysHidden.string(), &params);
1144 getKeyboardName(keyboard.string(), &params);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001145 getNavHiddenName(navHidden.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 getNavigationName(navigation.string(), &params);
1147 getScreenSizeName(screenSize.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 getVersionName(version.string(), &params);
Dianne Hackbornef05e072010-03-01 17:43:39 -08001149
1150 // Fix up version number based on specified parameters.
1151 int minSdk = 0;
1152 if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1153 != ResTable_config::UI_MODE_TYPE_ANY
1154 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1155 != ResTable_config::UI_MODE_NIGHT_ANY) {
1156 minSdk = SDK_FROYO;
1157 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1158 != ResTable_config::SCREENSIZE_ANY
1159 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1160 != ResTable_config::SCREENLONG_ANY
1161 || params.density != ResTable_config::DENSITY_DEFAULT) {
1162 minSdk = SDK_DONUT;
1163 }
1164
1165 if (minSdk > params.sdkVersion) {
1166 params.sdkVersion = minSdk;
1167 }
1168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 return params;
1170}
1171
1172// =========================================================================
1173// =========================================================================
1174// =========================================================================
1175
1176void* AaptFile::editData(size_t size)
1177{
1178 if (size <= mBufferSize) {
1179 mDataSize = size;
1180 return mData;
1181 }
1182 size_t allocSize = (size*3)/2;
1183 void* buf = realloc(mData, allocSize);
1184 if (buf == NULL) {
1185 return NULL;
1186 }
1187 mData = buf;
1188 mDataSize = size;
1189 mBufferSize = allocSize;
1190 return buf;
1191}
1192
1193void* AaptFile::editData(size_t* outSize)
1194{
1195 if (outSize) {
1196 *outSize = mDataSize;
1197 }
1198 return mData;
1199}
1200
1201void* AaptFile::padData(size_t wordSize)
1202{
1203 const size_t extra = mDataSize%wordSize;
1204 if (extra == 0) {
1205 return mData;
1206 }
1207
1208 size_t initial = mDataSize;
1209 void* data = editData(initial+(wordSize-extra));
1210 if (data != NULL) {
1211 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1212 }
1213 return data;
1214}
1215
1216status_t AaptFile::writeData(const void* data, size_t size)
1217{
1218 size_t end = mDataSize;
1219 size_t total = size + end;
1220 void* buf = editData(total);
1221 if (buf == NULL) {
1222 return UNKNOWN_ERROR;
1223 }
1224 memcpy(((char*)buf)+end, data, size);
1225 return NO_ERROR;
1226}
1227
1228void AaptFile::clearData()
1229{
1230 if (mData != NULL) free(mData);
1231 mData = NULL;
1232 mDataSize = 0;
1233 mBufferSize = 0;
1234}
1235
1236String8 AaptFile::getPrintableSource() const
1237{
1238 if (hasData()) {
1239 String8 name(mGroupEntry.locale.string());
1240 name.appendPath(mGroupEntry.vendor.string());
1241 name.appendPath(mPath);
1242 name.append(" #generated");
1243 return name;
1244 }
1245 return mSourceFile;
1246}
1247
1248// =========================================================================
1249// =========================================================================
1250// =========================================================================
1251
1252status_t AaptGroup::addFile(const sp<AaptFile>& file)
1253{
1254 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1255 file->mPath = mPath;
1256 mFiles.add(file->getGroupEntry(), file);
1257 return NO_ERROR;
1258 }
1259
1260 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1261 getPrintableSource().string());
1262 return UNKNOWN_ERROR;
1263}
1264
1265void AaptGroup::removeFile(size_t index)
1266{
1267 mFiles.removeItemsAt(index);
1268}
1269
1270void AaptGroup::print() const
1271{
1272 printf(" %s\n", getPath().string());
1273 const size_t N=mFiles.size();
1274 size_t i;
1275 for (i=0; i<N; i++) {
1276 sp<AaptFile> file = mFiles.valueAt(i);
1277 const AaptGroupEntry& e = file->getGroupEntry();
1278 if (file->hasData()) {
1279 printf(" Gen: (%s) %d bytes\n", e.toString().string(),
1280 (int)file->getSize());
1281 } else {
1282 printf(" Src: %s\n", file->getPrintableSource().string());
1283 }
1284 }
1285}
1286
1287String8 AaptGroup::getPrintableSource() const
1288{
1289 if (mFiles.size() > 0) {
1290 // Arbitrarily pull the first source file out of the list.
1291 return mFiles.valueAt(0)->getPrintableSource();
1292 }
1293
1294 // Should never hit this case, but to be safe...
1295 return getPath();
1296
1297}
1298
1299// =========================================================================
1300// =========================================================================
1301// =========================================================================
1302
1303status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1304{
1305 if (mFiles.indexOfKey(name) >= 0) {
1306 return ALREADY_EXISTS;
1307 }
1308 mFiles.add(name, file);
1309 return NO_ERROR;
1310}
1311
1312status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1313{
1314 if (mDirs.indexOfKey(name) >= 0) {
1315 return ALREADY_EXISTS;
1316 }
1317 mDirs.add(name, dir);
1318 return NO_ERROR;
1319}
1320
1321sp<AaptDir> AaptDir::makeDir(const String8& path)
1322{
1323 String8 name;
1324 String8 remain = path;
1325
1326 sp<AaptDir> subdir = this;
1327 while (name = remain.walkPath(&remain), remain != "") {
1328 subdir = subdir->makeDir(name);
1329 }
1330
1331 ssize_t i = subdir->mDirs.indexOfKey(name);
1332 if (i >= 0) {
1333 return subdir->mDirs.valueAt(i);
1334 }
1335 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1336 subdir->mDirs.add(name, dir);
1337 return dir;
1338}
1339
1340void AaptDir::removeFile(const String8& name)
1341{
1342 mFiles.removeItem(name);
1343}
1344
1345void AaptDir::removeDir(const String8& name)
1346{
1347 mDirs.removeItem(name);
1348}
1349
1350status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1351{
1352 sp<AaptGroup> origGroup;
1353
1354 // Find and remove the given file with shear, brute force!
1355 const size_t NG = mFiles.size();
1356 size_t i;
1357 for (i=0; origGroup == NULL && i<NG; i++) {
1358 sp<AaptGroup> g = mFiles.valueAt(i);
1359 const size_t NF = g->getFiles().size();
1360 for (size_t j=0; j<NF; j++) {
1361 if (g->getFiles().valueAt(j) == file) {
1362 origGroup = g;
1363 g->removeFile(j);
1364 if (NF == 1) {
1365 mFiles.removeItemsAt(i);
1366 }
1367 break;
1368 }
1369 }
1370 }
1371
1372 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1373
1374 // Place the file under its new name.
1375 if (origGroup != NULL) {
1376 return addLeafFile(newName, file);
1377 }
1378
1379 return NO_ERROR;
1380}
1381
1382status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1383{
1384 sp<AaptGroup> group;
1385 if (mFiles.indexOfKey(leafName) >= 0) {
1386 group = mFiles.valueFor(leafName);
1387 } else {
1388 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1389 mFiles.add(leafName, group);
1390 }
1391
1392 return group->addFile(file);
1393}
1394
1395ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1396 const AaptGroupEntry& kind, const String8& resType)
1397{
1398 Vector<String8> fileNames;
1399
1400 {
1401 DIR* dir = NULL;
1402
1403 dir = opendir(srcDir.string());
1404 if (dir == NULL) {
1405 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1406 return UNKNOWN_ERROR;
1407 }
1408
1409 /*
1410 * Slurp the filenames out of the directory.
1411 */
1412 while (1) {
1413 struct dirent* entry;
1414
1415 entry = readdir(dir);
1416 if (entry == NULL)
1417 break;
1418
1419 if (isHidden(srcDir.string(), entry->d_name))
1420 continue;
1421
1422 fileNames.add(String8(entry->d_name));
1423 }
1424
1425 closedir(dir);
1426 }
1427
1428 ssize_t count = 0;
1429
1430 /*
1431 * Stash away the files and recursively descend into subdirectories.
1432 */
1433 const size_t N = fileNames.size();
1434 size_t i;
1435 for (i = 0; i < N; i++) {
1436 String8 pathName(srcDir);
1437 FileType type;
1438
1439 pathName.appendPath(fileNames[i].string());
1440 type = getFileType(pathName.string());
1441 if (type == kFileTypeDirectory) {
1442 sp<AaptDir> subdir;
1443 bool notAdded = false;
1444 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1445 subdir = mDirs.valueFor(fileNames[i]);
1446 } else {
1447 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1448 notAdded = true;
1449 }
1450 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1451 resType);
1452 if (res < NO_ERROR) {
1453 return res;
1454 }
1455 if (res > 0 && notAdded) {
1456 mDirs.add(fileNames[i], subdir);
1457 }
1458 count += res;
1459 } else if (type == kFileTypeRegular) {
1460 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1461 status_t err = addLeafFile(fileNames[i], file);
1462 if (err != NO_ERROR) {
1463 return err;
1464 }
1465
1466 count++;
1467
1468 } else {
1469 if (bundle->getVerbose())
1470 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1471 }
1472 }
1473
1474 return count;
1475}
1476
1477status_t AaptDir::validate() const
1478{
1479 const size_t NF = mFiles.size();
1480 const size_t ND = mDirs.size();
1481 size_t i;
1482 for (i = 0; i < NF; i++) {
1483 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1484 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1485 "Invalid filename. Unable to add.");
1486 return UNKNOWN_ERROR;
1487 }
1488
1489 size_t j;
1490 for (j = i+1; j < NF; j++) {
1491 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1492 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1493 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1494 "File is case-insensitive equivalent to: %s",
1495 mFiles.valueAt(j)->getPrintableSource().string());
1496 return UNKNOWN_ERROR;
1497 }
1498
1499 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1500 // (this is mostly caught by the "marked" stuff, below)
1501 }
1502
1503 for (j = 0; j < ND; j++) {
1504 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1505 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1506 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1507 "File conflicts with dir from: %s",
1508 mDirs.valueAt(j)->getPrintableSource().string());
1509 return UNKNOWN_ERROR;
1510 }
1511 }
1512 }
1513
1514 for (i = 0; i < ND; i++) {
1515 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1516 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1517 "Invalid directory name, unable to add.");
1518 return UNKNOWN_ERROR;
1519 }
1520
1521 size_t j;
1522 for (j = i+1; j < ND; j++) {
1523 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1524 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1525 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1526 "Directory is case-insensitive equivalent to: %s",
1527 mDirs.valueAt(j)->getPrintableSource().string());
1528 return UNKNOWN_ERROR;
1529 }
1530 }
1531
1532 status_t err = mDirs.valueAt(i)->validate();
1533 if (err != NO_ERROR) {
1534 return err;
1535 }
1536 }
1537
1538 return NO_ERROR;
1539}
1540
1541void AaptDir::print() const
1542{
1543 const size_t ND=getDirs().size();
1544 size_t i;
1545 for (i=0; i<ND; i++) {
1546 getDirs().valueAt(i)->print();
1547 }
1548
1549 const size_t NF=getFiles().size();
1550 for (i=0; i<NF; i++) {
1551 getFiles().valueAt(i)->print();
1552 }
1553}
1554
1555String8 AaptDir::getPrintableSource() const
1556{
1557 if (mFiles.size() > 0) {
1558 // Arbitrarily pull the first file out of the list as the source dir.
1559 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1560 }
1561 if (mDirs.size() > 0) {
1562 // Or arbitrarily pull the first dir out of the list as the source dir.
1563 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1564 }
1565
1566 // Should never hit this case, but to be safe...
1567 return mPath;
1568
1569}
1570
1571// =========================================================================
1572// =========================================================================
1573// =========================================================================
1574
1575sp<AaptFile> AaptAssets::addFile(
1576 const String8& filePath, const AaptGroupEntry& entry,
1577 const String8& srcDir, sp<AaptGroup>* outGroup,
1578 const String8& resType)
1579{
1580 sp<AaptDir> dir = this;
1581 sp<AaptGroup> group;
1582 sp<AaptFile> file;
1583 String8 root, remain(filePath), partialPath;
1584 while (remain.length() > 0) {
1585 root = remain.walkPath(&remain);
1586 partialPath.appendPath(root);
1587
1588 const String8 rootStr(root);
1589
1590 if (remain.length() == 0) {
1591 ssize_t i = dir->getFiles().indexOfKey(rootStr);
1592 if (i >= 0) {
1593 group = dir->getFiles().valueAt(i);
1594 } else {
1595 group = new AaptGroup(rootStr, filePath);
1596 status_t res = dir->addFile(rootStr, group);
1597 if (res != NO_ERROR) {
1598 return NULL;
1599 }
1600 }
1601 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1602 status_t res = group->addFile(file);
1603 if (res != NO_ERROR) {
1604 return NULL;
1605 }
1606 break;
1607
1608 } else {
1609 ssize_t i = dir->getDirs().indexOfKey(rootStr);
1610 if (i >= 0) {
1611 dir = dir->getDirs().valueAt(i);
1612 } else {
1613 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1614 status_t res = dir->addDir(rootStr, subdir);
1615 if (res != NO_ERROR) {
1616 return NULL;
1617 }
1618 dir = subdir;
1619 }
1620 }
1621 }
1622
1623 mGroupEntries.add(entry);
1624 if (outGroup) *outGroup = group;
1625 return file;
1626}
1627
1628void AaptAssets::addResource(const String8& leafName, const String8& path,
1629 const sp<AaptFile>& file, const String8& resType)
1630{
1631 sp<AaptDir> res = AaptDir::makeDir(kResString);
1632 String8 dirname = file->getGroupEntry().toDirName(resType);
1633 sp<AaptDir> subdir = res->makeDir(dirname);
1634 sp<AaptGroup> grr = new AaptGroup(leafName, path);
1635 grr->addFile(file);
1636
1637 subdir->addFile(leafName, grr);
1638}
1639
1640
1641ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1642{
1643 int count;
1644 int totalCount = 0;
1645 FileType type;
1646 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1647 const size_t dirCount =resDirs.size();
1648 sp<AaptAssets> current = this;
1649
1650 const int N = bundle->getFileSpecCount();
1651
1652 /*
1653 * If a package manifest was specified, include that first.
1654 */
1655 if (bundle->getAndroidManifestFile() != NULL) {
1656 // place at root of zip.
1657 String8 srcFile(bundle->getAndroidManifestFile());
1658 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1659 NULL, String8());
1660 totalCount++;
1661 }
1662
1663 /*
1664 * If a directory of custom assets was supplied, slurp 'em up.
1665 */
1666 if (bundle->getAssetSourceDir()) {
1667 const char* assetDir = bundle->getAssetSourceDir();
1668
1669 FileType type = getFileType(assetDir);
1670 if (type == kFileTypeNonexistent) {
1671 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1672 return UNKNOWN_ERROR;
1673 }
1674 if (type != kFileTypeDirectory) {
1675 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1676 return UNKNOWN_ERROR;
1677 }
1678
1679 String8 assetRoot(assetDir);
1680 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1681 AaptGroupEntry group;
1682 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1683 String8());
1684 if (count < 0) {
1685 totalCount = count;
1686 goto bail;
1687 }
1688 if (count > 0) {
1689 mGroupEntries.add(group);
1690 }
1691 totalCount += count;
1692
1693 if (bundle->getVerbose())
1694 printf("Found %d custom asset file%s in %s\n",
1695 count, (count==1) ? "" : "s", assetDir);
1696 }
1697
1698 /*
1699 * If a directory of resource-specific assets was supplied, slurp 'em up.
1700 */
1701 for (size_t i=0; i<dirCount; i++) {
1702 const char *res = resDirs[i];
1703 if (res) {
1704 type = getFileType(res);
1705 if (type == kFileTypeNonexistent) {
1706 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1707 return UNKNOWN_ERROR;
1708 }
1709 if (type == kFileTypeDirectory) {
1710 if (i>0) {
1711 sp<AaptAssets> nextOverlay = new AaptAssets();
1712 current->setOverlay(nextOverlay);
1713 current = nextOverlay;
1714 }
1715 count = current->slurpResourceTree(bundle, String8(res));
1716
1717 if (count < 0) {
1718 totalCount = count;
1719 goto bail;
1720 }
1721 totalCount += count;
1722 }
1723 else {
1724 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1725 return UNKNOWN_ERROR;
1726 }
1727 }
1728
1729 }
1730 /*
1731 * Now do any additional raw files.
1732 */
1733 for (int arg=0; arg<N; arg++) {
1734 const char* assetDir = bundle->getFileSpecEntry(arg);
1735
1736 FileType type = getFileType(assetDir);
1737 if (type == kFileTypeNonexistent) {
1738 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1739 return UNKNOWN_ERROR;
1740 }
1741 if (type != kFileTypeDirectory) {
1742 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1743 return UNKNOWN_ERROR;
1744 }
1745
1746 String8 assetRoot(assetDir);
1747
1748 if (bundle->getVerbose())
1749 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1750
1751 /*
1752 * Do a recursive traversal of subdir tree. We don't make any
1753 * guarantees about ordering, so we're okay with an inorder search
1754 * using whatever order the OS happens to hand back to us.
1755 */
1756 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
1757 if (count < 0) {
1758 /* failure; report error and remove archive */
1759 totalCount = count;
1760 goto bail;
1761 }
1762 totalCount += count;
1763
1764 if (bundle->getVerbose())
1765 printf("Found %d asset file%s in %s\n",
1766 count, (count==1) ? "" : "s", assetDir);
1767 }
1768
1769 count = validate();
1770 if (count != NO_ERROR) {
1771 totalCount = count;
1772 goto bail;
1773 }
1774
1775
1776bail:
1777 return totalCount;
1778}
1779
1780ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1781 const AaptGroupEntry& kind,
1782 const String8& resType)
1783{
1784 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
1785 if (res > 0) {
1786 mGroupEntries.add(kind);
1787 }
1788
1789 return res;
1790}
1791
1792ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1793{
1794 ssize_t err = 0;
1795
1796 DIR* dir = opendir(srcDir.string());
1797 if (dir == NULL) {
1798 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1799 return UNKNOWN_ERROR;
1800 }
1801
1802 status_t count = 0;
1803
1804 /*
1805 * Run through the directory, looking for dirs that match the
1806 * expected pattern.
1807 */
1808 while (1) {
1809 struct dirent* entry = readdir(dir);
1810 if (entry == NULL) {
1811 break;
1812 }
1813
1814 if (isHidden(srcDir.string(), entry->d_name)) {
1815 continue;
1816 }
1817
1818 String8 subdirName(srcDir);
1819 subdirName.appendPath(entry->d_name);
1820
1821 AaptGroupEntry group;
1822 String8 resType;
1823 bool b = group.initFromDirName(entry->d_name, &resType);
1824 if (!b) {
1825 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
1826 entry->d_name);
1827 err = -1;
1828 continue;
1829 }
1830
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07001831 if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
1832 int maxResInt = atoi(bundle->getMaxResVersion());
1833 const char *verString = group.version.string();
1834 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1835 if (dirVersionInt > maxResInt) {
1836 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1837 continue;
1838 }
1839 }
1840
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 FileType type = getFileType(subdirName.string());
1842
1843 if (type == kFileTypeDirectory) {
1844 sp<AaptDir> dir = makeDir(String8(entry->d_name));
1845 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1846 resType);
1847 if (res < 0) {
1848 count = res;
1849 goto bail;
1850 }
1851 if (res > 0) {
1852 mGroupEntries.add(group);
1853 count += res;
1854 }
1855
1856 mDirs.add(dir);
1857 } else {
1858 if (bundle->getVerbose()) {
1859 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
1860 }
1861 }
1862 }
1863
1864bail:
1865 closedir(dir);
1866 dir = NULL;
1867
1868 if (err != 0) {
1869 return err;
1870 }
1871 return count;
1872}
1873
1874ssize_t
1875AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
1876{
1877 int count = 0;
1878 SortedVector<AaptGroupEntry> entries;
1879
1880 ZipFile* zip = new ZipFile;
1881 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1882 if (err != NO_ERROR) {
1883 fprintf(stderr, "error opening zip file %s\n", filename);
1884 count = err;
1885 delete zip;
1886 return -1;
1887 }
1888
1889 const int N = zip->getNumEntries();
1890 for (int i=0; i<N; i++) {
1891 ZipEntry* entry = zip->getEntryByIndex(i);
1892 if (entry->getDeleted()) {
1893 continue;
1894 }
1895
1896 String8 entryName(entry->getFileName());
1897
1898 String8 dirName = entryName.getPathDir();
1899 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1900
1901 String8 resType;
1902 AaptGroupEntry kind;
1903
1904 String8 remain;
1905 if (entryName.walkPath(&remain) == kResourceDir) {
1906 // these are the resources, pull their type out of the directory name
1907 kind.initFromDirName(remain.walkPath().string(), &resType);
1908 } else {
1909 // these are untyped and don't have an AaptGroupEntry
1910 }
1911 if (entries.indexOf(kind) < 0) {
1912 entries.add(kind);
1913 mGroupEntries.add(kind);
1914 }
1915
1916 // use the one from the zip file if they both exist.
1917 dir->removeFile(entryName.getPathLeaf());
1918
1919 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1920 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1921 if (err != NO_ERROR) {
1922 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1923 count = err;
1924 goto bail;
1925 }
1926 file->setCompressionMethod(entry->getCompressionMethod());
1927
1928#if 0
1929 if (entryName == "AndroidManifest.xml") {
1930 printf("AndroidManifest.xml\n");
1931 }
1932 printf("\n\nfile: %s\n", entryName.string());
1933#endif
1934
1935 size_t len = entry->getUncompressedLen();
1936 void* data = zip->uncompress(entry);
1937 void* buf = file->editData(len);
1938 memcpy(buf, data, len);
1939
1940#if 0
1941 const int OFF = 0;
1942 const unsigned char* p = (unsigned char*)data;
1943 const unsigned char* end = p+len;
1944 p += OFF;
1945 for (int i=0; i<32 && p < end; i++) {
1946 printf("0x%03x ", i*0x10 + OFF);
1947 for (int j=0; j<0x10 && p < end; j++) {
1948 printf(" %02x", *p);
1949 p++;
1950 }
1951 printf("\n");
1952 }
1953#endif
1954
1955 free(data);
1956
1957 count++;
1958 }
1959
1960bail:
1961 delete zip;
1962 return count;
1963}
1964
1965sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1966{
1967 sp<AaptSymbols> sym = mSymbols.valueFor(name);
1968 if (sym == NULL) {
1969 sym = new AaptSymbols();
1970 mSymbols.add(name, sym);
1971 }
1972 return sym;
1973}
1974
1975status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1976{
1977 if (!mHaveIncludedAssets) {
1978 // Add in all includes.
1979 const Vector<const char*>& incl = bundle->getPackageIncludes();
1980 const size_t N=incl.size();
1981 for (size_t i=0; i<N; i++) {
1982 if (bundle->getVerbose())
1983 printf("Including resources from package: %s\n", incl[i]);
1984 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
1985 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1986 incl[i]);
1987 return UNKNOWN_ERROR;
1988 }
1989 }
1990 mHaveIncludedAssets = true;
1991 }
1992
1993 return NO_ERROR;
1994}
1995
1996status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1997{
1998 const ResTable& res = getIncludedResources();
1999 // XXX dirty!
2000 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2001}
2002
2003const ResTable& AaptAssets::getIncludedResources() const
2004{
2005 return mIncludedAssets.getResources(false);
2006}
2007
2008void AaptAssets::print() const
2009{
2010 printf("Locale/Vendor pairs:\n");
2011 const size_t N=mGroupEntries.size();
2012 for (size_t i=0; i<N; i++) {
2013 printf(" %s/%s\n",
2014 mGroupEntries.itemAt(i).locale.string(),
2015 mGroupEntries.itemAt(i).vendor.string());
2016 }
2017
2018 printf("\nFiles:\n");
2019 AaptDir::print();
2020}
2021
Joe Onorato1553c822009-08-30 13:36:22 -07002022sp<AaptDir> AaptAssets::resDir(const String8& name)
2023{
2024 const Vector<sp<AaptDir> >& dirs = mDirs;
2025 const size_t N = dirs.size();
2026 for (size_t i=0; i<N; i++) {
2027 const sp<AaptDir>& d = dirs.itemAt(i);
2028 if (d->getLeaf() == name) {
2029 return d;
2030 }
2031 }
2032 return NULL;
2033}
2034
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002035bool
2036valid_symbol_name(const String8& symbol)
2037{
2038 static char const * const KEYWORDS[] = {
2039 "abstract", "assert", "boolean", "break",
2040 "byte", "case", "catch", "char", "class", "const", "continue",
2041 "default", "do", "double", "else", "enum", "extends", "final",
2042 "finally", "float", "for", "goto", "if", "implements", "import",
2043 "instanceof", "int", "interface", "long", "native", "new", "package",
2044 "private", "protected", "public", "return", "short", "static",
2045 "strictfp", "super", "switch", "synchronized", "this", "throw",
2046 "throws", "transient", "try", "void", "volatile", "while",
2047 "true", "false", "null",
2048 NULL
2049 };
2050 const char*const* k = KEYWORDS;
2051 const char*const s = symbol.string();
2052 while (*k) {
2053 if (0 == strcmp(s, *k)) {
2054 return false;
2055 }
2056 k++;
2057 }
2058 return true;
2059}