blob: d8e2de659f4882723e367b85024978622b7589a5 [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"
Dianne Hackborne6b68032011-10-13 16:26:02 -07006#include "ResourceFilter.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007#include "Main.h"
8
9#include <utils/misc.h>
10#include <utils/SortedVector.h>
11
12#include <ctype.h>
13#include <dirent.h>
14#include <errno.h>
15
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080016static const char* kWildcardName = "any";
17static const char* kAssetDir = "assets";
18static const char* kResourceDir = "res";
Dianne Hackborne6b68032011-10-13 16:26:02 -070019static const char* kValuesDir = "values";
20static const char* kMipmapDir = "mipmap";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021static const char* kInvalidChars = "/\\:";
22static const size_t kMaxAssetFileName = 100;
23
24static const String8 kResString(kResourceDir);
25
26/*
27 * Names of asset files must meet the following criteria:
28 *
29 * - the filename length must be less than kMaxAssetFileName bytes long
30 * (and can't be empty)
31 * - all characters must be 7-bit printable ASCII
32 * - none of { '/' '\\' ':' }
33 *
34 * Pass in just the filename, not the full path.
35 */
36static bool validateFileName(const char* fileName)
37{
38 const char* cp = fileName;
39 size_t len = 0;
40
41 while (*cp != '\0') {
42 if ((*cp & 0x80) != 0)
43 return false; // reject high ASCII
44 if (*cp < 0x20 || *cp >= 0x7f)
45 return false; // reject control chars and 0x7f
46 if (strchr(kInvalidChars, *cp) != NULL)
47 return false; // reject path sep chars
48 cp++;
49 len++;
50 }
51
52 if (len < 1 || len > kMaxAssetFileName)
53 return false; // reject empty or too long
54
55 return true;
56}
57
Raphael Moll6c255a32012-05-07 16:16:46 -070058// The default to use if no other ignore pattern is defined.
59const char * const gDefaultIgnoreAssets =
Tor Norbyee0219c82012-06-04 10:38:13 -070060 "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
Raphael Moll6c255a32012-05-07 16:16:46 -070061// The ignore pattern that can be passed via --ignore-assets in Main.cpp
62const char * gUserIgnoreAssets = NULL;
63
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064static bool isHidden(const char *root, const char *path)
65{
Raphael Moll6c255a32012-05-07 16:16:46 -070066 // Patterns syntax:
67 // - Delimiter is :
68 // - Entry can start with the flag ! to avoid printing a warning
69 // about the file being ignored.
70 // - Entry can have the flag "<dir>" to match only directories
71 // or <file> to match only files. Default is to match both.
72 // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
73 // where prefix/suffix must have at least 1 character (so that
74 // we don't match a '*' catch-all pattern.)
75 // - The special filenames "." and ".." are always ignored.
76 // - Otherwise the full string is matched.
77 // - match is not case-sensitive.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
Raphael Moll6c255a32012-05-07 16:16:46 -070079 if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 }
82
Raphael Moll6c255a32012-05-07 16:16:46 -070083 const char *delim = ":";
84 const char *p = gUserIgnoreAssets;
85 if (!p || !p[0]) {
86 p = getenv("ANDROID_AAPT_IGNORE");
87 }
88 if (!p || !p[0]) {
89 p = gDefaultIgnoreAssets;
90 }
91 char *patterns = strdup(p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
Raphael Moll6c255a32012-05-07 16:16:46 -070093 bool ignore = false;
94 bool chatty = true;
95 char *matchedPattern = NULL;
96
97 String8 fullPath(root);
98 fullPath.appendPath(path);
99 FileType type = getFileType(fullPath);
100
101 int plen = strlen(path);
102
103 // Note: we don't have strtok_r under mingw.
104 for(char *token = strtok(patterns, delim);
105 !ignore && token != NULL;
106 token = strtok(NULL, delim)) {
107 chatty = token[0] != '!';
108 if (!chatty) token++; // skip !
109 if (strncasecmp(token, "<dir>" , 5) == 0) {
110 if (type != kFileTypeDirectory) continue;
111 token += 5;
112 }
113 if (strncasecmp(token, "<file>", 6) == 0) {
114 if (type != kFileTypeRegular) continue;
115 token += 6;
116 }
117
118 matchedPattern = token;
119 int n = strlen(token);
120
121 if (token[0] == '*') {
122 // Match *suffix
123 token++;
Ying Wang996b0732012-05-22 11:24:22 -0700124 n--;
Raphael Moll6c255a32012-05-07 16:16:46 -0700125 if (n <= plen) {
126 ignore = strncasecmp(token, path + plen - n, n) == 0;
127 }
128 } else if (n > 1 && token[n - 1] == '*') {
129 // Match prefix*
130 ignore = strncasecmp(token, path, n - 1) == 0;
131 } else {
132 ignore = strcasecmp(token, path) == 0;
133 }
134 }
135
136 if (ignore && chatty) {
137 fprintf(stderr, " (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
138 type == kFileTypeDirectory ? "dir" : "file",
139 path,
140 matchedPattern ? matchedPattern : "");
141 }
142
143 free(patterns);
144 return ignore;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145}
146
147// =========================================================================
148// =========================================================================
149// =========================================================================
150
Narayan Kamath788fa412014-01-21 15:32:36 +0000151/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
152 Vector<String8>* parts, const char separator) {
153 const char *p = chars;
154 const char *q;
155 while (NULL != (q = strchr(p, separator))) {
156 String8 val(p, q - p);
157 val.toLower();
158 parts->add(val);
159 p = q+1;
160 }
161
162 if (p < chars + strlen(chars)) {
163 String8 val(p);
164 val.toLower();
165 parts->add(val);
166 }
167}
168
169/* static */
170inline bool isAlpha(const String8& string) {
171 const size_t length = string.length();
172 for (size_t i = 0; i < length; ++i) {
173 if (!isalpha(string[i])) {
174 return false;
175 }
176 }
177
178 return true;
179}
180
181/* static */
182inline bool isNumber(const String8& string) {
183 const size_t length = string.length();
184 for (size_t i = 0; i < length; ++i) {
185 if (!isdigit(string[i])) {
186 return false;
187 }
188 }
189
190 return true;
191}
192
193void AaptLocaleValue::setLanguage(const char* languageChars) {
194 size_t i = 0;
195 while ((*languageChars) != '\0') {
196 language[i++] = tolower(*languageChars);
197 languageChars++;
198 }
199}
200
201void AaptLocaleValue::setRegion(const char* regionChars) {
202 size_t i = 0;
203 while ((*regionChars) != '\0') {
204 region[i++] = toupper(*regionChars);
205 regionChars++;
206 }
207}
208
209void AaptLocaleValue::setScript(const char* scriptChars) {
210 size_t i = 0;
211 while ((*scriptChars) != '\0') {
212 if (i == 0) {
213 script[i++] = toupper(*scriptChars);
214 } else {
215 script[i++] = tolower(*scriptChars);
216 }
217 scriptChars++;
218 }
219}
220
221void AaptLocaleValue::setVariant(const char* variantChars) {
222 size_t i = 0;
223 while ((*variantChars) != '\0') {
224 variant[i++] = *variantChars;
225 variantChars++;
226 }
227}
228
229bool AaptLocaleValue::initFromFilterString(const String8& str) {
230 // A locale (as specified in the filter) is an underscore separated name such
231 // as "en_US", "en_Latn_US", or "en_US_POSIX".
232 Vector<String8> parts;
233 splitAndLowerCase(str.string(), &parts, '_');
234
235 const int numTags = parts.size();
236 bool valid = false;
237 if (numTags >= 1) {
238 const String8& lang = parts[0];
239 if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
240 setLanguage(lang.string());
241 valid = true;
242 }
243 }
244
245 if (!valid || numTags == 1) {
246 return valid;
247 }
248
249 // At this point, valid == true && numTags > 1.
250 const String8& part2 = parts[1];
251 if ((part2.length() == 2 && isAlpha(part2)) ||
252 (part2.length() == 3 && isNumber(part2))) {
253 setRegion(part2.string());
254 } else if (part2.length() == 4 && isAlpha(part2)) {
255 setScript(part2.string());
256 } else if (part2.length() >= 5 && part2.length() <= 8) {
257 setVariant(part2.string());
258 } else {
259 valid = false;
260 }
261
262 if (!valid || numTags == 2) {
263 return valid;
264 }
265
266 // At this point, valid == true && numTags > 1.
267 const String8& part3 = parts[2];
268 if (((part3.length() == 2 && isAlpha(part3)) ||
269 (part3.length() == 3 && isNumber(part3))) && script[0]) {
270 setRegion(part3.string());
271 } else if (part3.length() >= 5 && part3.length() <= 8) {
272 setVariant(part3.string());
273 } else {
274 valid = false;
275 }
276
277 if (!valid || numTags == 3) {
278 return valid;
279 }
280
281 const String8& part4 = parts[3];
282 if (part4.length() >= 5 && part4.length() <= 8) {
283 setVariant(part4.string());
284 } else {
285 valid = false;
286 }
287
288 if (!valid || numTags > 4) {
289 return false;
290 }
291
292 return true;
293}
294
295int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
296 const int size = parts.size();
297 int currentIndex = startIndex;
298
299 String8 part = parts[currentIndex];
300 if (part[0] == 'b' && part[1] == '+') {
301 // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
302 // except that the separator is "+" and not "-".
303 Vector<String8> subtags;
304 AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
305 subtags.removeItemsAt(0);
306 if (subtags.size() == 1) {
307 setLanguage(subtags[0]);
308 } else if (subtags.size() == 2) {
309 setLanguage(subtags[0]);
310
311 // The second tag can either be a region, a variant or a script.
312 switch (subtags[1].size()) {
313 case 2:
314 case 3:
315 setRegion(subtags[1]);
316 break;
317 case 4:
318 setScript(subtags[1]);
319 break;
320 case 5:
321 case 6:
322 case 7:
323 case 8:
324 setVariant(subtags[1]);
325 break;
326 default:
327 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
328 part.string());
329 return -1;
330 }
331 } else if (subtags.size() == 3) {
332 // The language is always the first subtag.
333 setLanguage(subtags[0]);
334
335 // The second subtag can either be a script or a region code.
336 // If its size is 4, it's a script code, else it's a region code.
337 bool hasRegion = false;
338 if (subtags[1].size() == 4) {
339 setScript(subtags[1]);
340 } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
341 setRegion(subtags[1]);
342 hasRegion = true;
343 } else {
344 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
345 return -1;
346 }
347
348 // The third tag can either be a region code (if the second tag was
349 // a script), else a variant code.
350 if (subtags[2].size() > 4) {
351 setVariant(subtags[2]);
352 } else {
353 setRegion(subtags[2]);
354 }
355 } else if (subtags.size() == 4) {
356 setLanguage(subtags[0]);
357 setScript(subtags[1]);
358 setRegion(subtags[2]);
359 setVariant(subtags[3]);
360 } else {
361 fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
362 return -1;
363 }
364
365 return ++currentIndex;
366 } else {
367 if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
368 setLanguage(part);
369 if (++currentIndex == size) {
370 return size;
371 }
372 } else {
373 return currentIndex;
374 }
375
376 part = parts[currentIndex];
377 if (part.string()[0] == 'r' && part.length() == 3) {
378 setRegion(part.string() + 1);
379 if (++currentIndex == size) {
380 return size;
381 }
382 }
383 }
384
385 return currentIndex;
386}
387
388
389String8 AaptLocaleValue::toDirName() const {
390 String8 dirName("");
391 if (language[0]) {
392 dirName += language;
393 } else {
394 return dirName;
395 }
396
397 if (script[0]) {
398 dirName += "-s";
399 dirName += script;
400 }
401
402 if (region[0]) {
403 dirName += "-r";
404 dirName += region;
405 }
406
407 if (variant[0]) {
408 dirName += "-v";
409 dirName += variant;
410 }
411
412 return dirName;
413}
414
415void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
416 config.unpackLanguage(language);
417 config.unpackRegion(region);
418 if (config.localeScript[0]) {
419 memcpy(script, config.localeScript, sizeof(config.localeScript));
420 }
421
422 if (config.localeVariant[0]) {
423 memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
424 }
425}
426
427void AaptLocaleValue::writeTo(ResTable_config* out) const {
428 out->packLanguage(language);
429 out->packRegion(region);
430
431 if (script[0]) {
432 memcpy(out->localeScript, script, sizeof(out->localeScript));
433 }
434
435 if (variant[0]) {
436 memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
437 }
438}
439
440
441/* static */ bool
442AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443{
444 ResTable_config config;
Narayan Kamath788fa412014-01-21 15:32:36 +0000445 memset(&config, 0, sizeof(ResTable_config));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446
447 // IMSI - MCC
448 if (getMccName(part.string(), &config)) {
449 *axis = AXIS_MCC;
Narayan Kamath788fa412014-01-21 15:32:36 +0000450 value->intValue = config.mcc;
451 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 }
453
454 // IMSI - MNC
455 if (getMncName(part.string(), &config)) {
456 *axis = AXIS_MNC;
Narayan Kamath788fa412014-01-21 15:32:36 +0000457 value->intValue = config.mnc;
458 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 }
460
461 // locale - language
Narayan Kamath788fa412014-01-21 15:32:36 +0000462 if (value->localeValue.initFromFilterString(part)) {
463 *axis = AXIS_LOCALE;
464 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 }
466
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700467 // layout direction
468 if (getLayoutDirectionName(part.string(), &config)) {
469 *axis = AXIS_LAYOUTDIR;
Narayan Kamath788fa412014-01-21 15:32:36 +0000470 value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
471 return true;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700472 }
473
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700474 // smallest screen dp width
475 if (getSmallestScreenWidthDpName(part.string(), &config)) {
476 *axis = AXIS_SMALLESTSCREENWIDTHDP;
Narayan Kamath788fa412014-01-21 15:32:36 +0000477 value->intValue = config.smallestScreenWidthDp;
478 return true;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700479 }
480
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700481 // screen dp width
482 if (getScreenWidthDpName(part.string(), &config)) {
483 *axis = AXIS_SCREENWIDTHDP;
Narayan Kamath788fa412014-01-21 15:32:36 +0000484 value->intValue = config.screenWidthDp;
485 return true;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700486 }
487
488 // screen dp height
489 if (getScreenHeightDpName(part.string(), &config)) {
490 *axis = AXIS_SCREENHEIGHTDP;
Narayan Kamath788fa412014-01-21 15:32:36 +0000491 value->intValue = config.screenHeightDp;
492 return true;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700493 }
494
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700495 // screen layout size
496 if (getScreenLayoutSizeName(part.string(), &config)) {
497 *axis = AXIS_SCREENLAYOUTSIZE;
Narayan Kamath788fa412014-01-21 15:32:36 +0000498 value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
499 return true;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700500 }
501
502 // screen layout long
503 if (getScreenLayoutLongName(part.string(), &config)) {
504 *axis = AXIS_SCREENLAYOUTLONG;
Narayan Kamath788fa412014-01-21 15:32:36 +0000505 value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
506 return true;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700507 }
508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 // orientation
510 if (getOrientationName(part.string(), &config)) {
511 *axis = AXIS_ORIENTATION;
Narayan Kamath788fa412014-01-21 15:32:36 +0000512 value->intValue = config.orientation;
513 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 }
515
Tobias Haamel27b28b32010-02-09 23:09:17 +0100516 // ui mode type
517 if (getUiModeTypeName(part.string(), &config)) {
518 *axis = AXIS_UIMODETYPE;
Narayan Kamath788fa412014-01-21 15:32:36 +0000519 value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
520 return true;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100521 }
522
523 // ui mode night
524 if (getUiModeNightName(part.string(), &config)) {
525 *axis = AXIS_UIMODENIGHT;
Narayan Kamath788fa412014-01-21 15:32:36 +0000526 value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
527 return true;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100528 }
529
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 // density
531 if (getDensityName(part.string(), &config)) {
532 *axis = AXIS_DENSITY;
Narayan Kamath788fa412014-01-21 15:32:36 +0000533 value->intValue = config.density;
534 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 }
536
537 // touchscreen
538 if (getTouchscreenName(part.string(), &config)) {
539 *axis = AXIS_TOUCHSCREEN;
Narayan Kamath788fa412014-01-21 15:32:36 +0000540 value->intValue = config.touchscreen;
541 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 }
543
544 // keyboard hidden
545 if (getKeysHiddenName(part.string(), &config)) {
546 *axis = AXIS_KEYSHIDDEN;
Narayan Kamath788fa412014-01-21 15:32:36 +0000547 value->intValue = config.inputFlags;
548 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 }
550
551 // keyboard
552 if (getKeyboardName(part.string(), &config)) {
553 *axis = AXIS_KEYBOARD;
Narayan Kamath788fa412014-01-21 15:32:36 +0000554 value->intValue = config.keyboard;
555 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 }
557
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700558 // navigation hidden
559 if (getNavHiddenName(part.string(), &config)) {
560 *axis = AXIS_NAVHIDDEN;
Narayan Kamath788fa412014-01-21 15:32:36 +0000561 value->intValue = config.inputFlags;
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700562 return 0;
563 }
564
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 // navigation
566 if (getNavigationName(part.string(), &config)) {
567 *axis = AXIS_NAVIGATION;
Narayan Kamath788fa412014-01-21 15:32:36 +0000568 value->intValue = config.navigation;
569 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 }
571
572 // screen size
573 if (getScreenSizeName(part.string(), &config)) {
574 *axis = AXIS_SCREENSIZE;
Narayan Kamath788fa412014-01-21 15:32:36 +0000575 value->intValue = config.screenSize;
576 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 }
578
579 // version
580 if (getVersionName(part.string(), &config)) {
581 *axis = AXIS_VERSION;
Narayan Kamath788fa412014-01-21 15:32:36 +0000582 value->intValue = config.version;
583 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 }
585
Narayan Kamath788fa412014-01-21 15:32:36 +0000586 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587}
588
Narayan Kamath788fa412014-01-21 15:32:36 +0000589AxisValue
Dianne Hackborne6b68032011-10-13 16:26:02 -0700590AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
591{
Narayan Kamath788fa412014-01-21 15:32:36 +0000592 AxisValue value;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700593 switch (axis) {
594 case AXIS_MCC:
Narayan Kamath788fa412014-01-21 15:32:36 +0000595 value.intValue = config.mcc;
596 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700597 case AXIS_MNC:
Narayan Kamath788fa412014-01-21 15:32:36 +0000598 value.intValue = config.mnc;
599 break;
600 case AXIS_LOCALE:
601 value.localeValue.initFromResTable(config);
602 break;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700603 case AXIS_LAYOUTDIR:
Narayan Kamath788fa412014-01-21 15:32:36 +0000604 value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
605 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700606 case AXIS_SCREENLAYOUTSIZE:
Narayan Kamath788fa412014-01-21 15:32:36 +0000607 value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
608 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700609 case AXIS_ORIENTATION:
Narayan Kamath788fa412014-01-21 15:32:36 +0000610 value.intValue = config.orientation;
611 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700612 case AXIS_UIMODETYPE:
Narayan Kamath788fa412014-01-21 15:32:36 +0000613 value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
614 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700615 case AXIS_UIMODENIGHT:
Narayan Kamath788fa412014-01-21 15:32:36 +0000616 value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
617 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700618 case AXIS_DENSITY:
Narayan Kamath788fa412014-01-21 15:32:36 +0000619 value.intValue = config.density;
620 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700621 case AXIS_TOUCHSCREEN:
Narayan Kamath788fa412014-01-21 15:32:36 +0000622 value.intValue = config.touchscreen;
623 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700624 case AXIS_KEYSHIDDEN:
Narayan Kamath788fa412014-01-21 15:32:36 +0000625 value.intValue = config.inputFlags;
626 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700627 case AXIS_KEYBOARD:
Narayan Kamath788fa412014-01-21 15:32:36 +0000628 value.intValue = config.keyboard;
629 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700630 case AXIS_NAVIGATION:
Narayan Kamath788fa412014-01-21 15:32:36 +0000631 value.intValue = config.navigation;
632 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700633 case AXIS_SCREENSIZE:
Narayan Kamath788fa412014-01-21 15:32:36 +0000634 value.intValue = config.screenSize;
635 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700636 case AXIS_SMALLESTSCREENWIDTHDP:
Narayan Kamath788fa412014-01-21 15:32:36 +0000637 value.intValue = config.smallestScreenWidthDp;
638 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700639 case AXIS_SCREENWIDTHDP:
Narayan Kamath788fa412014-01-21 15:32:36 +0000640 value.intValue = config.screenWidthDp;
641 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700642 case AXIS_SCREENHEIGHTDP:
Narayan Kamath788fa412014-01-21 15:32:36 +0000643 value.intValue = config.screenHeightDp;
644 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700645 case AXIS_VERSION:
Narayan Kamath788fa412014-01-21 15:32:36 +0000646 value.intValue = config.version;
647 break;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700648 }
Narayan Kamath788fa412014-01-21 15:32:36 +0000649
650 return value;
Dianne Hackborne6b68032011-10-13 16:26:02 -0700651}
652
653bool
654AaptGroupEntry::configSameExcept(const ResTable_config& config,
655 const ResTable_config& otherConfig, int axis)
656{
657 for (int i=AXIS_START; i<=AXIS_END; i++) {
658 if (i == axis) {
659 continue;
660 }
661 if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
662 return false;
663 }
664 }
665 return true;
666}
667
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668bool
669AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
670{
Dianne Hackborne6b68032011-10-13 16:26:02 -0700671 mParamsChanged = true;
672
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 Vector<String8> parts;
Narayan Kamath788fa412014-01-21 15:32:36 +0000674 AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675
Narayan Kamath788fa412014-01-21 15:32:36 +0000676 String8 mcc, mnc, layoutsize, layoutlong, orient, den;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700677 String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700678 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679
Narayan Kamath788fa412014-01-21 15:32:36 +0000680 AaptLocaleValue locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681
682 const int N = parts.size();
683 int index = 0;
684 String8 part = parts[index];
685
686 // resource type
687 if (!isValidResourceType(part)) {
688 return false;
689 }
690 *resType = part;
691
692 index++;
693 if (index == N) {
694 goto success;
695 }
696 part = parts[index];
697
698 // imsi - mcc
699 if (getMccName(part.string())) {
700 mcc = part;
701
702 index++;
703 if (index == N) {
704 goto success;
705 }
706 part = parts[index];
707 } else {
708 //printf("not mcc: %s\n", part.string());
709 }
710
711 // imsi - mnc
712 if (getMncName(part.string())) {
713 mnc = part;
714
715 index++;
716 if (index == N) {
717 goto success;
718 }
719 part = parts[index];
720 } else {
Narayan Kamath788fa412014-01-21 15:32:36 +0000721 //printf("not mnc: %s\n", part.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 }
723
Narayan Kamath788fa412014-01-21 15:32:36 +0000724 index = locale.initFromDirName(parts, index);
725 if (index == -1) {
726 return false;
727 }
728 if (index >= N){
729 goto success;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 }
731
Narayan Kamath788fa412014-01-21 15:32:36 +0000732 part = parts[index];
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700733 if (getLayoutDirectionName(part.string())) {
734 layoutDir = part;
735
736 index++;
737 if (index == N) {
738 goto success;
739 }
740 part = parts[index];
741 } else {
742 //printf("not layout direction: %s\n", part.string());
743 }
744
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700745 if (getSmallestScreenWidthDpName(part.string())) {
746 smallestwidthdp = part;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700747
748 index++;
749 if (index == N) {
750 goto success;
751 }
752 part = parts[index];
753 } else {
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700754 //printf("not smallest screen width dp: %s\n", part.string());
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700755 }
756
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700757 if (getScreenWidthDpName(part.string())) {
758 widthdp = part;
759
760 index++;
761 if (index == N) {
762 goto success;
763 }
764 part = parts[index];
765 } else {
766 //printf("not screen width dp: %s\n", part.string());
767 }
768
769 if (getScreenHeightDpName(part.string())) {
770 heightdp = part;
771
772 index++;
773 if (index == N) {
774 goto success;
775 }
776 part = parts[index];
777 } else {
778 //printf("not screen height dp: %s\n", part.string());
779 }
780
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700781 if (getScreenLayoutSizeName(part.string())) {
782 layoutsize = part;
783
784 index++;
785 if (index == N) {
786 goto success;
787 }
788 part = parts[index];
789 } else {
790 //printf("not screen layout size: %s\n", part.string());
791 }
792
793 if (getScreenLayoutLongName(part.string())) {
794 layoutlong = part;
795
796 index++;
797 if (index == N) {
798 goto success;
799 }
800 part = parts[index];
801 } else {
802 //printf("not screen layout long: %s\n", part.string());
803 }
804
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 // orientation
806 if (getOrientationName(part.string())) {
807 orient = part;
808
809 index++;
810 if (index == N) {
811 goto success;
812 }
813 part = parts[index];
814 } else {
815 //printf("not orientation: %s\n", part.string());
816 }
817
Tobias Haamel27b28b32010-02-09 23:09:17 +0100818 // ui mode type
819 if (getUiModeTypeName(part.string())) {
820 uiModeType = part;
821
822 index++;
823 if (index == N) {
824 goto success;
825 }
826 part = parts[index];
827 } else {
828 //printf("not ui mode type: %s\n", part.string());
829 }
830
831 // ui mode night
832 if (getUiModeNightName(part.string())) {
833 uiModeNight = part;
834
835 index++;
836 if (index == N) {
837 goto success;
838 }
839 part = parts[index];
840 } else {
841 //printf("not ui mode night: %s\n", part.string());
842 }
843
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 // density
845 if (getDensityName(part.string())) {
846 den = part;
847
848 index++;
849 if (index == N) {
850 goto success;
851 }
852 part = parts[index];
853 } else {
854 //printf("not density: %s\n", part.string());
855 }
856
857 // touchscreen
858 if (getTouchscreenName(part.string())) {
859 touch = part;
860
861 index++;
862 if (index == N) {
863 goto success;
864 }
865 part = parts[index];
866 } else {
867 //printf("not touchscreen: %s\n", part.string());
868 }
869
870 // keyboard hidden
871 if (getKeysHiddenName(part.string())) {
872 keysHidden = part;
873
874 index++;
875 if (index == N) {
876 goto success;
877 }
878 part = parts[index];
879 } else {
880 //printf("not keysHidden: %s\n", part.string());
881 }
882
883 // keyboard
884 if (getKeyboardName(part.string())) {
885 key = part;
886
887 index++;
888 if (index == N) {
889 goto success;
890 }
891 part = parts[index];
892 } else {
893 //printf("not keyboard: %s\n", part.string());
894 }
895
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700896 // navigation hidden
897 if (getNavHiddenName(part.string())) {
898 navHidden = part;
899
900 index++;
901 if (index == N) {
902 goto success;
903 }
904 part = parts[index];
905 } else {
906 //printf("not navHidden: %s\n", part.string());
907 }
908
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 if (getNavigationName(part.string())) {
910 nav = part;
911
912 index++;
913 if (index == N) {
914 goto success;
915 }
916 part = parts[index];
917 } else {
918 //printf("not navigation: %s\n", part.string());
919 }
920
921 if (getScreenSizeName(part.string())) {
922 size = part;
923
924 index++;
925 if (index == N) {
926 goto success;
927 }
928 part = parts[index];
929 } else {
930 //printf("not screen size: %s\n", part.string());
931 }
932
933 if (getVersionName(part.string())) {
934 vers = part;
935
936 index++;
937 if (index == N) {
938 goto success;
939 }
940 part = parts[index];
941 } else {
942 //printf("not version: %s\n", part.string());
943 }
944
945 // if there are extra parts, it doesn't match
946 return false;
947
948success:
949 this->mcc = mcc;
950 this->mnc = mnc;
Narayan Kamath788fa412014-01-21 15:32:36 +0000951 this->locale = locale;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700952 this->screenLayoutSize = layoutsize;
953 this->screenLayoutLong = layoutlong;
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700954 this->smallestScreenWidthDp = smallestwidthdp;
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700955 this->screenWidthDp = widthdp;
956 this->screenHeightDp = heightdp;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 this->orientation = orient;
Tobias Haamel27b28b32010-02-09 23:09:17 +0100958 this->uiModeType = uiModeType;
959 this->uiModeNight = uiModeNight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 this->density = den;
961 this->touchscreen = touch;
962 this->keysHidden = keysHidden;
963 this->keyboard = key;
Dianne Hackborn93e462b2009-09-15 22:50:40 -0700964 this->navHidden = navHidden;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 this->navigation = nav;
966 this->screenSize = size;
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700967 this->layoutDirection = layoutDir;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 this->version = vers;
969
970 // what is this anyway?
971 this->vendor = "";
972
973 return true;
974}
975
976String8
977AaptGroupEntry::toString() const
978{
979 String8 s = this->mcc;
980 s += ",";
981 s += this->mnc;
982 s += ",";
Narayan Kamath788fa412014-01-21 15:32:36 +0000983 s += locale.toDirName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 s += ",";
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700985 s += layoutDirection;
986 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700987 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -0700988 s += ",";
Dianne Hackbornebff8f92011-05-12 18:07:47 -0700989 s += screenWidthDp;
990 s += ",";
991 s += screenHeightDp;
992 s += ",";
Dianne Hackborn69cb8752011-05-19 18:13:32 -0700993 s += screenLayoutSize;
994 s += ",";
995 s += screenLayoutLong;
996 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 s += this->orientation;
998 s += ",";
Tobias Haamel27b28b32010-02-09 23:09:17 +0100999 s += uiModeType;
1000 s += ",";
1001 s += uiModeNight;
1002 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 s += density;
1004 s += ",";
1005 s += touchscreen;
1006 s += ",";
1007 s += keysHidden;
1008 s += ",";
1009 s += keyboard;
1010 s += ",";
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001011 s += navHidden;
1012 s += ",";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 s += navigation;
1014 s += ",";
1015 s += screenSize;
1016 s += ",";
1017 s += version;
1018 return s;
1019}
1020
1021String8
1022AaptGroupEntry::toDirName(const String8& resType) const
1023{
1024 String8 s = resType;
1025 if (this->mcc != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001026 if (s.length() > 0) {
1027 s += "-";
1028 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 s += mcc;
1030 }
1031 if (this->mnc != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001032 if (s.length() > 0) {
1033 s += "-";
1034 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 s += mnc;
1036 }
Narayan Kamath788fa412014-01-21 15:32:36 +00001037
1038 const String8 localeComponent = locale.toDirName();
1039 if (localeComponent != "") {
1040 if (s.length() > 0) {
1041 s += "-";
1042 }
1043 s += localeComponent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
Narayan Kamath788fa412014-01-21 15:32:36 +00001045
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001046 if (this->layoutDirection != "") {
1047 if (s.length() > 0) {
1048 s += "-";
1049 }
1050 s += layoutDirection;
1051 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001052 if (this->smallestScreenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001053 if (s.length() > 0) {
1054 s += "-";
1055 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001056 s += smallestScreenWidthDp;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001057 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001058 if (this->screenWidthDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001059 if (s.length() > 0) {
1060 s += "-";
1061 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001062 s += screenWidthDp;
1063 }
1064 if (this->screenHeightDp != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001065 if (s.length() > 0) {
1066 s += "-";
1067 }
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001068 s += screenHeightDp;
1069 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001070 if (this->screenLayoutSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001071 if (s.length() > 0) {
1072 s += "-";
1073 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001074 s += screenLayoutSize;
1075 }
1076 if (this->screenLayoutLong != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001077 if (s.length() > 0) {
1078 s += "-";
1079 }
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001080 s += screenLayoutLong;
1081 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 if (this->orientation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001083 if (s.length() > 0) {
1084 s += "-";
1085 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 s += orientation;
1087 }
Tobias Haamel27b28b32010-02-09 23:09:17 +01001088 if (this->uiModeType != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001089 if (s.length() > 0) {
1090 s += "-";
1091 }
Tobias Haamel27b28b32010-02-09 23:09:17 +01001092 s += uiModeType;
1093 }
1094 if (this->uiModeNight != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001095 if (s.length() > 0) {
1096 s += "-";
1097 }
Tobias Haamel27b28b32010-02-09 23:09:17 +01001098 s += uiModeNight;
1099 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 if (this->density != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001101 if (s.length() > 0) {
1102 s += "-";
1103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 s += density;
1105 }
1106 if (this->touchscreen != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001107 if (s.length() > 0) {
1108 s += "-";
1109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 s += touchscreen;
1111 }
1112 if (this->keysHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001113 if (s.length() > 0) {
1114 s += "-";
1115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 s += keysHidden;
1117 }
1118 if (this->keyboard != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001119 if (s.length() > 0) {
1120 s += "-";
1121 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 s += keyboard;
1123 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001124 if (this->navHidden != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001125 if (s.length() > 0) {
1126 s += "-";
1127 }
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001128 s += navHidden;
1129 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 if (this->navigation != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001131 if (s.length() > 0) {
1132 s += "-";
1133 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 s += navigation;
1135 }
1136 if (this->screenSize != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001137 if (s.length() > 0) {
1138 s += "-";
1139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 s += screenSize;
1141 }
1142 if (this->version != "") {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001143 if (s.length() > 0) {
1144 s += "-";
1145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 s += version;
1147 }
1148
1149 return s;
1150}
1151
1152bool AaptGroupEntry::getMccName(const char* name,
1153 ResTable_config* out)
1154{
1155 if (strcmp(name, kWildcardName) == 0) {
1156 if (out) out->mcc = 0;
1157 return true;
1158 }
1159 const char* c = name;
1160 if (tolower(*c) != 'm') return false;
1161 c++;
1162 if (tolower(*c) != 'c') return false;
1163 c++;
1164 if (tolower(*c) != 'c') return false;
1165 c++;
1166
1167 const char* val = c;
1168
1169 while (*c >= '0' && *c <= '9') {
1170 c++;
1171 }
1172 if (*c != 0) return false;
1173 if (c-val != 3) return false;
1174
1175 int d = atoi(val);
1176 if (d != 0) {
1177 if (out) out->mcc = d;
1178 return true;
1179 }
1180
1181 return false;
1182}
1183
1184bool AaptGroupEntry::getMncName(const char* name,
1185 ResTable_config* out)
1186{
1187 if (strcmp(name, kWildcardName) == 0) {
1188 if (out) out->mcc = 0;
1189 return true;
1190 }
1191 const char* c = name;
1192 if (tolower(*c) != 'm') return false;
1193 c++;
1194 if (tolower(*c) != 'n') return false;
1195 c++;
1196 if (tolower(*c) != 'c') return false;
1197 c++;
1198
1199 const char* val = c;
1200
1201 while (*c >= '0' && *c <= '9') {
1202 c++;
1203 }
1204 if (*c != 0) return false;
1205 if (c-val == 0 || c-val > 3) return false;
1206
Johan Redestig5ef0b9d2010-11-09 14:13:31 +01001207 if (out) {
1208 out->mnc = atoi(val);
Mattias Petersson1d766b52011-10-07 09:33:52 +02001209 if (out->mnc == 0) {
1210 out->mnc = ACONFIGURATION_MNC_ZERO;
1211 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 }
1213
Johan Redestig5ef0b9d2010-11-09 14:13:31 +01001214 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215}
1216
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001217bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
1218{
1219 if (strcmp(name, kWildcardName) == 0) {
1220 if (out) out->screenLayout =
1221 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1222 | ResTable_config::LAYOUTDIR_ANY;
1223 return true;
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07001224 } else if (strcmp(name, "ldltr") == 0) {
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001225 if (out) out->screenLayout =
1226 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1227 | ResTable_config::LAYOUTDIR_LTR;
1228 return true;
Fabrice Di Meglio8a802db2012-09-05 13:12:02 -07001229 } else if (strcmp(name, "ldrtl") == 0) {
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001230 if (out) out->screenLayout =
1231 (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
1232 | ResTable_config::LAYOUTDIR_RTL;
1233 return true;
1234 }
1235
1236 return false;
1237}
1238
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001239bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
1240 ResTable_config* out)
1241{
1242 if (strcmp(name, kWildcardName) == 0) {
1243 if (out) out->screenLayout =
1244 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1245 | ResTable_config::SCREENSIZE_ANY;
1246 return true;
1247 } else if (strcmp(name, "small") == 0) {
1248 if (out) out->screenLayout =
1249 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1250 | ResTable_config::SCREENSIZE_SMALL;
1251 return true;
1252 } else if (strcmp(name, "normal") == 0) {
1253 if (out) out->screenLayout =
1254 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1255 | ResTable_config::SCREENSIZE_NORMAL;
1256 return true;
1257 } else if (strcmp(name, "large") == 0) {
1258 if (out) out->screenLayout =
1259 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1260 | ResTable_config::SCREENSIZE_LARGE;
1261 return true;
Dianne Hackborn14cee9f2010-04-23 17:51:26 -07001262 } else if (strcmp(name, "xlarge") == 0) {
1263 if (out) out->screenLayout =
1264 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
1265 | ResTable_config::SCREENSIZE_XLARGE;
1266 return true;
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001267 }
1268
1269 return false;
1270}
1271
1272bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
1273 ResTable_config* out)
1274{
1275 if (strcmp(name, kWildcardName) == 0) {
1276 if (out) out->screenLayout =
1277 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1278 | ResTable_config::SCREENLONG_ANY;
1279 return true;
1280 } else if (strcmp(name, "long") == 0) {
1281 if (out) out->screenLayout =
1282 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1283 | ResTable_config::SCREENLONG_YES;
1284 return true;
1285 } else if (strcmp(name, "notlong") == 0) {
1286 if (out) out->screenLayout =
1287 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
1288 | ResTable_config::SCREENLONG_NO;
1289 return true;
1290 }
1291
1292 return false;
1293}
1294
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295bool AaptGroupEntry::getOrientationName(const char* name,
1296 ResTable_config* out)
1297{
1298 if (strcmp(name, kWildcardName) == 0) {
1299 if (out) out->orientation = out->ORIENTATION_ANY;
1300 return true;
1301 } else if (strcmp(name, "port") == 0) {
1302 if (out) out->orientation = out->ORIENTATION_PORT;
1303 return true;
1304 } else if (strcmp(name, "land") == 0) {
1305 if (out) out->orientation = out->ORIENTATION_LAND;
1306 return true;
1307 } else if (strcmp(name, "square") == 0) {
1308 if (out) out->orientation = out->ORIENTATION_SQUARE;
1309 return true;
1310 }
1311
1312 return false;
1313}
1314
Tobias Haamel27b28b32010-02-09 23:09:17 +01001315bool AaptGroupEntry::getUiModeTypeName(const char* name,
1316 ResTable_config* out)
1317{
1318 if (strcmp(name, kWildcardName) == 0) {
1319 if (out) out->uiMode =
1320 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackborn7299c412010-03-04 18:41:49 -08001321 | ResTable_config::UI_MODE_TYPE_ANY;
1322 return true;
1323 } else if (strcmp(name, "desk") == 0) {
1324 if (out) out->uiMode =
1325 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1326 | ResTable_config::UI_MODE_TYPE_DESK;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001327 return true;
1328 } else if (strcmp(name, "car") == 0) {
1329 if (out) out->uiMode =
1330 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1331 | ResTable_config::UI_MODE_TYPE_CAR;
1332 return true;
Dianne Hackborne360bb62011-05-20 16:11:04 -07001333 } else if (strcmp(name, "television") == 0) {
1334 if (out) out->uiMode =
1335 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1336 | ResTable_config::UI_MODE_TYPE_TELEVISION;
1337 return true;
Joe Onorato44fcb832011-12-14 20:59:30 -08001338 } else if (strcmp(name, "appliance") == 0) {
1339 if (out) out->uiMode =
1340 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
1341 | ResTable_config::UI_MODE_TYPE_APPLIANCE;
1342 return true;
Tobias Haamel27b28b32010-02-09 23:09:17 +01001343 }
1344
1345 return false;
1346}
1347
1348bool AaptGroupEntry::getUiModeNightName(const char* name,
1349 ResTable_config* out)
1350{
1351 if (strcmp(name, kWildcardName) == 0) {
1352 if (out) out->uiMode =
1353 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1354 | ResTable_config::UI_MODE_NIGHT_ANY;
1355 return true;
1356 } else if (strcmp(name, "night") == 0) {
1357 if (out) out->uiMode =
1358 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1359 | ResTable_config::UI_MODE_NIGHT_YES;
1360 return true;
1361 } else if (strcmp(name, "notnight") == 0) {
1362 if (out) out->uiMode =
1363 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
1364 | ResTable_config::UI_MODE_NIGHT_NO;
1365 return true;
1366 }
1367
1368 return false;
1369}
1370
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371bool AaptGroupEntry::getDensityName(const char* name,
1372 ResTable_config* out)
1373{
1374 if (strcmp(name, kWildcardName) == 0) {
Dianne Hackborna53b8282009-07-17 11:13:48 -07001375 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 return true;
1377 }
Dianne Hackborna53b8282009-07-17 11:13:48 -07001378
1379 if (strcmp(name, "nodpi") == 0) {
1380 if (out) out->density = ResTable_config::DENSITY_NONE;
1381 return true;
1382 }
1383
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001384 if (strcmp(name, "ldpi") == 0) {
1385 if (out) out->density = ResTable_config::DENSITY_LOW;
1386 return true;
1387 }
1388
1389 if (strcmp(name, "mdpi") == 0) {
1390 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
1391 return true;
1392 }
1393
Dianne Hackbornb96cbbd2011-05-27 13:40:26 -07001394 if (strcmp(name, "tvdpi") == 0) {
1395 if (out) out->density = ResTable_config::DENSITY_TV;
1396 return true;
1397 }
1398
Dianne Hackbornc4db95c2009-07-21 17:46:02 -07001399 if (strcmp(name, "hdpi") == 0) {
1400 if (out) out->density = ResTable_config::DENSITY_HIGH;
1401 return true;
1402 }
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001403
Dianne Hackborn588feee2010-06-04 14:36:39 -07001404 if (strcmp(name, "xhdpi") == 0) {
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001405 if (out) out->density = ResTable_config::DENSITY_XHIGH;
Dianne Hackborn588feee2010-06-04 14:36:39 -07001406 return true;
1407 }
Dianne Hackbornd96e3df2012-01-25 15:12:23 -08001408
1409 if (strcmp(name, "xxhdpi") == 0) {
1410 if (out) out->density = ResTable_config::DENSITY_XXHIGH;
1411 return true;
1412 }
1413
Dianne Hackborn56a23012013-02-12 15:41:49 -08001414 if (strcmp(name, "xxxhdpi") == 0) {
1415 if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
1416 return true;
1417 }
1418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 char* c = (char*)name;
1420 while (*c >= '0' && *c <= '9') {
1421 c++;
1422 }
1423
1424 // check that we have 'dpi' after the last digit.
1425 if (toupper(c[0]) != 'D' ||
1426 toupper(c[1]) != 'P' ||
1427 toupper(c[2]) != 'I' ||
1428 c[3] != 0) {
1429 return false;
1430 }
1431
1432 // temporarily replace the first letter with \0 to
1433 // use atoi.
1434 char tmp = c[0];
1435 c[0] = '\0';
1436
1437 int d = atoi(name);
1438 c[0] = tmp;
1439
1440 if (d != 0) {
1441 if (out) out->density = d;
1442 return true;
1443 }
1444
1445 return false;
1446}
1447
1448bool AaptGroupEntry::getTouchscreenName(const char* name,
1449 ResTable_config* out)
1450{
1451 if (strcmp(name, kWildcardName) == 0) {
1452 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1453 return true;
1454 } else if (strcmp(name, "notouch") == 0) {
1455 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1456 return true;
1457 } else if (strcmp(name, "stylus") == 0) {
1458 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1459 return true;
1460 } else if (strcmp(name, "finger") == 0) {
1461 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1462 return true;
1463 }
1464
1465 return false;
1466}
1467
1468bool AaptGroupEntry::getKeysHiddenName(const char* name,
1469 ResTable_config* out)
1470{
1471 uint8_t mask = 0;
1472 uint8_t value = 0;
1473 if (strcmp(name, kWildcardName) == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001474 mask = ResTable_config::MASK_KEYSHIDDEN;
1475 value = ResTable_config::KEYSHIDDEN_ANY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 } else if (strcmp(name, "keysexposed") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001477 mask = ResTable_config::MASK_KEYSHIDDEN;
1478 value = ResTable_config::KEYSHIDDEN_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 } else if (strcmp(name, "keyshidden") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001480 mask = ResTable_config::MASK_KEYSHIDDEN;
1481 value = ResTable_config::KEYSHIDDEN_YES;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 } else if (strcmp(name, "keyssoft") == 0) {
Kenny Rootfedfea22010-02-18 08:54:47 -08001483 mask = ResTable_config::MASK_KEYSHIDDEN;
1484 value = ResTable_config::KEYSHIDDEN_SOFT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 }
1486
1487 if (mask != 0) {
1488 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1489 return true;
1490 }
1491
1492 return false;
1493}
1494
1495bool AaptGroupEntry::getKeyboardName(const char* name,
1496 ResTable_config* out)
1497{
1498 if (strcmp(name, kWildcardName) == 0) {
1499 if (out) out->keyboard = out->KEYBOARD_ANY;
1500 return true;
1501 } else if (strcmp(name, "nokeys") == 0) {
1502 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1503 return true;
1504 } else if (strcmp(name, "qwerty") == 0) {
1505 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1506 return true;
1507 } else if (strcmp(name, "12key") == 0) {
1508 if (out) out->keyboard = out->KEYBOARD_12KEY;
1509 return true;
1510 }
1511
1512 return false;
1513}
1514
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001515bool AaptGroupEntry::getNavHiddenName(const char* name,
1516 ResTable_config* out)
1517{
1518 uint8_t mask = 0;
1519 uint8_t value = 0;
1520 if (strcmp(name, kWildcardName) == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001521 mask = ResTable_config::MASK_NAVHIDDEN;
1522 value = ResTable_config::NAVHIDDEN_ANY;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001523 } else if (strcmp(name, "navexposed") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001524 mask = ResTable_config::MASK_NAVHIDDEN;
1525 value = ResTable_config::NAVHIDDEN_NO;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001526 } else if (strcmp(name, "navhidden") == 0) {
Kenny Roote599f782010-02-19 12:45:48 -08001527 mask = ResTable_config::MASK_NAVHIDDEN;
1528 value = ResTable_config::NAVHIDDEN_YES;
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001529 }
1530
1531 if (mask != 0) {
1532 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1533 return true;
1534 }
1535
1536 return false;
1537}
1538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539bool AaptGroupEntry::getNavigationName(const char* name,
1540 ResTable_config* out)
1541{
1542 if (strcmp(name, kWildcardName) == 0) {
1543 if (out) out->navigation = out->NAVIGATION_ANY;
1544 return true;
1545 } else if (strcmp(name, "nonav") == 0) {
1546 if (out) out->navigation = out->NAVIGATION_NONAV;
1547 return true;
1548 } else if (strcmp(name, "dpad") == 0) {
1549 if (out) out->navigation = out->NAVIGATION_DPAD;
1550 return true;
1551 } else if (strcmp(name, "trackball") == 0) {
1552 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1553 return true;
1554 } else if (strcmp(name, "wheel") == 0) {
1555 if (out) out->navigation = out->NAVIGATION_WHEEL;
1556 return true;
1557 }
1558
1559 return false;
1560}
1561
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001562bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563{
1564 if (strcmp(name, kWildcardName) == 0) {
1565 if (out) {
1566 out->screenWidth = out->SCREENWIDTH_ANY;
1567 out->screenHeight = out->SCREENHEIGHT_ANY;
1568 }
1569 return true;
1570 }
1571
1572 const char* x = name;
1573 while (*x >= '0' && *x <= '9') x++;
1574 if (x == name || *x != 'x') return false;
1575 String8 xName(name, x-name);
1576 x++;
1577
1578 const char* y = x;
1579 while (*y >= '0' && *y <= '9') y++;
1580 if (y == name || *y != 0) return false;
1581 String8 yName(x, y-x);
1582
1583 uint16_t w = (uint16_t)atoi(xName.string());
1584 uint16_t h = (uint16_t)atoi(yName.string());
1585 if (w < h) {
1586 return false;
1587 }
1588
1589 if (out) {
1590 out->screenWidth = w;
1591 out->screenHeight = h;
1592 }
1593
1594 return true;
1595}
1596
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001597bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1598{
1599 if (strcmp(name, kWildcardName) == 0) {
1600 if (out) {
1601 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1602 }
1603 return true;
1604 }
1605
1606 if (*name != 's') return false;
1607 name++;
1608 if (*name != 'w') return false;
1609 name++;
1610 const char* x = name;
1611 while (*x >= '0' && *x <= '9') x++;
1612 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1613 String8 xName(name, x-name);
1614
1615 if (out) {
1616 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1617 }
1618
1619 return true;
1620}
1621
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001622bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1623{
1624 if (strcmp(name, kWildcardName) == 0) {
1625 if (out) {
1626 out->screenWidthDp = out->SCREENWIDTH_ANY;
1627 }
1628 return true;
1629 }
1630
1631 if (*name != 'w') return false;
1632 name++;
1633 const char* x = name;
1634 while (*x >= '0' && *x <= '9') x++;
1635 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1636 String8 xName(name, x-name);
1637
1638 if (out) {
1639 out->screenWidthDp = (uint16_t)atoi(xName.string());
1640 }
1641
1642 return true;
1643}
1644
1645bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1646{
1647 if (strcmp(name, kWildcardName) == 0) {
1648 if (out) {
1649 out->screenHeightDp = out->SCREENWIDTH_ANY;
1650 }
1651 return true;
1652 }
1653
1654 if (*name != 'h') return false;
1655 name++;
1656 const char* x = name;
1657 while (*x >= '0' && *x <= '9') x++;
1658 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1659 String8 xName(name, x-name);
1660
1661 if (out) {
1662 out->screenHeightDp = (uint16_t)atoi(xName.string());
1663 }
1664
1665 return true;
1666}
1667
1668bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001669{
1670 if (strcmp(name, kWildcardName) == 0) {
1671 if (out) {
1672 out->sdkVersion = out->SDKVERSION_ANY;
1673 out->minorVersion = out->MINORVERSION_ANY;
1674 }
1675 return true;
1676 }
1677
1678 if (*name != 'v') {
1679 return false;
1680 }
1681
1682 name++;
1683 const char* s = name;
1684 while (*s >= '0' && *s <= '9') s++;
1685 if (s == name || *s != 0) return false;
1686 String8 sdkName(name, s-name);
1687
1688 if (out) {
1689 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1690 out->minorVersion = 0;
1691 }
1692
1693 return true;
1694}
1695
1696int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1697{
1698 int v = mcc.compare(o.mcc);
1699 if (v == 0) v = mnc.compare(o.mnc);
1700 if (v == 0) v = locale.compare(o.locale);
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001701 if (v == 0) v = layoutDirection.compare(o.layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001702 if (v == 0) v = vendor.compare(o.vendor);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001703 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001704 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1705 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001706 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1707 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 if (v == 0) v = orientation.compare(o.orientation);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001709 if (v == 0) v = uiModeType.compare(o.uiModeType);
1710 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001711 if (v == 0) v = density.compare(o.density);
1712 if (v == 0) v = touchscreen.compare(o.touchscreen);
1713 if (v == 0) v = keysHidden.compare(o.keysHidden);
1714 if (v == 0) v = keyboard.compare(o.keyboard);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001715 if (v == 0) v = navHidden.compare(o.navHidden);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 if (v == 0) v = navigation.compare(o.navigation);
1717 if (v == 0) v = screenSize.compare(o.screenSize);
1718 if (v == 0) v = version.compare(o.version);
1719 return v;
1720}
1721
Narayan Kamath788fa412014-01-21 15:32:36 +00001722const ResTable_config AaptGroupEntry::toParams() const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001724 if (!mParamsChanged) {
1725 return mParams;
1726 }
1727
1728 mParamsChanged = false;
Narayan Kamath788fa412014-01-21 15:32:36 +00001729 ResTable_config& params = mParams;
1730 memset(&params, 0, sizeof(ResTable_config));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 getMccName(mcc.string(), &params);
1732 getMncName(mnc.string(), &params);
Narayan Kamath788fa412014-01-21 15:32:36 +00001733 locale.writeTo(&params);
Fabrice Di Meglio5f797992012-06-15 20:16:41 -07001734 getLayoutDirectionName(layoutDirection.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001735 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001736 getScreenWidthDpName(screenWidthDp.string(), &params);
1737 getScreenHeightDpName(screenHeightDp.string(), &params);
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001738 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1739 getScreenLayoutLongName(screenLayoutLong.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 getOrientationName(orientation.string(), &params);
Tobias Haamel27b28b32010-02-09 23:09:17 +01001741 getUiModeTypeName(uiModeType.string(), &params);
1742 getUiModeNightName(uiModeNight.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 getDensityName(density.string(), &params);
1744 getTouchscreenName(touchscreen.string(), &params);
1745 getKeysHiddenName(keysHidden.string(), &params);
1746 getKeyboardName(keyboard.string(), &params);
Dianne Hackborn93e462b2009-09-15 22:50:40 -07001747 getNavHiddenName(navHidden.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 getNavigationName(navigation.string(), &params);
1749 getScreenSizeName(screenSize.string(), &params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 getVersionName(version.string(), &params);
Dianne Hackbornef05e072010-03-01 17:43:39 -08001751
1752 // Fix up version number based on specified parameters.
1753 int minSdk = 0;
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001754 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1755 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001756 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
Dianne Hackborn69cb8752011-05-19 18:13:32 -07001757 minSdk = SDK_HONEYCOMB_MR2;
Dianne Hackbornebff8f92011-05-12 18:07:47 -07001758 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
Dianne Hackbornef05e072010-03-01 17:43:39 -08001759 != ResTable_config::UI_MODE_TYPE_ANY
1760 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1761 != ResTable_config::UI_MODE_NIGHT_ANY) {
1762 minSdk = SDK_FROYO;
1763 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1764 != ResTable_config::SCREENSIZE_ANY
1765 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1766 != ResTable_config::SCREENLONG_ANY
1767 || params.density != ResTable_config::DENSITY_DEFAULT) {
1768 minSdk = SDK_DONUT;
1769 }
1770
1771 if (minSdk > params.sdkVersion) {
1772 params.sdkVersion = minSdk;
1773 }
1774
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001775 return params;
1776}
1777
1778// =========================================================================
1779// =========================================================================
1780// =========================================================================
1781
1782void* AaptFile::editData(size_t size)
1783{
1784 if (size <= mBufferSize) {
1785 mDataSize = size;
1786 return mData;
1787 }
1788 size_t allocSize = (size*3)/2;
1789 void* buf = realloc(mData, allocSize);
1790 if (buf == NULL) {
1791 return NULL;
1792 }
1793 mData = buf;
1794 mDataSize = size;
1795 mBufferSize = allocSize;
1796 return buf;
1797}
1798
1799void* AaptFile::editData(size_t* outSize)
1800{
1801 if (outSize) {
1802 *outSize = mDataSize;
1803 }
1804 return mData;
1805}
1806
1807void* AaptFile::padData(size_t wordSize)
1808{
1809 const size_t extra = mDataSize%wordSize;
1810 if (extra == 0) {
1811 return mData;
1812 }
1813
1814 size_t initial = mDataSize;
1815 void* data = editData(initial+(wordSize-extra));
1816 if (data != NULL) {
1817 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1818 }
1819 return data;
1820}
1821
1822status_t AaptFile::writeData(const void* data, size_t size)
1823{
1824 size_t end = mDataSize;
1825 size_t total = size + end;
1826 void* buf = editData(total);
1827 if (buf == NULL) {
1828 return UNKNOWN_ERROR;
1829 }
1830 memcpy(((char*)buf)+end, data, size);
1831 return NO_ERROR;
1832}
1833
1834void AaptFile::clearData()
1835{
1836 if (mData != NULL) free(mData);
1837 mData = NULL;
1838 mDataSize = 0;
1839 mBufferSize = 0;
1840}
1841
1842String8 AaptFile::getPrintableSource() const
1843{
1844 if (hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001845 String8 name(mGroupEntry.toDirName(String8()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 name.appendPath(mPath);
1847 name.append(" #generated");
1848 return name;
1849 }
1850 return mSourceFile;
1851}
1852
1853// =========================================================================
1854// =========================================================================
1855// =========================================================================
1856
1857status_t AaptGroup::addFile(const sp<AaptFile>& file)
1858{
1859 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1860 file->mPath = mPath;
1861 mFiles.add(file->getGroupEntry(), file);
1862 return NO_ERROR;
1863 }
1864
Dianne Hackborne6b68032011-10-13 16:26:02 -07001865#if 0
1866 printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
1867 file->getSourceFile().string(),
1868 file->getGroupEntry().toDirName(String8()).string(),
1869 mLeaf.string(), mPath.string());
1870#endif
1871
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1873 getPrintableSource().string());
1874 return UNKNOWN_ERROR;
1875}
1876
1877void AaptGroup::removeFile(size_t index)
1878{
1879 mFiles.removeItemsAt(index);
1880}
1881
Dianne Hackborne6b68032011-10-13 16:26:02 -07001882void AaptGroup::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883{
Dianne Hackborne6b68032011-10-13 16:26:02 -07001884 printf("%s%s\n", prefix.string(), getPath().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 const size_t N=mFiles.size();
1886 size_t i;
1887 for (i=0; i<N; i++) {
1888 sp<AaptFile> file = mFiles.valueAt(i);
1889 const AaptGroupEntry& e = file->getGroupEntry();
1890 if (file->hasData()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001891 printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001892 (int)file->getSize());
1893 } else {
Dianne Hackborne6b68032011-10-13 16:26:02 -07001894 printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
1895 file->getPrintableSource().string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001896 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07001897 //printf("%s File Group Entry: %s\n", prefix.string(),
1898 // file->getGroupEntry().toDirName(String8()).string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001899 }
1900}
1901
1902String8 AaptGroup::getPrintableSource() const
1903{
1904 if (mFiles.size() > 0) {
1905 // Arbitrarily pull the first source file out of the list.
1906 return mFiles.valueAt(0)->getPrintableSource();
1907 }
1908
1909 // Should never hit this case, but to be safe...
1910 return getPath();
1911
1912}
1913
1914// =========================================================================
1915// =========================================================================
1916// =========================================================================
1917
1918status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1919{
1920 if (mFiles.indexOfKey(name) >= 0) {
1921 return ALREADY_EXISTS;
1922 }
1923 mFiles.add(name, file);
1924 return NO_ERROR;
1925}
1926
1927status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1928{
1929 if (mDirs.indexOfKey(name) >= 0) {
1930 return ALREADY_EXISTS;
1931 }
1932 mDirs.add(name, dir);
1933 return NO_ERROR;
1934}
1935
1936sp<AaptDir> AaptDir::makeDir(const String8& path)
1937{
1938 String8 name;
1939 String8 remain = path;
1940
1941 sp<AaptDir> subdir = this;
1942 while (name = remain.walkPath(&remain), remain != "") {
1943 subdir = subdir->makeDir(name);
1944 }
1945
1946 ssize_t i = subdir->mDirs.indexOfKey(name);
1947 if (i >= 0) {
1948 return subdir->mDirs.valueAt(i);
1949 }
1950 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1951 subdir->mDirs.add(name, dir);
1952 return dir;
1953}
1954
1955void AaptDir::removeFile(const String8& name)
1956{
1957 mFiles.removeItem(name);
1958}
1959
1960void AaptDir::removeDir(const String8& name)
1961{
1962 mDirs.removeItem(name);
1963}
1964
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001965status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1966{
1967 sp<AaptGroup> group;
1968 if (mFiles.indexOfKey(leafName) >= 0) {
1969 group = mFiles.valueFor(leafName);
1970 } else {
1971 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1972 mFiles.add(leafName, group);
1973 }
1974
1975 return group->addFile(file);
1976}
1977
1978ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07001979 const AaptGroupEntry& kind, const String8& resType,
1980 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001981{
1982 Vector<String8> fileNames;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001983 {
1984 DIR* dir = NULL;
1985
1986 dir = opendir(srcDir.string());
1987 if (dir == NULL) {
1988 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1989 return UNKNOWN_ERROR;
1990 }
1991
1992 /*
1993 * Slurp the filenames out of the directory.
1994 */
1995 while (1) {
1996 struct dirent* entry;
1997
1998 entry = readdir(dir);
1999 if (entry == NULL)
2000 break;
2001
2002 if (isHidden(srcDir.string(), entry->d_name))
2003 continue;
2004
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002005 String8 name(entry->d_name);
2006 fileNames.add(name);
2007 // Add fully qualified path for dependency purposes
2008 // if we're collecting them
2009 if (fullResPaths != NULL) {
2010 fullResPaths->add(srcDir.appendPathCopy(name));
2011 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002012 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 closedir(dir);
2014 }
2015
2016 ssize_t count = 0;
2017
2018 /*
2019 * Stash away the files and recursively descend into subdirectories.
2020 */
2021 const size_t N = fileNames.size();
2022 size_t i;
2023 for (i = 0; i < N; i++) {
2024 String8 pathName(srcDir);
2025 FileType type;
2026
2027 pathName.appendPath(fileNames[i].string());
2028 type = getFileType(pathName.string());
2029 if (type == kFileTypeDirectory) {
2030 sp<AaptDir> subdir;
2031 bool notAdded = false;
2032 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
2033 subdir = mDirs.valueFor(fileNames[i]);
2034 } else {
2035 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
2036 notAdded = true;
2037 }
2038 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002039 resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002040 if (res < NO_ERROR) {
2041 return res;
2042 }
2043 if (res > 0 && notAdded) {
2044 mDirs.add(fileNames[i], subdir);
2045 }
2046 count += res;
2047 } else if (type == kFileTypeRegular) {
2048 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
2049 status_t err = addLeafFile(fileNames[i], file);
2050 if (err != NO_ERROR) {
2051 return err;
2052 }
2053
2054 count++;
2055
2056 } else {
2057 if (bundle->getVerbose())
2058 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
2059 }
2060 }
2061
2062 return count;
2063}
2064
2065status_t AaptDir::validate() const
2066{
2067 const size_t NF = mFiles.size();
2068 const size_t ND = mDirs.size();
2069 size_t i;
2070 for (i = 0; i < NF; i++) {
2071 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
2072 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2073 "Invalid filename. Unable to add.");
2074 return UNKNOWN_ERROR;
2075 }
2076
2077 size_t j;
2078 for (j = i+1; j < NF; j++) {
2079 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2080 mFiles.valueAt(j)->getLeaf().string()) == 0) {
2081 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2082 "File is case-insensitive equivalent to: %s",
2083 mFiles.valueAt(j)->getPrintableSource().string());
2084 return UNKNOWN_ERROR;
2085 }
2086
2087 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
2088 // (this is mostly caught by the "marked" stuff, below)
2089 }
2090
2091 for (j = 0; j < ND; j++) {
2092 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
2093 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2094 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
2095 "File conflicts with dir from: %s",
2096 mDirs.valueAt(j)->getPrintableSource().string());
2097 return UNKNOWN_ERROR;
2098 }
2099 }
2100 }
2101
2102 for (i = 0; i < ND; i++) {
2103 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
2104 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2105 "Invalid directory name, unable to add.");
2106 return UNKNOWN_ERROR;
2107 }
2108
2109 size_t j;
2110 for (j = i+1; j < ND; j++) {
2111 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
2112 mDirs.valueAt(j)->getLeaf().string()) == 0) {
2113 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
2114 "Directory is case-insensitive equivalent to: %s",
2115 mDirs.valueAt(j)->getPrintableSource().string());
2116 return UNKNOWN_ERROR;
2117 }
2118 }
2119
2120 status_t err = mDirs.valueAt(i)->validate();
2121 if (err != NO_ERROR) {
2122 return err;
2123 }
2124 }
2125
2126 return NO_ERROR;
2127}
2128
Dianne Hackborne6b68032011-10-13 16:26:02 -07002129void AaptDir::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130{
2131 const size_t ND=getDirs().size();
2132 size_t i;
2133 for (i=0; i<ND; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002134 getDirs().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002135 }
2136
2137 const size_t NF=getFiles().size();
2138 for (i=0; i<NF; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002139 getFiles().valueAt(i)->print(prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002140 }
2141}
2142
2143String8 AaptDir::getPrintableSource() const
2144{
2145 if (mFiles.size() > 0) {
2146 // Arbitrarily pull the first file out of the list as the source dir.
2147 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
2148 }
2149 if (mDirs.size() > 0) {
2150 // Or arbitrarily pull the first dir out of the list as the source dir.
2151 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
2152 }
2153
2154 // Should never hit this case, but to be safe...
2155 return mPath;
2156
2157}
2158
2159// =========================================================================
2160// =========================================================================
2161// =========================================================================
2162
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002163status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols)
2164{
2165 status_t err = NO_ERROR;
2166 size_t N = javaSymbols->mSymbols.size();
2167 for (size_t i=0; i<N; i++) {
2168 const String8& name = javaSymbols->mSymbols.keyAt(i);
2169 const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
2170 ssize_t pos = mSymbols.indexOfKey(name);
2171 if (pos < 0) {
2172 entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
2173 err = UNKNOWN_ERROR;
2174 continue;
2175 }
2176 //printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
2177 // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
2178 mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
2179 }
2180
2181 N = javaSymbols->mNestedSymbols.size();
2182 for (size_t i=0; i<N; i++) {
2183 const String8& name = javaSymbols->mNestedSymbols.keyAt(i);
2184 const sp<AaptSymbols>& symbols = javaSymbols->mNestedSymbols.valueAt(i);
2185 ssize_t pos = mNestedSymbols.indexOfKey(name);
2186 if (pos < 0) {
2187 SourcePos pos;
2188 pos.error("Java symbol dir %s not defined\n", name.string());
2189 err = UNKNOWN_ERROR;
2190 continue;
2191 }
2192 //printf("**** applying java symbols in dir %s\n", name.string());
2193 status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2194 if (myerr != NO_ERROR) {
2195 err = myerr;
2196 }
2197 }
2198
2199 return err;
2200}
2201
2202// =========================================================================
2203// =========================================================================
2204// =========================================================================
2205
Dianne Hackborne6b68032011-10-13 16:26:02 -07002206AaptAssets::AaptAssets()
2207 : AaptDir(String8(), String8()),
Narayan Kamath788fa412014-01-21 15:32:36 +00002208 mHavePrivateSymbols(false),
2209 mChanged(false), mHaveIncludedAssets(false),
2210 mRes(NULL)
Dianne Hackborne6b68032011-10-13 16:26:02 -07002211{
2212}
2213
2214const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
2215 if (mChanged) {
2216 }
2217 return mGroupEntries;
2218}
2219
2220status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
2221{
2222 mChanged = true;
2223 return AaptDir::addFile(name, file);
2224}
2225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002226sp<AaptFile> AaptAssets::addFile(
2227 const String8& filePath, const AaptGroupEntry& entry,
2228 const String8& srcDir, sp<AaptGroup>* outGroup,
2229 const String8& resType)
2230{
2231 sp<AaptDir> dir = this;
2232 sp<AaptGroup> group;
2233 sp<AaptFile> file;
2234 String8 root, remain(filePath), partialPath;
2235 while (remain.length() > 0) {
2236 root = remain.walkPath(&remain);
2237 partialPath.appendPath(root);
2238
2239 const String8 rootStr(root);
2240
2241 if (remain.length() == 0) {
2242 ssize_t i = dir->getFiles().indexOfKey(rootStr);
2243 if (i >= 0) {
2244 group = dir->getFiles().valueAt(i);
2245 } else {
2246 group = new AaptGroup(rootStr, filePath);
2247 status_t res = dir->addFile(rootStr, group);
2248 if (res != NO_ERROR) {
2249 return NULL;
2250 }
2251 }
2252 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
2253 status_t res = group->addFile(file);
2254 if (res != NO_ERROR) {
2255 return NULL;
2256 }
2257 break;
2258
2259 } else {
2260 ssize_t i = dir->getDirs().indexOfKey(rootStr);
2261 if (i >= 0) {
2262 dir = dir->getDirs().valueAt(i);
2263 } else {
2264 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
2265 status_t res = dir->addDir(rootStr, subdir);
2266 if (res != NO_ERROR) {
2267 return NULL;
2268 }
2269 dir = subdir;
2270 }
2271 }
2272 }
2273
2274 mGroupEntries.add(entry);
2275 if (outGroup) *outGroup = group;
2276 return file;
2277}
2278
2279void AaptAssets::addResource(const String8& leafName, const String8& path,
2280 const sp<AaptFile>& file, const String8& resType)
2281{
2282 sp<AaptDir> res = AaptDir::makeDir(kResString);
2283 String8 dirname = file->getGroupEntry().toDirName(resType);
2284 sp<AaptDir> subdir = res->makeDir(dirname);
2285 sp<AaptGroup> grr = new AaptGroup(leafName, path);
2286 grr->addFile(file);
2287
2288 subdir->addFile(leafName, grr);
2289}
2290
2291
2292ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
2293{
2294 int count;
2295 int totalCount = 0;
2296 FileType type;
2297 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
2298 const size_t dirCount =resDirs.size();
2299 sp<AaptAssets> current = this;
2300
2301 const int N = bundle->getFileSpecCount();
2302
2303 /*
2304 * If a package manifest was specified, include that first.
2305 */
2306 if (bundle->getAndroidManifestFile() != NULL) {
2307 // place at root of zip.
2308 String8 srcFile(bundle->getAndroidManifestFile());
2309 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
2310 NULL, String8());
2311 totalCount++;
2312 }
2313
2314 /*
2315 * If a directory of custom assets was supplied, slurp 'em up.
2316 */
2317 if (bundle->getAssetSourceDir()) {
2318 const char* assetDir = bundle->getAssetSourceDir();
2319
2320 FileType type = getFileType(assetDir);
2321 if (type == kFileTypeNonexistent) {
2322 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
2323 return UNKNOWN_ERROR;
2324 }
2325 if (type != kFileTypeDirectory) {
2326 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2327 return UNKNOWN_ERROR;
2328 }
2329
2330 String8 assetRoot(assetDir);
2331 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
2332 AaptGroupEntry group;
2333 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002334 String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002335 if (count < 0) {
2336 totalCount = count;
2337 goto bail;
2338 }
2339 if (count > 0) {
2340 mGroupEntries.add(group);
2341 }
2342 totalCount += count;
2343
2344 if (bundle->getVerbose())
2345 printf("Found %d custom asset file%s in %s\n",
2346 count, (count==1) ? "" : "s", assetDir);
2347 }
2348
2349 /*
2350 * If a directory of resource-specific assets was supplied, slurp 'em up.
2351 */
2352 for (size_t i=0; i<dirCount; i++) {
2353 const char *res = resDirs[i];
2354 if (res) {
2355 type = getFileType(res);
2356 if (type == kFileTypeNonexistent) {
2357 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
2358 return UNKNOWN_ERROR;
2359 }
2360 if (type == kFileTypeDirectory) {
2361 if (i>0) {
2362 sp<AaptAssets> nextOverlay = new AaptAssets();
2363 current->setOverlay(nextOverlay);
2364 current = nextOverlay;
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002365 current->setFullResPaths(mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002366 }
2367 count = current->slurpResourceTree(bundle, String8(res));
2368
2369 if (count < 0) {
2370 totalCount = count;
2371 goto bail;
2372 }
2373 totalCount += count;
2374 }
2375 else {
2376 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
2377 return UNKNOWN_ERROR;
2378 }
2379 }
2380
2381 }
2382 /*
2383 * Now do any additional raw files.
2384 */
2385 for (int arg=0; arg<N; arg++) {
2386 const char* assetDir = bundle->getFileSpecEntry(arg);
2387
2388 FileType type = getFileType(assetDir);
2389 if (type == kFileTypeNonexistent) {
2390 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
2391 return UNKNOWN_ERROR;
2392 }
2393 if (type != kFileTypeDirectory) {
2394 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
2395 return UNKNOWN_ERROR;
2396 }
2397
2398 String8 assetRoot(assetDir);
2399
2400 if (bundle->getVerbose())
2401 printf("Processing raw dir '%s'\n", (const char*) assetDir);
2402
2403 /*
2404 * Do a recursive traversal of subdir tree. We don't make any
2405 * guarantees about ordering, so we're okay with an inorder search
2406 * using whatever order the OS happens to hand back to us.
2407 */
Josiah Gaskin03589cc2011-06-27 16:26:02 -07002408 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002409 if (count < 0) {
2410 /* failure; report error and remove archive */
2411 totalCount = count;
2412 goto bail;
2413 }
2414 totalCount += count;
2415
2416 if (bundle->getVerbose())
2417 printf("Found %d asset file%s in %s\n",
2418 count, (count==1) ? "" : "s", assetDir);
2419 }
2420
2421 count = validate();
2422 if (count != NO_ERROR) {
2423 totalCount = count;
2424 goto bail;
2425 }
2426
Dianne Hackborne6b68032011-10-13 16:26:02 -07002427 count = filter(bundle);
2428 if (count != NO_ERROR) {
2429 totalCount = count;
2430 goto bail;
2431 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002432
2433bail:
2434 return totalCount;
2435}
2436
2437ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
2438 const AaptGroupEntry& kind,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002439 const String8& resType,
2440 sp<FilePathStore>& fullResPaths)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002441{
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002442 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002443 if (res > 0) {
2444 mGroupEntries.add(kind);
2445 }
2446
2447 return res;
2448}
2449
2450ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
2451{
2452 ssize_t err = 0;
2453
2454 DIR* dir = opendir(srcDir.string());
2455 if (dir == NULL) {
2456 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
2457 return UNKNOWN_ERROR;
2458 }
2459
2460 status_t count = 0;
2461
2462 /*
2463 * Run through the directory, looking for dirs that match the
2464 * expected pattern.
2465 */
2466 while (1) {
2467 struct dirent* entry = readdir(dir);
2468 if (entry == NULL) {
2469 break;
2470 }
2471
2472 if (isHidden(srcDir.string(), entry->d_name)) {
2473 continue;
2474 }
2475
2476 String8 subdirName(srcDir);
2477 subdirName.appendPath(entry->d_name);
2478
2479 AaptGroupEntry group;
2480 String8 resType;
2481 bool b = group.initFromDirName(entry->d_name, &resType);
2482 if (!b) {
2483 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2484 entry->d_name);
2485 err = -1;
2486 continue;
2487 }
2488
Dianne Hackborne6b68032011-10-13 16:26:02 -07002489 if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002490 int maxResInt = atoi(bundle->getMaxResVersion());
Dianne Hackborne6b68032011-10-13 16:26:02 -07002491 const char *verString = group.getVersionString().string();
Ficus Kirkpatrick588f2282010-08-13 14:13:08 -07002492 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2493 if (dirVersionInt > maxResInt) {
2494 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2495 continue;
2496 }
2497 }
2498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002499 FileType type = getFileType(subdirName.string());
2500
2501 if (type == kFileTypeDirectory) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002502 sp<AaptDir> dir = makeDir(resType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002503 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
Josiah Gaskin9bf34ca2011-06-14 13:57:09 -07002504 resType, mFullResPaths);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002505 if (res < 0) {
2506 count = res;
2507 goto bail;
2508 }
2509 if (res > 0) {
2510 mGroupEntries.add(group);
2511 count += res;
2512 }
2513
Dianne Hackborne6b68032011-10-13 16:26:02 -07002514 // Only add this directory if we don't already have a resource dir
2515 // for the current type. This ensures that we only add the dir once
2516 // for all configs.
2517 sp<AaptDir> rdir = resDir(resType);
2518 if (rdir == NULL) {
2519 mResDirs.add(dir);
2520 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002521 } else {
2522 if (bundle->getVerbose()) {
2523 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2524 }
2525 }
2526 }
2527
2528bail:
2529 closedir(dir);
2530 dir = NULL;
2531
2532 if (err != 0) {
2533 return err;
2534 }
2535 return count;
2536}
2537
2538ssize_t
Andreas Gampe2412f842014-09-30 20:55:57 -07002539AaptAssets::slurpResourceZip(Bundle* /* bundle */, const char* filename)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002540{
2541 int count = 0;
2542 SortedVector<AaptGroupEntry> entries;
2543
2544 ZipFile* zip = new ZipFile;
2545 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2546 if (err != NO_ERROR) {
2547 fprintf(stderr, "error opening zip file %s\n", filename);
2548 count = err;
2549 delete zip;
2550 return -1;
2551 }
2552
2553 const int N = zip->getNumEntries();
2554 for (int i=0; i<N; i++) {
2555 ZipEntry* entry = zip->getEntryByIndex(i);
2556 if (entry->getDeleted()) {
2557 continue;
2558 }
2559
2560 String8 entryName(entry->getFileName());
2561
2562 String8 dirName = entryName.getPathDir();
2563 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2564
2565 String8 resType;
2566 AaptGroupEntry kind;
2567
2568 String8 remain;
2569 if (entryName.walkPath(&remain) == kResourceDir) {
2570 // these are the resources, pull their type out of the directory name
2571 kind.initFromDirName(remain.walkPath().string(), &resType);
2572 } else {
2573 // these are untyped and don't have an AaptGroupEntry
2574 }
2575 if (entries.indexOf(kind) < 0) {
2576 entries.add(kind);
2577 mGroupEntries.add(kind);
2578 }
2579
2580 // use the one from the zip file if they both exist.
2581 dir->removeFile(entryName.getPathLeaf());
2582
2583 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2584 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2585 if (err != NO_ERROR) {
2586 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2587 count = err;
2588 goto bail;
2589 }
2590 file->setCompressionMethod(entry->getCompressionMethod());
2591
2592#if 0
2593 if (entryName == "AndroidManifest.xml") {
2594 printf("AndroidManifest.xml\n");
2595 }
2596 printf("\n\nfile: %s\n", entryName.string());
2597#endif
2598
2599 size_t len = entry->getUncompressedLen();
2600 void* data = zip->uncompress(entry);
2601 void* buf = file->editData(len);
2602 memcpy(buf, data, len);
2603
2604#if 0
2605 const int OFF = 0;
2606 const unsigned char* p = (unsigned char*)data;
2607 const unsigned char* end = p+len;
2608 p += OFF;
2609 for (int i=0; i<32 && p < end; i++) {
2610 printf("0x%03x ", i*0x10 + OFF);
2611 for (int j=0; j<0x10 && p < end; j++) {
2612 printf(" %02x", *p);
2613 p++;
2614 }
2615 printf("\n");
2616 }
2617#endif
2618
2619 free(data);
2620
2621 count++;
2622 }
2623
2624bail:
2625 delete zip;
2626 return count;
2627}
2628
Dianne Hackborne6b68032011-10-13 16:26:02 -07002629status_t AaptAssets::filter(Bundle* bundle)
2630{
2631 ResourceFilter reqFilter;
2632 status_t err = reqFilter.parse(bundle->getConfigurations());
2633 if (err != NO_ERROR) {
2634 return err;
2635 }
2636
2637 ResourceFilter prefFilter;
2638 err = prefFilter.parse(bundle->getPreferredConfigurations());
2639 if (err != NO_ERROR) {
2640 return err;
2641 }
2642
2643 if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
2644 return NO_ERROR;
2645 }
2646
Dianne Hackbornbd9d2bc2011-10-16 14:17:07 -07002647 if (bundle->getVerbose()) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002648 if (!reqFilter.isEmpty()) {
2649 printf("Applying required filter: %s\n",
2650 bundle->getConfigurations());
2651 }
2652 if (!prefFilter.isEmpty()) {
2653 printf("Applying preferred filter: %s\n",
2654 bundle->getPreferredConfigurations());
2655 }
2656 }
2657
2658 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2659 const size_t ND = resdirs.size();
2660 for (size_t i=0; i<ND; i++) {
2661 const sp<AaptDir>& dir = resdirs.itemAt(i);
2662 if (dir->getLeaf() == kValuesDir) {
2663 // The "value" dir is special since a single file defines
2664 // multiple resources, so we can not do filtering on the
2665 // files themselves.
2666 continue;
2667 }
2668 if (dir->getLeaf() == kMipmapDir) {
2669 // We also skip the "mipmap" directory, since the point of this
2670 // is to include all densities without stripping. If you put
2671 // other configurations in here as well they won't be stripped
2672 // either... So don't do that. Seriously. What is wrong with you?
2673 continue;
2674 }
2675
2676 const size_t NG = dir->getFiles().size();
2677 for (size_t j=0; j<NG; j++) {
2678 sp<AaptGroup> grp = dir->getFiles().valueAt(j);
2679
2680 // First remove any configurations we know we don't need.
2681 for (size_t k=0; k<grp->getFiles().size(); k++) {
2682 sp<AaptFile> file = grp->getFiles().valueAt(k);
2683 if (k == 0 && grp->getFiles().size() == 1) {
2684 // If this is the only file left, we need to keep it.
2685 // Otherwise the resource IDs we are using will be inconsistent
2686 // with what we get when not stripping. Sucky, but at least
2687 // for now we can rely on the back-end doing another filtering
2688 // pass to take this out and leave us with this resource name
2689 // containing no entries.
2690 continue;
2691 }
2692 if (file->getPath().getPathExtension() == ".xml") {
2693 // We can't remove .xml files at this point, because when
2694 // we parse them they may add identifier resources, so
2695 // removing them can cause our resource identifiers to
2696 // become inconsistent.
2697 continue;
2698 }
2699 const ResTable_config& config(file->getGroupEntry().toParams());
2700 if (!reqFilter.match(config)) {
2701 if (bundle->getVerbose()) {
2702 printf("Pruning unneeded resource: %s\n",
2703 file->getPrintableSource().string());
2704 }
2705 grp->removeFile(k);
2706 k--;
2707 }
2708 }
2709
2710 // Quick check: no preferred filters, nothing more to do.
2711 if (prefFilter.isEmpty()) {
2712 continue;
2713 }
2714
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002715 // Get the preferred density if there is one. We do not match exactly for density.
2716 // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
2717 // pick xhdpi.
2718 uint32_t preferredDensity = 0;
Narayan Kamath788fa412014-01-21 15:32:36 +00002719 const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002720 if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
Narayan Kamath788fa412014-01-21 15:32:36 +00002721 preferredDensity = (*preferredConfigs)[0].intValue;
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002722 }
2723
Dianne Hackborne6b68032011-10-13 16:26:02 -07002724 // Now deal with preferred configurations.
2725 for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
2726 for (size_t k=0; k<grp->getFiles().size(); k++) {
2727 sp<AaptFile> file = grp->getFiles().valueAt(k);
2728 if (k == 0 && grp->getFiles().size() == 1) {
2729 // If this is the only file left, we need to keep it.
2730 // Otherwise the resource IDs we are using will be inconsistent
2731 // with what we get when not stripping. Sucky, but at least
2732 // for now we can rely on the back-end doing another filtering
2733 // pass to take this out and leave us with this resource name
2734 // containing no entries.
2735 continue;
2736 }
2737 if (file->getPath().getPathExtension() == ".xml") {
2738 // We can't remove .xml files at this point, because when
2739 // we parse them they may add identifier resources, so
2740 // removing them can cause our resource identifiers to
2741 // become inconsistent.
2742 continue;
2743 }
2744 const ResTable_config& config(file->getGroupEntry().toParams());
2745 if (!prefFilter.match(axis, config)) {
2746 // This is a resource we would prefer not to have. Check
2747 // to see if have a similar variation that we would like
2748 // to have and, if so, we can drop it.
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002749
2750 uint32_t bestDensity = config.density;
2751
Dianne Hackborne6b68032011-10-13 16:26:02 -07002752 for (size_t m=0; m<grp->getFiles().size(); m++) {
2753 if (m == k) continue;
2754 sp<AaptFile> mfile = grp->getFiles().valueAt(m);
2755 const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
2756 if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002757 if (axis == AXIS_DENSITY && preferredDensity > 0) {
2758 // See if there is a better density resource
2759 if (mconfig.density < bestDensity &&
2760 mconfig.density > preferredDensity &&
2761 bestDensity > preferredDensity) {
2762 // This density is between our best density and
2763 // the preferred density, therefore it is better.
2764 bestDensity = mconfig.density;
2765 } else if (mconfig.density > bestDensity &&
2766 bestDensity < preferredDensity) {
2767 // This density is better than our best density and
2768 // our best density was smaller than our preferred
2769 // density, so it is better.
2770 bestDensity = mconfig.density;
2771 }
2772 } else if (prefFilter.match(axis, mconfig)) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002773 if (bundle->getVerbose()) {
2774 printf("Pruning unneeded resource: %s\n",
2775 file->getPrintableSource().string());
2776 }
2777 grp->removeFile(k);
2778 k--;
2779 break;
2780 }
2781 }
2782 }
Adam Lesinski9438c2d2013-10-15 17:18:51 -07002783
2784 if (axis == AXIS_DENSITY && preferredDensity > 0 &&
2785 bestDensity != config.density) {
2786 if (bundle->getVerbose()) {
2787 printf("Pruning unneeded resource: %s\n",
2788 file->getPrintableSource().string());
2789 }
2790 grp->removeFile(k);
2791 k--;
2792 }
Dianne Hackborne6b68032011-10-13 16:26:02 -07002793 }
2794 }
2795 }
2796 }
2797 }
2798
2799 return NO_ERROR;
2800}
2801
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002802sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2803{
2804 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2805 if (sym == NULL) {
2806 sym = new AaptSymbols();
2807 mSymbols.add(name, sym);
2808 }
2809 return sym;
2810}
2811
Dianne Hackborn1644c6d72012-02-06 15:33:21 -08002812sp<AaptSymbols> AaptAssets::getJavaSymbolsFor(const String8& name)
2813{
2814 sp<AaptSymbols> sym = mJavaSymbols.valueFor(name);
2815 if (sym == NULL) {
2816 sym = new AaptSymbols();
2817 mJavaSymbols.add(name, sym);
2818 }
2819 return sym;
2820}
2821
2822status_t AaptAssets::applyJavaSymbols()
2823{
2824 size_t N = mJavaSymbols.size();
2825 for (size_t i=0; i<N; i++) {
2826 const String8& name = mJavaSymbols.keyAt(i);
2827 const sp<AaptSymbols>& symbols = mJavaSymbols.valueAt(i);
2828 ssize_t pos = mSymbols.indexOfKey(name);
2829 if (pos < 0) {
2830 SourcePos pos;
2831 pos.error("Java symbol dir %s not defined\n", name.string());
2832 return UNKNOWN_ERROR;
2833 }
2834 //printf("**** applying java symbols in dir %s\n", name.string());
2835 status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
2836 if (err != NO_ERROR) {
2837 return err;
2838 }
2839 }
2840
2841 return NO_ERROR;
2842}
2843
2844bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
2845 //printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
2846 // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
2847 // sym.isJavaSymbol ? 1 : 0);
2848 if (!mHavePrivateSymbols) return true;
2849 if (sym.isPublic) return true;
2850 if (includePrivate && sym.isJavaSymbol) return true;
2851 return false;
2852}
2853
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002854status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2855{
2856 if (!mHaveIncludedAssets) {
2857 // Add in all includes.
2858 const Vector<const char*>& incl = bundle->getPackageIncludes();
2859 const size_t N=incl.size();
2860 for (size_t i=0; i<N; i++) {
2861 if (bundle->getVerbose())
2862 printf("Including resources from package: %s\n", incl[i]);
2863 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2864 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2865 incl[i]);
2866 return UNKNOWN_ERROR;
2867 }
2868 }
2869 mHaveIncludedAssets = true;
2870 }
2871
2872 return NO_ERROR;
2873}
2874
2875status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2876{
2877 const ResTable& res = getIncludedResources();
2878 // XXX dirty!
Narayan Kamath7c4887f2014-01-27 17:32:37 +00002879 return const_cast<ResTable&>(res).add(file->getData(), file->getSize());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002880}
2881
2882const ResTable& AaptAssets::getIncludedResources() const
2883{
2884 return mIncludedAssets.getResources(false);
2885}
2886
Dianne Hackborne6b68032011-10-13 16:26:02 -07002887void AaptAssets::print(const String8& prefix) const
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002888{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002889 String8 innerPrefix(prefix);
2890 innerPrefix.append(" ");
2891 String8 innerInnerPrefix(innerPrefix);
2892 innerInnerPrefix.append(" ");
2893 printf("%sConfigurations:\n", prefix.string());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002894 const size_t N=mGroupEntries.size();
2895 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002896 String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
2897 printf("%s %s\n", prefix.string(),
2898 cname != "" ? cname.string() : "(default)");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002899 }
2900
Dianne Hackborne6b68032011-10-13 16:26:02 -07002901 printf("\n%sFiles:\n", prefix.string());
2902 AaptDir::print(innerPrefix);
2903
2904 printf("\n%sResource Dirs:\n", prefix.string());
2905 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2906 const size_t NR = resdirs.size();
2907 for (size_t i=0; i<NR; i++) {
2908 const sp<AaptDir>& d = resdirs.itemAt(i);
2909 printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
2910 d->print(innerInnerPrefix);
2911 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002912}
2913
Dianne Hackborne6b68032011-10-13 16:26:02 -07002914sp<AaptDir> AaptAssets::resDir(const String8& name) const
Joe Onorato1553c822009-08-30 13:36:22 -07002915{
Dianne Hackborne6b68032011-10-13 16:26:02 -07002916 const Vector<sp<AaptDir> >& resdirs = mResDirs;
2917 const size_t N = resdirs.size();
Joe Onorato1553c822009-08-30 13:36:22 -07002918 for (size_t i=0; i<N; i++) {
Dianne Hackborne6b68032011-10-13 16:26:02 -07002919 const sp<AaptDir>& d = resdirs.itemAt(i);
Joe Onorato1553c822009-08-30 13:36:22 -07002920 if (d->getLeaf() == name) {
2921 return d;
2922 }
2923 }
2924 return NULL;
2925}
2926
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002927bool
2928valid_symbol_name(const String8& symbol)
2929{
2930 static char const * const KEYWORDS[] = {
2931 "abstract", "assert", "boolean", "break",
2932 "byte", "case", "catch", "char", "class", "const", "continue",
2933 "default", "do", "double", "else", "enum", "extends", "final",
2934 "finally", "float", "for", "goto", "if", "implements", "import",
2935 "instanceof", "int", "interface", "long", "native", "new", "package",
2936 "private", "protected", "public", "return", "short", "static",
2937 "strictfp", "super", "switch", "synchronized", "this", "throw",
2938 "throws", "transient", "try", "void", "volatile", "while",
2939 "true", "false", "null",
2940 NULL
2941 };
2942 const char*const* k = KEYWORDS;
2943 const char*const s = symbol.string();
2944 while (*k) {
2945 if (0 == strcmp(s, *k)) {
2946 return false;
2947 }
2948 k++;
2949 }
2950 return true;
2951}