blob: 6bc1ee6cbfbc5bd67197fefbec19deeca3b4d6e3 [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001//
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 = "/\\:";
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070020static 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{
58 const char *type = NULL;
59
60 // Skip all hidden files.
61 if (path[0] == '.') {
62 // Skip ., .. and .svn but don't chatter about it.
63 if (strcmp(path, ".") == 0
64 || strcmp(path, "..") == 0
65 || strcmp(path, ".svn") == 0) {
66 return true;
67 }
68 type = "hidden";
69 } else if (path[0] == '_') {
70 // skip directories starting with _ (don't chatter about it)
71 String8 subdirName(root);
72 subdirName.appendPath(path);
73 if (getFileType(subdirName.string()) == kFileTypeDirectory) {
74 return true;
75 }
76 } else if (strcmp(path, "CVS") == 0) {
77 // Skip CVS but don't chatter about it.
78 return true;
79 } else if (strcasecmp(path, "thumbs.db") == 0
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080080 || strcasecmp(path, "picasa.ini") == 0) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070081 // Skip suspected image indexes files.
82 type = "index";
83 } else if (path[strlen(path)-1] == '~') {
84 // Skip suspected emacs backup files.
85 type = "backup";
86 } else {
87 // Let everything else through.
88 return false;
89 }
90
91 /* If we get this far, "type" should be set and the file
92 * should be skipped.
93 */
94 String8 subdirName(root);
95 subdirName.appendPath(path);
96 fprintf(stderr, " (skipping %s %s '%s')\n", type,
97 getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
98 subdirName.string());
99
100 return true;
101}
The Android Open Source Project9266c552009-01-15 16:12:10 -0800102
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700103// =========================================================================
104// =========================================================================
105// =========================================================================
106
107status_t
108AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
109{
110 ResTable_config config;
111
112 // IMSI - MCC
113 if (getMccName(part.string(), &config)) {
114 *axis = AXIS_MCC;
115 *value = config.mcc;
116 return 0;
117 }
118
119 // IMSI - MNC
120 if (getMncName(part.string(), &config)) {
121 *axis = AXIS_MNC;
122 *value = config.mnc;
123 return 0;
124 }
125
126 // locale - language
127 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
128 *axis = AXIS_LANGUAGE;
129 *value = part[1] << 8 | part[0];
130 return 0;
131 }
132
133 // locale - language_REGION
134 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
135 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
136 *axis = AXIS_LANGUAGE;
137 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
138 return 0;
139 }
140
141 // orientation
142 if (getOrientationName(part.string(), &config)) {
143 *axis = AXIS_ORIENTATION;
144 *value = config.orientation;
145 return 0;
146 }
147
148 // density
149 if (getDensityName(part.string(), &config)) {
150 *axis = AXIS_DENSITY;
151 *value = config.density;
152 return 0;
153 }
154
155 // touchscreen
156 if (getTouchscreenName(part.string(), &config)) {
157 *axis = AXIS_TOUCHSCREEN;
158 *value = config.touchscreen;
159 return 0;
160 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800161
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700162 // keyboard hidden
163 if (getKeysHiddenName(part.string(), &config)) {
164 *axis = AXIS_KEYSHIDDEN;
165 *value = config.inputFlags;
166 return 0;
167 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800168
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700169 // keyboard
170 if (getKeyboardName(part.string(), &config)) {
171 *axis = AXIS_KEYBOARD;
172 *value = config.keyboard;
173 return 0;
174 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800175
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700176 // navigation
177 if (getNavigationName(part.string(), &config)) {
178 *axis = AXIS_NAVIGATION;
179 *value = config.navigation;
180 return 0;
181 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800182
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700183 // screen size
184 if (getScreenSizeName(part.string(), &config)) {
185 *axis = AXIS_SCREENSIZE;
186 *value = config.screenSize;
187 return 0;
188 }
189
190 // version
191 if (getVersionName(part.string(), &config)) {
192 *axis = AXIS_VERSION;
193 *value = config.version;
194 return 0;
195 }
196
197 return 1;
198}
199
200bool
201AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
202{
203 Vector<String8> parts;
204
205 String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, vers;
206
207 const char *p = dir;
208 const char *q;
209 while (NULL != (q = strchr(p, '-'))) {
210 String8 val(p, q-p);
211 val.toLower();
212 parts.add(val);
213 //printf("part: %s\n", parts[parts.size()-1].string());
214 p = q+1;
215 }
216 String8 val(p);
217 val.toLower();
218 parts.add(val);
219 //printf("part: %s\n", parts[parts.size()-1].string());
220
221 const int N = parts.size();
222 int index = 0;
223 String8 part = parts[index];
224
225 // resource type
226 if (!isValidResourceType(part)) {
227 return false;
228 }
229 *resType = part;
230
231 index++;
232 if (index == N) {
233 goto success;
234 }
235 part = parts[index];
236
237 // imsi - mcc
238 if (getMccName(part.string())) {
239 mcc = part;
240
241 index++;
242 if (index == N) {
243 goto success;
244 }
245 part = parts[index];
246 } else {
247 //printf("not mcc: %s\n", part.string());
248 }
249
250 // imsi - mnc
251 if (getMncName(part.string())) {
252 mnc = part;
253
254 index++;
255 if (index == N) {
256 goto success;
257 }
258 part = parts[index];
259 } else {
260 //printf("not mcc: %s\n", part.string());
261 }
262
263 // locale - language
264 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
265 loc = part;
266
267 index++;
268 if (index == N) {
269 goto success;
270 }
271 part = parts[index];
272 } else {
273 //printf("not language: %s\n", part.string());
274 }
275
276 // locale - region
277 if (loc.length() > 0
278 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
279 loc += "-";
280 part.toUpper();
281 loc += part.string() + 1;
282
283 index++;
284 if (index == N) {
285 goto success;
286 }
287 part = parts[index];
288 } else {
289 //printf("not region: %s\n", part.string());
290 }
291
292 // orientation
293 if (getOrientationName(part.string())) {
294 orient = part;
295
296 index++;
297 if (index == N) {
298 goto success;
299 }
300 part = parts[index];
301 } else {
302 //printf("not orientation: %s\n", part.string());
303 }
304
305 // density
306 if (getDensityName(part.string())) {
307 den = part;
308
309 index++;
310 if (index == N) {
311 goto success;
312 }
313 part = parts[index];
314 } else {
315 //printf("not density: %s\n", part.string());
316 }
317
318 // touchscreen
319 if (getTouchscreenName(part.string())) {
320 touch = part;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800321
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700322 index++;
323 if (index == N) {
324 goto success;
325 }
326 part = parts[index];
327 } else {
328 //printf("not touchscreen: %s\n", part.string());
329 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800330
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700331 // keyboard hidden
332 if (getKeysHiddenName(part.string())) {
333 keysHidden = part;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800334
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700335 index++;
336 if (index == N) {
337 goto success;
338 }
339 part = parts[index];
340 } else {
341 //printf("not keysHidden: %s\n", part.string());
342 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800343
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700344 // keyboard
345 if (getKeyboardName(part.string())) {
346 key = part;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800347
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700348 index++;
349 if (index == N) {
350 goto success;
351 }
352 part = parts[index];
353 } else {
354 //printf("not keyboard: %s\n", part.string());
355 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800356
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700357 if (getNavigationName(part.string())) {
358 nav = part;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800359
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700360 index++;
361 if (index == N) {
362 goto success;
363 }
364 part = parts[index];
365 } else {
366 //printf("not navigation: %s\n", part.string());
367 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800368
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700369 if (getScreenSizeName(part.string())) {
370 size = part;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800371
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700372 index++;
373 if (index == N) {
374 goto success;
375 }
376 part = parts[index];
377 } else {
378 //printf("not screen size: %s\n", part.string());
379 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800380
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700381 if (getVersionName(part.string())) {
382 vers = part;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800383
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700384 index++;
385 if (index == N) {
386 goto success;
387 }
388 part = parts[index];
389 } else {
390 //printf("not version: %s\n", part.string());
391 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800392
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700393 // if there are extra parts, it doesn't match
394 return false;
395
396success:
397 this->mcc = mcc;
398 this->mnc = mnc;
399 this->locale = loc;
400 this->orientation = orient;
401 this->density = den;
402 this->touchscreen = touch;
403 this->keysHidden = keysHidden;
404 this->keyboard = key;
405 this->navigation = nav;
406 this->screenSize = size;
407 this->version = vers;
408
409 // what is this anyway?
410 this->vendor = "";
411
412 return true;
413}
414
415String8
416AaptGroupEntry::toString() const
417{
418 String8 s = this->mcc;
419 s += ",";
420 s += this->mnc;
421 s += ",";
422 s += this->locale;
423 s += ",";
424 s += this->orientation;
425 s += ",";
426 s += density;
427 s += ",";
428 s += touchscreen;
429 s += ",";
430 s += keysHidden;
431 s += ",";
432 s += keyboard;
433 s += ",";
434 s += navigation;
435 s += ",";
436 s += screenSize;
437 s += ",";
438 s += version;
439 return s;
440}
441
442String8
443AaptGroupEntry::toDirName(const String8& resType) const
444{
445 String8 s = resType;
446 if (this->mcc != "") {
447 s += "-";
448 s += mcc;
449 }
450 if (this->mnc != "") {
451 s += "-";
452 s += mnc;
453 }
454 if (this->locale != "") {
455 s += "-";
456 s += locale;
457 }
458 if (this->orientation != "") {
459 s += "-";
460 s += orientation;
461 }
462 if (this->density != "") {
463 s += "-";
464 s += density;
465 }
466 if (this->touchscreen != "") {
467 s += "-";
468 s += touchscreen;
469 }
470 if (this->keysHidden != "") {
471 s += "-";
472 s += keysHidden;
473 }
474 if (this->keyboard != "") {
475 s += "-";
476 s += keyboard;
477 }
478 if (this->navigation != "") {
479 s += "-";
480 s += navigation;
481 }
482 if (this->screenSize != "") {
483 s += "-";
484 s += screenSize;
485 }
486 if (this->version != "") {
487 s += "-";
488 s += version;
489 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800490
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700491 return s;
492}
493
494bool AaptGroupEntry::getMccName(const char* name,
495 ResTable_config* out)
496{
497 if (strcmp(name, kWildcardName) == 0) {
498 if (out) out->mcc = 0;
499 return true;
500 }
501 const char* c = name;
502 if (tolower(*c) != 'm') return false;
503 c++;
504 if (tolower(*c) != 'c') return false;
505 c++;
506 if (tolower(*c) != 'c') return false;
507 c++;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800508
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700509 const char* val = c;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800510
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700511 while (*c >= '0' && *c <= '9') {
512 c++;
513 }
514 if (*c != 0) return false;
515 if (c-val != 3) return false;
516
517 int d = atoi(val);
518 if (d != 0) {
519 if (out) out->mcc = d;
520 return true;
521 }
522
523 return false;
524}
525
526bool AaptGroupEntry::getMncName(const char* name,
527 ResTable_config* out)
528{
529 if (strcmp(name, kWildcardName) == 0) {
530 if (out) out->mcc = 0;
531 return true;
532 }
533 const char* c = name;
534 if (tolower(*c) != 'm') return false;
535 c++;
536 if (tolower(*c) != 'n') return false;
537 c++;
538 if (tolower(*c) != 'c') return false;
539 c++;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800540
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700541 const char* val = c;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800542
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700543 while (*c >= '0' && *c <= '9') {
544 c++;
545 }
546 if (*c != 0) return false;
547 if (c-val == 0 || c-val > 3) return false;
548
549 int d = atoi(val);
550 if (d != 0) {
551 if (out) out->mnc = d;
552 return true;
553 }
554
555 return false;
556}
557
558/*
559 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
560 * "default")?
561 *
562 * TODO: Should insist that the first two letters are lower case, and the
563 * second two are upper.
564 */
565bool AaptGroupEntry::getLocaleName(const char* fileName,
566 ResTable_config* out)
567{
568 if (strcmp(fileName, kWildcardName) == 0
569 || strcmp(fileName, kDefaultLocale) == 0) {
570 if (out) {
571 out->language[0] = 0;
572 out->language[1] = 0;
573 out->country[0] = 0;
574 out->country[1] = 0;
575 }
576 return true;
577 }
578
579 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
580 if (out) {
581 out->language[0] = fileName[0];
582 out->language[1] = fileName[1];
583 out->country[0] = 0;
584 out->country[1] = 0;
585 }
586 return true;
587 }
588
589 if (strlen(fileName) == 5 &&
590 isalpha(fileName[0]) &&
591 isalpha(fileName[1]) &&
592 fileName[2] == '-' &&
593 isalpha(fileName[3]) &&
594 isalpha(fileName[4])) {
595 if (out) {
596 out->language[0] = fileName[0];
597 out->language[1] = fileName[1];
598 out->country[0] = fileName[3];
599 out->country[1] = fileName[4];
600 }
601 return true;
602 }
603
604 return false;
605}
606
607bool AaptGroupEntry::getOrientationName(const char* name,
608 ResTable_config* out)
609{
610 if (strcmp(name, kWildcardName) == 0) {
611 if (out) out->orientation = out->ORIENTATION_ANY;
612 return true;
613 } else if (strcmp(name, "port") == 0) {
614 if (out) out->orientation = out->ORIENTATION_PORT;
615 return true;
616 } else if (strcmp(name, "land") == 0) {
617 if (out) out->orientation = out->ORIENTATION_LAND;
618 return true;
619 } else if (strcmp(name, "square") == 0) {
620 if (out) out->orientation = out->ORIENTATION_SQUARE;
621 return true;
622 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800623
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700624 return false;
625}
626
627bool AaptGroupEntry::getDensityName(const char* name,
628 ResTable_config* out)
629{
630 if (strcmp(name, kWildcardName) == 0) {
631 if (out) out->density = 0;
632 return true;
633 }
634 char* c = (char*)name;
635 while (*c >= '0' && *c <= '9') {
636 c++;
637 }
638
639 // check that we have 'dpi' after the last digit.
640 if (toupper(c[0]) != 'D' ||
641 toupper(c[1]) != 'P' ||
642 toupper(c[2]) != 'I' ||
643 c[3] != 0) {
644 return false;
645 }
646
647 // temporarily replace the first letter with \0 to
648 // use atoi.
649 char tmp = c[0];
650 c[0] = '\0';
651
652 int d = atoi(name);
653 c[0] = tmp;
654
655 if (d != 0) {
656 if (out) out->density = d;
657 return true;
658 }
659
660 return false;
661}
662
663bool AaptGroupEntry::getTouchscreenName(const char* name,
664 ResTable_config* out)
665{
666 if (strcmp(name, kWildcardName) == 0) {
667 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
668 return true;
669 } else if (strcmp(name, "notouch") == 0) {
670 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
671 return true;
672 } else if (strcmp(name, "stylus") == 0) {
673 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
674 return true;
675 } else if (strcmp(name, "finger") == 0) {
676 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
677 return true;
678 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800679
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700680 return false;
681}
682
683bool AaptGroupEntry::getKeysHiddenName(const char* name,
684 ResTable_config* out)
685{
686 uint8_t mask = 0;
687 uint8_t value = 0;
688 if (strcmp(name, kWildcardName) == 0) {
689 mask = out->MASK_KEYSHIDDEN;
690 value = out->KEYSHIDDEN_ANY;
691 } else if (strcmp(name, "keysexposed") == 0) {
692 mask = out->MASK_KEYSHIDDEN;
693 value = out->KEYSHIDDEN_NO;
694 } else if (strcmp(name, "keyshidden") == 0) {
695 mask = out->MASK_KEYSHIDDEN;
696 value = out->KEYSHIDDEN_YES;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800697 } else if (strcmp(name, "keyssoft") == 0) {
698 mask = out->MASK_KEYSHIDDEN;
699 value = out->KEYSHIDDEN_SOFT;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700700 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800701
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700702 if (mask != 0) {
703 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
704 return true;
705 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800706
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700707 return false;
708}
709
710bool AaptGroupEntry::getKeyboardName(const char* name,
711 ResTable_config* out)
712{
713 if (strcmp(name, kWildcardName) == 0) {
714 if (out) out->keyboard = out->KEYBOARD_ANY;
715 return true;
716 } else if (strcmp(name, "nokeys") == 0) {
717 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
718 return true;
719 } else if (strcmp(name, "qwerty") == 0) {
720 if (out) out->keyboard = out->KEYBOARD_QWERTY;
721 return true;
722 } else if (strcmp(name, "12key") == 0) {
723 if (out) out->keyboard = out->KEYBOARD_12KEY;
724 return true;
725 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800726
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700727 return false;
728}
729
730bool AaptGroupEntry::getNavigationName(const char* name,
731 ResTable_config* out)
732{
733 if (strcmp(name, kWildcardName) == 0) {
734 if (out) out->navigation = out->NAVIGATION_ANY;
735 return true;
736 } else if (strcmp(name, "nonav") == 0) {
737 if (out) out->navigation = out->NAVIGATION_NONAV;
738 return true;
739 } else if (strcmp(name, "dpad") == 0) {
740 if (out) out->navigation = out->NAVIGATION_DPAD;
741 return true;
742 } else if (strcmp(name, "trackball") == 0) {
743 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
744 return true;
745 } else if (strcmp(name, "wheel") == 0) {
746 if (out) out->navigation = out->NAVIGATION_WHEEL;
747 return true;
748 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800749
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700750 return false;
751}
752
753bool AaptGroupEntry::getScreenSizeName(const char* name,
754 ResTable_config* out)
755{
756 if (strcmp(name, kWildcardName) == 0) {
757 if (out) {
758 out->screenWidth = out->SCREENWIDTH_ANY;
759 out->screenHeight = out->SCREENHEIGHT_ANY;
760 }
761 return true;
762 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800763
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700764 const char* x = name;
765 while (*x >= '0' && *x <= '9') x++;
766 if (x == name || *x != 'x') return false;
767 String8 xName(name, x-name);
768 x++;
The Android Open Source Project9266c552009-01-15 16:12:10 -0800769
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700770 const char* y = x;
771 while (*y >= '0' && *y <= '9') y++;
772 if (y == name || *y != 0) return false;
773 String8 yName(x, y-x);
The Android Open Source Project9266c552009-01-15 16:12:10 -0800774
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700775 uint16_t w = (uint16_t)atoi(xName.string());
776 uint16_t h = (uint16_t)atoi(yName.string());
777 if (w < h) {
778 return false;
779 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800780
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700781 if (out) {
782 out->screenWidth = w;
783 out->screenHeight = h;
784 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800785
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700786 return true;
787}
788
789bool AaptGroupEntry::getVersionName(const char* name,
790 ResTable_config* out)
791{
792 if (strcmp(name, kWildcardName) == 0) {
793 if (out) {
794 out->sdkVersion = out->SDKVERSION_ANY;
795 out->minorVersion = out->MINORVERSION_ANY;
796 }
797 return true;
798 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800799
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700800 if (*name != 'v') {
801 return false;
802 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800803
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700804 name++;
805 const char* s = name;
806 while (*s >= '0' && *s <= '9') s++;
807 if (s == name || *s != 0) return false;
808 String8 sdkName(name, s-name);
The Android Open Source Project9266c552009-01-15 16:12:10 -0800809
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700810 if (out) {
811 out->sdkVersion = (uint16_t)atoi(sdkName.string());
812 out->minorVersion = 0;
813 }
The Android Open Source Project9266c552009-01-15 16:12:10 -0800814
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700815 return true;
816}
817
818int AaptGroupEntry::compare(const AaptGroupEntry& o) const
819{
820 int v = mcc.compare(o.mcc);
821 if (v == 0) v = mnc.compare(o.mnc);
822 if (v == 0) v = locale.compare(o.locale);
823 if (v == 0) v = vendor.compare(o.vendor);
824 if (v == 0) v = orientation.compare(o.orientation);
825 if (v == 0) v = density.compare(o.density);
826 if (v == 0) v = touchscreen.compare(o.touchscreen);
827 if (v == 0) v = keysHidden.compare(o.keysHidden);
828 if (v == 0) v = keyboard.compare(o.keyboard);
829 if (v == 0) v = navigation.compare(o.navigation);
830 if (v == 0) v = screenSize.compare(o.screenSize);
831 if (v == 0) v = version.compare(o.version);
832 return v;
833}
834
835ResTable_config AaptGroupEntry::toParams() const
836{
837 ResTable_config params;
838 memset(&params, 0, sizeof(params));
839 getMccName(mcc.string(), &params);
840 getMncName(mnc.string(), &params);
841 getLocaleName(locale.string(), &params);
842 getOrientationName(orientation.string(), &params);
843 getDensityName(density.string(), &params);
844 getTouchscreenName(touchscreen.string(), &params);
845 getKeysHiddenName(keysHidden.string(), &params);
846 getKeyboardName(keyboard.string(), &params);
847 getNavigationName(navigation.string(), &params);
848 getScreenSizeName(screenSize.string(), &params);
849 getVersionName(version.string(), &params);
850 return params;
851}
852
853// =========================================================================
854// =========================================================================
855// =========================================================================
856
857void* AaptFile::editData(size_t size)
858{
859 if (size <= mBufferSize) {
860 mDataSize = size;
861 return mData;
862 }
863 size_t allocSize = (size*3)/2;
864 void* buf = realloc(mData, allocSize);
865 if (buf == NULL) {
866 return NULL;
867 }
868 mData = buf;
869 mDataSize = size;
870 mBufferSize = allocSize;
871 return buf;
872}
873
874void* AaptFile::editData(size_t* outSize)
875{
876 if (outSize) {
877 *outSize = mDataSize;
878 }
879 return mData;
880}
881
882void* AaptFile::padData(size_t wordSize)
883{
884 const size_t extra = mDataSize%wordSize;
885 if (extra == 0) {
886 return mData;
887 }
888
889 size_t initial = mDataSize;
890 void* data = editData(initial+(wordSize-extra));
891 if (data != NULL) {
892 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
893 }
894 return data;
895}
896
897status_t AaptFile::writeData(const void* data, size_t size)
898{
899 size_t end = mDataSize;
900 size_t total = size + end;
901 void* buf = editData(total);
902 if (buf == NULL) {
903 return UNKNOWN_ERROR;
904 }
905 memcpy(((char*)buf)+end, data, size);
906 return NO_ERROR;
907}
908
909void AaptFile::clearData()
910{
911 if (mData != NULL) free(mData);
912 mData = NULL;
913 mDataSize = 0;
914 mBufferSize = 0;
915}
916
917String8 AaptFile::getPrintableSource() const
918{
919 if (hasData()) {
920 String8 name(mGroupEntry.locale.string());
921 name.appendPath(mGroupEntry.vendor.string());
922 name.appendPath(mPath);
923 name.append(" #generated");
924 return name;
925 }
926 return mSourceFile;
927}
928
929// =========================================================================
930// =========================================================================
931// =========================================================================
932
933status_t AaptGroup::addFile(const sp<AaptFile>& file)
934{
935 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
936 file->mPath = mPath;
937 mFiles.add(file->getGroupEntry(), file);
938 return NO_ERROR;
939 }
940
941 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
942 getPrintableSource().string());
943 return UNKNOWN_ERROR;
944}
945
946void AaptGroup::removeFile(size_t index)
947{
948 mFiles.removeItemsAt(index);
949}
950
951void AaptGroup::print() const
952{
953 printf(" %s\n", getPath().string());
954 const size_t N=mFiles.size();
955 size_t i;
956 for (i=0; i<N; i++) {
957 sp<AaptFile> file = mFiles.valueAt(i);
958 const AaptGroupEntry& e = file->getGroupEntry();
959 if (file->hasData()) {
960 printf(" Gen: (%s) %d bytes\n", e.toString().string(),
961 (int)file->getSize());
962 } else {
963 printf(" Src: %s\n", file->getPrintableSource().string());
964 }
965 }
966}
967
968String8 AaptGroup::getPrintableSource() const
969{
970 if (mFiles.size() > 0) {
971 // Arbitrarily pull the first source file out of the list.
972 return mFiles.valueAt(0)->getPrintableSource();
973 }
974
975 // Should never hit this case, but to be safe...
976 return getPath();
977
978}
979
980// =========================================================================
981// =========================================================================
982// =========================================================================
983
984status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
985{
986 if (mFiles.indexOfKey(name) >= 0) {
987 return ALREADY_EXISTS;
988 }
989 mFiles.add(name, file);
990 return NO_ERROR;
991}
992
993status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
994{
995 if (mDirs.indexOfKey(name) >= 0) {
996 return ALREADY_EXISTS;
997 }
998 mDirs.add(name, dir);
999 return NO_ERROR;
1000}
1001
1002sp<AaptDir> AaptDir::makeDir(const String8& path)
1003{
1004 String8 name;
1005 String8 remain = path;
1006
1007 sp<AaptDir> subdir = this;
1008 while (name = remain.walkPath(&remain), remain != "") {
1009 subdir = subdir->makeDir(name);
1010 }
1011
1012 ssize_t i = subdir->mDirs.indexOfKey(name);
1013 if (i >= 0) {
1014 return subdir->mDirs.valueAt(i);
1015 }
1016 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1017 subdir->mDirs.add(name, dir);
1018 return dir;
1019}
1020
1021void AaptDir::removeFile(const String8& name)
1022{
1023 mFiles.removeItem(name);
1024}
1025
1026void AaptDir::removeDir(const String8& name)
1027{
1028 mDirs.removeItem(name);
1029}
1030
1031status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1032{
1033 sp<AaptGroup> origGroup;
1034
1035 // Find and remove the given file with shear, brute force!
1036 const size_t NG = mFiles.size();
1037 size_t i;
1038 for (i=0; origGroup == NULL && i<NG; i++) {
1039 sp<AaptGroup> g = mFiles.valueAt(i);
1040 const size_t NF = g->getFiles().size();
1041 for (size_t j=0; j<NF; j++) {
1042 if (g->getFiles().valueAt(j) == file) {
1043 origGroup = g;
1044 g->removeFile(j);
1045 if (NF == 1) {
1046 mFiles.removeItemsAt(i);
1047 }
1048 break;
1049 }
1050 }
1051 }
1052
1053 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1054
1055 // Place the file under its new name.
1056 if (origGroup != NULL) {
1057 return addLeafFile(newName, file);
1058 }
1059
1060 return NO_ERROR;
1061}
1062
1063status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1064{
1065 sp<AaptGroup> group;
1066 if (mFiles.indexOfKey(leafName) >= 0) {
1067 group = mFiles.valueFor(leafName);
1068 } else {
1069 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1070 mFiles.add(leafName, group);
1071 }
1072
1073 return group->addFile(file);
1074}
1075
1076ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1077 const AaptGroupEntry& kind, const String8& resType)
1078{
1079 Vector<String8> fileNames;
1080
1081 {
1082 DIR* dir = NULL;
The Android Open Source Project9266c552009-01-15 16:12:10 -08001083
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001084 dir = opendir(srcDir.string());
1085 if (dir == NULL) {
1086 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1087 return UNKNOWN_ERROR;
1088 }
The Android Open Source Project9266c552009-01-15 16:12:10 -08001089
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001090 /*
1091 * Slurp the filenames out of the directory.
1092 */
1093 while (1) {
1094 struct dirent* entry;
The Android Open Source Project9266c552009-01-15 16:12:10 -08001095
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001096 entry = readdir(dir);
1097 if (entry == NULL)
1098 break;
The Android Open Source Project9266c552009-01-15 16:12:10 -08001099
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001100 if (isHidden(srcDir.string(), entry->d_name))
1101 continue;
1102
1103 fileNames.add(String8(entry->d_name));
1104 }
The Android Open Source Project9266c552009-01-15 16:12:10 -08001105
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001106 closedir(dir);
1107 }
1108
1109 ssize_t count = 0;
1110
1111 /*
1112 * Stash away the files and recursively descend into subdirectories.
1113 */
1114 const size_t N = fileNames.size();
1115 size_t i;
1116 for (i = 0; i < N; i++) {
1117 String8 pathName(srcDir);
1118 FileType type;
1119
1120 pathName.appendPath(fileNames[i].string());
1121 type = getFileType(pathName.string());
1122 if (type == kFileTypeDirectory) {
1123 sp<AaptDir> subdir;
1124 bool notAdded = false;
1125 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1126 subdir = mDirs.valueFor(fileNames[i]);
1127 } else {
1128 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1129 notAdded = true;
1130 }
1131 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1132 resType);
1133 if (res < NO_ERROR) {
1134 return res;
1135 }
1136 if (res > 0 && notAdded) {
1137 mDirs.add(fileNames[i], subdir);
1138 }
1139 count += res;
1140 } else if (type == kFileTypeRegular) {
1141 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1142 status_t err = addLeafFile(fileNames[i], file);
1143 if (err != NO_ERROR) {
1144 return err;
1145 }
1146
1147 count++;
1148
1149 } else {
1150 if (bundle->getVerbose())
1151 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1152 }
1153 }
1154
1155 return count;
1156}
1157
1158status_t AaptDir::validate() const
1159{
1160 const size_t NF = mFiles.size();
1161 const size_t ND = mDirs.size();
1162 size_t i;
1163 for (i = 0; i < NF; i++) {
1164 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1165 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1166 "Invalid filename. Unable to add.");
1167 return UNKNOWN_ERROR;
1168 }
1169
1170 size_t j;
1171 for (j = i+1; j < NF; j++) {
1172 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1173 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1174 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1175 "File is case-insensitive equivalent to: %s",
1176 mFiles.valueAt(j)->getPrintableSource().string());
1177 return UNKNOWN_ERROR;
1178 }
1179
1180 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1181 // (this is mostly caught by the "marked" stuff, below)
1182 }
1183
1184 for (j = 0; j < ND; j++) {
1185 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1186 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1187 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1188 "File conflicts with dir from: %s",
1189 mDirs.valueAt(j)->getPrintableSource().string());
1190 return UNKNOWN_ERROR;
1191 }
1192 }
1193 }
1194
1195 for (i = 0; i < ND; i++) {
1196 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1197 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1198 "Invalid directory name, unable to add.");
1199 return UNKNOWN_ERROR;
1200 }
1201
1202 size_t j;
1203 for (j = i+1; j < ND; j++) {
1204 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1205 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1206 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1207 "Directory is case-insensitive equivalent to: %s",
1208 mDirs.valueAt(j)->getPrintableSource().string());
1209 return UNKNOWN_ERROR;
1210 }
1211 }
1212
1213 status_t err = mDirs.valueAt(i)->validate();
1214 if (err != NO_ERROR) {
1215 return err;
1216 }
1217 }
1218
1219 return NO_ERROR;
1220}
1221
1222void AaptDir::print() const
1223{
1224 const size_t ND=getDirs().size();
1225 size_t i;
1226 for (i=0; i<ND; i++) {
1227 getDirs().valueAt(i)->print();
1228 }
1229
1230 const size_t NF=getFiles().size();
1231 for (i=0; i<NF; i++) {
1232 getFiles().valueAt(i)->print();
1233 }
1234}
1235
1236String8 AaptDir::getPrintableSource() const
1237{
1238 if (mFiles.size() > 0) {
1239 // Arbitrarily pull the first file out of the list as the source dir.
1240 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1241 }
1242 if (mDirs.size() > 0) {
1243 // Or arbitrarily pull the first dir out of the list as the source dir.
1244 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1245 }
1246
1247 // Should never hit this case, but to be safe...
1248 return mPath;
1249
1250}
1251
1252// =========================================================================
1253// =========================================================================
1254// =========================================================================
1255
1256sp<AaptFile> AaptAssets::addFile(
1257 const String8& filePath, const AaptGroupEntry& entry,
1258 const String8& srcDir, sp<AaptGroup>* outGroup,
1259 const String8& resType)
1260{
1261 sp<AaptDir> dir = this;
1262 sp<AaptGroup> group;
1263 sp<AaptFile> file;
1264 String8 root, remain(filePath), partialPath;
1265 while (remain.length() > 0) {
1266 root = remain.walkPath(&remain);
1267 partialPath.appendPath(root);
1268
1269 const String8 rootStr(root);
1270
1271 if (remain.length() == 0) {
1272 ssize_t i = dir->getFiles().indexOfKey(rootStr);
1273 if (i >= 0) {
1274 group = dir->getFiles().valueAt(i);
1275 } else {
1276 group = new AaptGroup(rootStr, filePath);
1277 status_t res = dir->addFile(rootStr, group);
1278 if (res != NO_ERROR) {
1279 return NULL;
1280 }
1281 }
1282 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1283 status_t res = group->addFile(file);
1284 if (res != NO_ERROR) {
1285 return NULL;
1286 }
1287 break;
1288
1289 } else {
1290 ssize_t i = dir->getDirs().indexOfKey(rootStr);
1291 if (i >= 0) {
1292 dir = dir->getDirs().valueAt(i);
1293 } else {
1294 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1295 status_t res = dir->addDir(rootStr, subdir);
1296 if (res != NO_ERROR) {
1297 return NULL;
1298 }
1299 dir = subdir;
1300 }
1301 }
1302 }
1303
1304 mGroupEntries.add(entry);
1305 if (outGroup) *outGroup = group;
1306 return file;
1307}
1308
1309void AaptAssets::addResource(const String8& leafName, const String8& path,
1310 const sp<AaptFile>& file, const String8& resType)
1311{
1312 sp<AaptDir> res = AaptDir::makeDir(kResString);
1313 String8 dirname = file->getGroupEntry().toDirName(resType);
1314 sp<AaptDir> subdir = res->makeDir(dirname);
1315 sp<AaptGroup> grr = new AaptGroup(leafName, path);
1316 grr->addFile(file);
The Android Open Source Project9266c552009-01-15 16:12:10 -08001317
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001318 subdir->addFile(leafName, grr);
1319}
1320
1321
1322ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1323{
1324 int count;
1325 int totalCount = 0;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001326 FileType type;
The Android Open Source Project9266c552009-01-15 16:12:10 -08001327 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1328 const size_t dirCount =resDirs.size();
1329 sp<AaptAssets> current = this;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001330
1331 const int N = bundle->getFileSpecCount();
1332
1333 /*
1334 * If a package manifest was specified, include that first.
1335 */
1336 if (bundle->getAndroidManifestFile() != NULL) {
1337 // place at root of zip.
1338 String8 srcFile(bundle->getAndroidManifestFile());
1339 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1340 NULL, String8());
1341 totalCount++;
1342 }
1343
1344 /*
1345 * If a directory of custom assets was supplied, slurp 'em up.
1346 */
1347 if (bundle->getAssetSourceDir()) {
1348 const char* assetDir = bundle->getAssetSourceDir();
1349
1350 FileType type = getFileType(assetDir);
1351 if (type == kFileTypeNonexistent) {
1352 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1353 return UNKNOWN_ERROR;
1354 }
1355 if (type != kFileTypeDirectory) {
1356 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1357 return UNKNOWN_ERROR;
1358 }
1359
1360 String8 assetRoot(assetDir);
1361 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1362 AaptGroupEntry group;
1363 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1364 String8());
1365 if (count < 0) {
1366 totalCount = count;
1367 goto bail;
1368 }
1369 if (count > 0) {
1370 mGroupEntries.add(group);
1371 }
1372 totalCount += count;
1373
1374 if (bundle->getVerbose())
1375 printf("Found %d custom asset file%s in %s\n",
1376 count, (count==1) ? "" : "s", assetDir);
1377 }
1378
1379 /*
1380 * If a directory of resource-specific assets was supplied, slurp 'em up.
1381 */
The Android Open Source Project9266c552009-01-15 16:12:10 -08001382 for (size_t i=0; i<dirCount; i++) {
1383 const char *res = resDirs[i];
1384 if (res) {
1385 type = getFileType(res);
1386 if (type == kFileTypeNonexistent) {
1387 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1388 return UNKNOWN_ERROR;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001389 }
The Android Open Source Project9266c552009-01-15 16:12:10 -08001390 if (type == kFileTypeDirectory) {
1391 if (i>0) {
1392 sp<AaptAssets> nextOverlay = new AaptAssets();
1393 current->setOverlay(nextOverlay);
1394 current = nextOverlay;
1395 }
1396 count = current->slurpResourceTree(bundle, String8(res));
1397
1398 if (count < 0) {
1399 totalCount = count;
1400 goto bail;
1401 }
1402 totalCount += count;
1403 }
1404 else {
1405 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1406 return UNKNOWN_ERROR;
1407 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001408 }
The Android Open Source Project9266c552009-01-15 16:12:10 -08001409
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001410 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001411 /*
1412 * Now do any additional raw files.
1413 */
1414 for (int arg=0; arg<N; arg++) {
1415 const char* assetDir = bundle->getFileSpecEntry(arg);
1416
1417 FileType type = getFileType(assetDir);
1418 if (type == kFileTypeNonexistent) {
1419 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1420 return UNKNOWN_ERROR;
1421 }
1422 if (type != kFileTypeDirectory) {
1423 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1424 return UNKNOWN_ERROR;
1425 }
1426
1427 String8 assetRoot(assetDir);
1428
1429 if (bundle->getVerbose())
1430 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1431
1432 /*
1433 * Do a recursive traversal of subdir tree. We don't make any
1434 * guarantees about ordering, so we're okay with an inorder search
1435 * using whatever order the OS happens to hand back to us.
1436 */
1437 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
1438 if (count < 0) {
1439 /* failure; report error and remove archive */
1440 totalCount = count;
1441 goto bail;
1442 }
1443 totalCount += count;
1444
1445 if (bundle->getVerbose())
1446 printf("Found %d asset file%s in %s\n",
1447 count, (count==1) ? "" : "s", assetDir);
1448 }
1449
1450 count = validate();
1451 if (count != NO_ERROR) {
1452 totalCount = count;
1453 goto bail;
1454 }
1455
1456
1457bail:
1458 return totalCount;
1459}
1460
1461ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1462 const AaptGroupEntry& kind,
1463 const String8& resType)
1464{
1465 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
1466 if (res > 0) {
1467 mGroupEntries.add(kind);
1468 }
1469
1470 return res;
1471}
1472
1473ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1474{
1475 ssize_t err = 0;
1476
1477 DIR* dir = opendir(srcDir.string());
1478 if (dir == NULL) {
1479 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1480 return UNKNOWN_ERROR;
1481 }
1482
1483 status_t count = 0;
1484
1485 /*
1486 * Run through the directory, looking for dirs that match the
1487 * expected pattern.
1488 */
1489 while (1) {
1490 struct dirent* entry = readdir(dir);
1491 if (entry == NULL) {
1492 break;
1493 }
1494
1495 if (isHidden(srcDir.string(), entry->d_name)) {
1496 continue;
1497 }
1498
1499 String8 subdirName(srcDir);
1500 subdirName.appendPath(entry->d_name);
1501
1502 AaptGroupEntry group;
1503 String8 resType;
1504 bool b = group.initFromDirName(entry->d_name, &resType);
1505 if (!b) {
1506 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
1507 entry->d_name);
1508 err = -1;
1509 continue;
1510 }
1511
1512 FileType type = getFileType(subdirName.string());
1513
1514 if (type == kFileTypeDirectory) {
1515 sp<AaptDir> dir = makeDir(String8(entry->d_name));
1516 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1517 resType);
1518 if (res < 0) {
1519 count = res;
1520 goto bail;
1521 }
1522 if (res > 0) {
1523 mGroupEntries.add(group);
1524 count += res;
1525 }
1526
1527 mDirs.add(dir);
1528 } else {
1529 if (bundle->getVerbose()) {
1530 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
1531 }
1532 }
1533 }
1534
1535bail:
1536 closedir(dir);
1537 dir = NULL;
1538
1539 if (err != 0) {
1540 return err;
1541 }
1542 return count;
1543}
1544
1545ssize_t
1546AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
1547{
1548 int count = 0;
1549 SortedVector<AaptGroupEntry> entries;
1550
1551 ZipFile* zip = new ZipFile;
1552 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1553 if (err != NO_ERROR) {
1554 fprintf(stderr, "error opening zip file %s\n", filename);
1555 count = err;
1556 delete zip;
1557 return -1;
1558 }
1559
1560 const int N = zip->getNumEntries();
1561 for (int i=0; i<N; i++) {
1562 ZipEntry* entry = zip->getEntryByIndex(i);
1563 if (entry->getDeleted()) {
1564 continue;
1565 }
1566
1567 String8 entryName(entry->getFileName());
1568
1569 String8 dirName = entryName.getPathDir();
1570 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1571
1572 String8 resType;
1573 AaptGroupEntry kind;
1574
1575 String8 remain;
1576 if (entryName.walkPath(&remain) == kResourceDir) {
1577 // these are the resources, pull their type out of the directory name
1578 kind.initFromDirName(remain.walkPath().string(), &resType);
1579 } else {
1580 // these are untyped and don't have an AaptGroupEntry
1581 }
1582 if (entries.indexOf(kind) < 0) {
1583 entries.add(kind);
1584 mGroupEntries.add(kind);
1585 }
1586
1587 // use the one from the zip file if they both exist.
1588 dir->removeFile(entryName.getPathLeaf());
1589
1590 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1591 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1592 if (err != NO_ERROR) {
1593 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1594 count = err;
1595 goto bail;
1596 }
1597 file->setCompressionMethod(entry->getCompressionMethod());
1598
1599#if 0
1600 if (entryName == "AndroidManifest.xml") {
1601 printf("AndroidManifest.xml\n");
1602 }
1603 printf("\n\nfile: %s\n", entryName.string());
1604#endif
1605
1606 size_t len = entry->getUncompressedLen();
1607 void* data = zip->uncompress(entry);
1608 void* buf = file->editData(len);
1609 memcpy(buf, data, len);
1610
1611#if 0
1612 const int OFF = 0;
1613 const unsigned char* p = (unsigned char*)data;
1614 const unsigned char* end = p+len;
1615 p += OFF;
1616 for (int i=0; i<32 && p < end; i++) {
1617 printf("0x%03x ", i*0x10 + OFF);
1618 for (int j=0; j<0x10 && p < end; j++) {
1619 printf(" %02x", *p);
1620 p++;
1621 }
1622 printf("\n");
1623 }
1624#endif
1625
1626 free(data);
1627
1628 count++;
1629 }
1630
1631bail:
1632 delete zip;
1633 return count;
1634}
1635
1636sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1637{
1638 sp<AaptSymbols> sym = mSymbols.valueFor(name);
1639 if (sym == NULL) {
1640 sym = new AaptSymbols();
1641 mSymbols.add(name, sym);
1642 }
1643 return sym;
1644}
1645
1646status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1647{
1648 if (!mHaveIncludedAssets) {
1649 // Add in all includes.
1650 const Vector<const char*>& incl = bundle->getPackageIncludes();
1651 const size_t N=incl.size();
1652 for (size_t i=0; i<N; i++) {
1653 if (bundle->getVerbose())
1654 printf("Including resources from package: %s\n", incl[i]);
1655 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
1656 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1657 incl[i]);
1658 return UNKNOWN_ERROR;
1659 }
1660 }
1661 mHaveIncludedAssets = true;
1662 }
1663
1664 return NO_ERROR;
1665}
1666
1667status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
1668{
1669 const ResTable& res = getIncludedResources();
1670 // XXX dirty!
1671 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
1672}
1673
1674const ResTable& AaptAssets::getIncludedResources() const
1675{
1676 return mIncludedAssets.getResources(false);
1677}
1678
1679void AaptAssets::print() const
1680{
1681 printf("Locale/Vendor pairs:\n");
1682 const size_t N=mGroupEntries.size();
1683 for (size_t i=0; i<N; i++) {
1684 printf(" %s/%s\n",
1685 mGroupEntries.itemAt(i).locale.string(),
1686 mGroupEntries.itemAt(i).vendor.string());
1687 }
1688
1689 printf("\nFiles:\n");
1690 AaptDir::print();
1691}
1692
1693bool
1694valid_symbol_name(const String8& symbol)
1695{
1696 static char const * const KEYWORDS[] = {
1697 "abstract", "assert", "boolean", "break",
1698 "byte", "case", "catch", "char", "class", "const", "continue",
1699 "default", "do", "double", "else", "enum", "extends", "final",
1700 "finally", "float", "for", "goto", "if", "implements", "import",
1701 "instanceof", "int", "interface", "long", "native", "new", "package",
1702 "private", "protected", "public", "return", "short", "static",
1703 "strictfp", "super", "switch", "synchronized", "this", "throw",
1704 "throws", "transient", "try", "void", "volatile", "while",
1705 "true", "false", "null",
1706 NULL
1707 };
1708 const char*const* k = KEYWORDS;
1709 const char*const s = symbol.string();
1710 while (*k) {
1711 if (0 == strcmp(s, *k)) {
1712 return false;
1713 }
1714 k++;
1715 }
1716 return true;
1717}