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