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