]> git.saurik.com Git - android/aapt.git/blame - AaptAssets.cpp
am b3ab070e: am 3a2d1c23: am ddb8e3b2: am 9bac0a9f: Merge "Fix to compile for windows"
[android/aapt.git] / AaptAssets.cpp
CommitLineData
a534180c
TAOSP
1//
2// Copyright 2006 The Android Open Source Project
3//
4
5#include "AaptAssets.h"
6#include "Main.h"
7
8#include <utils/misc.h>
9#include <utils/SortedVector.h>
10
11#include <ctype.h>
12#include <dirent.h>
13#include <errno.h>
14
15static const char* kDefaultLocale = "default";
16static const char* kWildcardName = "any";
17static const char* kAssetDir = "assets";
18static const char* kResourceDir = "res";
19static const char* kInvalidChars = "/\\:";
20static const size_t kMaxAssetFileName = 100;
21
22static const String8 kResString(kResourceDir);
23
24/*
25 * Names of asset files must meet the following criteria:
26 *
27 * - the filename length must be less than kMaxAssetFileName bytes long
28 * (and can't be empty)
29 * - all characters must be 7-bit printable ASCII
30 * - none of { '/' '\\' ':' }
31 *
32 * Pass in just the filename, not the full path.
33 */
34static bool validateFileName(const char* fileName)
35{
36 const char* cp = fileName;
37 size_t len = 0;
38
39 while (*cp != '\0') {
40 if ((*cp & 0x80) != 0)
41 return false; // reject high ASCII
42 if (*cp < 0x20 || *cp >= 0x7f)
43 return false; // reject control chars and 0x7f
44 if (strchr(kInvalidChars, *cp) != NULL)
45 return false; // reject path sep chars
46 cp++;
47 len++;
48 }
49
50 if (len < 1 || len > kMaxAssetFileName)
51 return false; // reject empty or too long
52
53 return true;
54}
55
56static bool isHidden(const char *root, const char *path)
57{
ce704366 58 const char *ext = NULL;
a534180c
TAOSP
59 const char *type = NULL;
60
61 // Skip all hidden files.
62 if (path[0] == '.') {
63 // Skip ., .. and .svn but don't chatter about it.
64 if (strcmp(path, ".") == 0
65 || strcmp(path, "..") == 0
66 || strcmp(path, ".svn") == 0) {
67 return true;
68 }
69 type = "hidden";
70 } else if (path[0] == '_') {
71 // skip directories starting with _ (don't chatter about it)
72 String8 subdirName(root);
73 subdirName.appendPath(path);
74 if (getFileType(subdirName.string()) == kFileTypeDirectory) {
75 return true;
76 }
77 } else if (strcmp(path, "CVS") == 0) {
78 // Skip CVS but don't chatter about it.
79 return true;
80 } else if (strcasecmp(path, "thumbs.db") == 0
81 || strcasecmp(path, "picasa.ini") == 0) {
82 // Skip suspected image indexes files.
83 type = "index";
84 } else if (path[strlen(path)-1] == '~') {
85 // Skip suspected emacs backup files.
86 type = "backup";
ce704366
R
87 } else if ((ext = strrchr(path, '.')) != NULL && strcmp(ext, ".scc") == 0) {
88 // Skip VisualSourceSafe files and don't chatter about it
89 return true;
a534180c
TAOSP
90 } else {
91 // Let everything else through.
92 return false;
93 }
94
95 /* If we get this far, "type" should be set and the file
96 * should be skipped.
97 */
98 String8 subdirName(root);
99 subdirName.appendPath(path);
100 fprintf(stderr, " (skipping %s %s '%s')\n", type,
101 getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
102 subdirName.string());
103
104 return true;
105}
106
107// =========================================================================
108// =========================================================================
109// =========================================================================
110
111status_t
112AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
113{
114 ResTable_config config;
115
116 // IMSI - MCC
117 if (getMccName(part.string(), &config)) {
118 *axis = AXIS_MCC;
119 *value = config.mcc;
120 return 0;
121 }
122
123 // IMSI - MNC
124 if (getMncName(part.string(), &config)) {
125 *axis = AXIS_MNC;
126 *value = config.mnc;
127 return 0;
128 }
129
130 // locale - language
131 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
132 *axis = AXIS_LANGUAGE;
133 *value = part[1] << 8 | part[0];
134 return 0;
135 }
136
137 // locale - language_REGION
138 if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
139 && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
140 *axis = AXIS_LANGUAGE;
141 *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
142 return 0;
143 }
144
f1f3915b
DH
145 // smallest screen dp width
146 if (getSmallestScreenWidthDpName(part.string(), &config)) {
147 *axis = AXIS_SMALLESTSCREENWIDTHDP;
148 *value = config.smallestScreenWidthDp;
784c6915
DH
149 return 0;
150 }
151
4ee37df2
DH
152 // screen dp width
153 if (getScreenWidthDpName(part.string(), &config)) {
154 *axis = AXIS_SCREENWIDTHDP;
155 *value = config.screenWidthDp;
156 return 0;
157 }
158
159 // screen dp height
160 if (getScreenHeightDpName(part.string(), &config)) {
161 *axis = AXIS_SCREENHEIGHTDP;
162 *value = config.screenHeightDp;
163 return 0;
164 }
165
f1f3915b
DH
166 // screen layout size
167 if (getScreenLayoutSizeName(part.string(), &config)) {
168 *axis = AXIS_SCREENLAYOUTSIZE;
169 *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
170 return 0;
171 }
172
173 // screen layout long
174 if (getScreenLayoutLongName(part.string(), &config)) {
175 *axis = AXIS_SCREENLAYOUTLONG;
176 *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
177 return 0;
178 }
179
a534180c
TAOSP
180 // orientation
181 if (getOrientationName(part.string(), &config)) {
182 *axis = AXIS_ORIENTATION;
183 *value = config.orientation;
184 return 0;
185 }
186
2ca01a37
TH
187 // ui mode type
188 if (getUiModeTypeName(part.string(), &config)) {
189 *axis = AXIS_UIMODETYPE;
190 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
191 return 0;
192 }
193
194 // ui mode night
195 if (getUiModeNightName(part.string(), &config)) {
196 *axis = AXIS_UIMODENIGHT;
197 *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
198 return 0;
199 }
200
a534180c
TAOSP
201 // density
202 if (getDensityName(part.string(), &config)) {
203 *axis = AXIS_DENSITY;
204 *value = config.density;
205 return 0;
206 }
207
208 // touchscreen
209 if (getTouchscreenName(part.string(), &config)) {
210 *axis = AXIS_TOUCHSCREEN;
211 *value = config.touchscreen;
212 return 0;
213 }
214
215 // keyboard hidden
216 if (getKeysHiddenName(part.string(), &config)) {
217 *axis = AXIS_KEYSHIDDEN;
218 *value = config.inputFlags;
219 return 0;
220 }
221
222 // keyboard
223 if (getKeyboardName(part.string(), &config)) {
224 *axis = AXIS_KEYBOARD;
225 *value = config.keyboard;
226 return 0;
227 }
228
0096feb5
DH
229 // navigation hidden
230 if (getNavHiddenName(part.string(), &config)) {
231 *axis = AXIS_NAVHIDDEN;
232 *value = config.inputFlags;
233 return 0;
234 }
235
a534180c
TAOSP
236 // navigation
237 if (getNavigationName(part.string(), &config)) {
238 *axis = AXIS_NAVIGATION;
239 *value = config.navigation;
240 return 0;
241 }
242
243 // screen size
244 if (getScreenSizeName(part.string(), &config)) {
245 *axis = AXIS_SCREENSIZE;
246 *value = config.screenSize;
247 return 0;
248 }
249
250 // version
251 if (getVersionName(part.string(), &config)) {
252 *axis = AXIS_VERSION;
253 *value = config.version;
254 return 0;
255 }
256
257 return 1;
258}
259
260bool
261AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
262{
263 Vector<String8> parts;
264
784c6915 265 String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
0096feb5 266 String8 touch, key, keysHidden, nav, navHidden, size, vers;
f1f3915b 267 String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
a534180c
TAOSP
268
269 const char *p = dir;
270 const char *q;
271 while (NULL != (q = strchr(p, '-'))) {
272 String8 val(p, q-p);
273 val.toLower();
274 parts.add(val);
275 //printf("part: %s\n", parts[parts.size()-1].string());
276 p = q+1;
277 }
278 String8 val(p);
279 val.toLower();
280 parts.add(val);
281 //printf("part: %s\n", parts[parts.size()-1].string());
282
283 const int N = parts.size();
284 int index = 0;
285 String8 part = parts[index];
286
287 // resource type
288 if (!isValidResourceType(part)) {
289 return false;
290 }
291 *resType = part;
292
293 index++;
294 if (index == N) {
295 goto success;
296 }
297 part = parts[index];
298
299 // imsi - mcc
300 if (getMccName(part.string())) {
301 mcc = part;
302
303 index++;
304 if (index == N) {
305 goto success;
306 }
307 part = parts[index];
308 } else {
309 //printf("not mcc: %s\n", part.string());
310 }
311
312 // imsi - mnc
313 if (getMncName(part.string())) {
314 mnc = part;
315
316 index++;
317 if (index == N) {
318 goto success;
319 }
320 part = parts[index];
321 } else {
322 //printf("not mcc: %s\n", part.string());
323 }
324
325 // locale - language
326 if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
327 loc = part;
328
329 index++;
330 if (index == N) {
331 goto success;
332 }
333 part = parts[index];
334 } else {
335 //printf("not language: %s\n", part.string());
336 }
337
338 // locale - region
339 if (loc.length() > 0
340 && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
341 loc += "-";
342 part.toUpper();
343 loc += part.string() + 1;
344
345 index++;
346 if (index == N) {
347 goto success;
348 }
349 part = parts[index];
350 } else {
351 //printf("not region: %s\n", part.string());
352 }
353
f1f3915b
DH
354 if (getSmallestScreenWidthDpName(part.string())) {
355 smallestwidthdp = part;
784c6915
DH
356
357 index++;
358 if (index == N) {
359 goto success;
360 }
361 part = parts[index];
362 } else {
f1f3915b 363 //printf("not smallest screen width dp: %s\n", part.string());
784c6915
DH
364 }
365
f1f3915b
DH
366 if (getScreenWidthDpName(part.string())) {
367 widthdp = part;
784c6915
DH
368
369 index++;
370 if (index == N) {
371 goto success;
372 }
373 part = parts[index];
374 } else {
f1f3915b 375 //printf("not screen width dp: %s\n", part.string());
784c6915
DH
376 }
377
f1f3915b
DH
378 if (getScreenHeightDpName(part.string())) {
379 heightdp = part;
4ee37df2
DH
380
381 index++;
382 if (index == N) {
383 goto success;
384 }
385 part = parts[index];
386 } else {
f1f3915b 387 //printf("not screen height dp: %s\n", part.string());
4ee37df2
DH
388 }
389
f1f3915b
DH
390 if (getScreenLayoutSizeName(part.string())) {
391 layoutsize = part;
4ee37df2
DH
392
393 index++;
394 if (index == N) {
395 goto success;
396 }
397 part = parts[index];
398 } else {
f1f3915b
DH
399 //printf("not screen layout size: %s\n", part.string());
400 }
401
402 if (getScreenLayoutLongName(part.string())) {
403 layoutlong = part;
404
405 index++;
406 if (index == N) {
407 goto success;
408 }
409 part = parts[index];
410 } else {
411 //printf("not screen layout long: %s\n", part.string());
4ee37df2
DH
412 }
413
a534180c
TAOSP
414 // orientation
415 if (getOrientationName(part.string())) {
416 orient = part;
417
418 index++;
419 if (index == N) {
420 goto success;
421 }
422 part = parts[index];
423 } else {
424 //printf("not orientation: %s\n", part.string());
425 }
426
2ca01a37
TH
427 // ui mode type
428 if (getUiModeTypeName(part.string())) {
429 uiModeType = part;
430
431 index++;
432 if (index == N) {
433 goto success;
434 }
435 part = parts[index];
436 } else {
437 //printf("not ui mode type: %s\n", part.string());
438 }
439
440 // ui mode night
441 if (getUiModeNightName(part.string())) {
442 uiModeNight = part;
443
444 index++;
445 if (index == N) {
446 goto success;
447 }
448 part = parts[index];
449 } else {
450 //printf("not ui mode night: %s\n", part.string());
451 }
452
a534180c
TAOSP
453 // density
454 if (getDensityName(part.string())) {
455 den = part;
456
457 index++;
458 if (index == N) {
459 goto success;
460 }
461 part = parts[index];
462 } else {
463 //printf("not density: %s\n", part.string());
464 }
465
466 // touchscreen
467 if (getTouchscreenName(part.string())) {
468 touch = part;
469
470 index++;
471 if (index == N) {
472 goto success;
473 }
474 part = parts[index];
475 } else {
476 //printf("not touchscreen: %s\n", part.string());
477 }
478
479 // keyboard hidden
480 if (getKeysHiddenName(part.string())) {
481 keysHidden = part;
482
483 index++;
484 if (index == N) {
485 goto success;
486 }
487 part = parts[index];
488 } else {
489 //printf("not keysHidden: %s\n", part.string());
490 }
491
492 // keyboard
493 if (getKeyboardName(part.string())) {
494 key = part;
495
496 index++;
497 if (index == N) {
498 goto success;
499 }
500 part = parts[index];
501 } else {
502 //printf("not keyboard: %s\n", part.string());
503 }
504
0096feb5
DH
505 // navigation hidden
506 if (getNavHiddenName(part.string())) {
507 navHidden = part;
508
509 index++;
510 if (index == N) {
511 goto success;
512 }
513 part = parts[index];
514 } else {
515 //printf("not navHidden: %s\n", part.string());
516 }
517
a534180c
TAOSP
518 if (getNavigationName(part.string())) {
519 nav = part;
520
521 index++;
522 if (index == N) {
523 goto success;
524 }
525 part = parts[index];
526 } else {
527 //printf("not navigation: %s\n", part.string());
528 }
529
530 if (getScreenSizeName(part.string())) {
531 size = part;
532
533 index++;
534 if (index == N) {
535 goto success;
536 }
537 part = parts[index];
538 } else {
539 //printf("not screen size: %s\n", part.string());
540 }
541
542 if (getVersionName(part.string())) {
543 vers = part;
544
545 index++;
546 if (index == N) {
547 goto success;
548 }
549 part = parts[index];
550 } else {
551 //printf("not version: %s\n", part.string());
552 }
553
554 // if there are extra parts, it doesn't match
555 return false;
556
557success:
558 this->mcc = mcc;
559 this->mnc = mnc;
560 this->locale = loc;
784c6915
DH
561 this->screenLayoutSize = layoutsize;
562 this->screenLayoutLong = layoutlong;
f1f3915b 563 this->smallestScreenWidthDp = smallestwidthdp;
4ee37df2
DH
564 this->screenWidthDp = widthdp;
565 this->screenHeightDp = heightdp;
a534180c 566 this->orientation = orient;
2ca01a37
TH
567 this->uiModeType = uiModeType;
568 this->uiModeNight = uiModeNight;
a534180c
TAOSP
569 this->density = den;
570 this->touchscreen = touch;
571 this->keysHidden = keysHidden;
572 this->keyboard = key;
0096feb5 573 this->navHidden = navHidden;
a534180c
TAOSP
574 this->navigation = nav;
575 this->screenSize = size;
576 this->version = vers;
577
578 // what is this anyway?
579 this->vendor = "";
580
581 return true;
582}
583
584String8
585AaptGroupEntry::toString() const
586{
587 String8 s = this->mcc;
588 s += ",";
589 s += this->mnc;
590 s += ",";
591 s += this->locale;
592 s += ",";
f1f3915b 593 s += smallestScreenWidthDp;
784c6915 594 s += ",";
4ee37df2
DH
595 s += screenWidthDp;
596 s += ",";
597 s += screenHeightDp;
598 s += ",";
f1f3915b
DH
599 s += screenLayoutSize;
600 s += ",";
601 s += screenLayoutLong;
602 s += ",";
a534180c
TAOSP
603 s += this->orientation;
604 s += ",";
2ca01a37
TH
605 s += uiModeType;
606 s += ",";
607 s += uiModeNight;
608 s += ",";
a534180c
TAOSP
609 s += density;
610 s += ",";
611 s += touchscreen;
612 s += ",";
613 s += keysHidden;
614 s += ",";
615 s += keyboard;
616 s += ",";
0096feb5
DH
617 s += navHidden;
618 s += ",";
a534180c
TAOSP
619 s += navigation;
620 s += ",";
621 s += screenSize;
622 s += ",";
623 s += version;
624 return s;
625}
626
627String8
628AaptGroupEntry::toDirName(const String8& resType) const
629{
630 String8 s = resType;
631 if (this->mcc != "") {
632 s += "-";
633 s += mcc;
634 }
635 if (this->mnc != "") {
636 s += "-";
637 s += mnc;
638 }
639 if (this->locale != "") {
640 s += "-";
641 s += locale;
642 }
f1f3915b 643 if (this->smallestScreenWidthDp != "") {
784c6915 644 s += "-";
f1f3915b 645 s += smallestScreenWidthDp;
784c6915 646 }
4ee37df2
DH
647 if (this->screenWidthDp != "") {
648 s += "-";
649 s += screenWidthDp;
650 }
651 if (this->screenHeightDp != "") {
652 s += "-";
653 s += screenHeightDp;
654 }
f1f3915b
DH
655 if (this->screenLayoutSize != "") {
656 s += "-";
657 s += screenLayoutSize;
658 }
659 if (this->screenLayoutLong != "") {
660 s += "-";
661 s += screenLayoutLong;
662 }
a534180c
TAOSP
663 if (this->orientation != "") {
664 s += "-";
665 s += orientation;
666 }
2ca01a37
TH
667 if (this->uiModeType != "") {
668 s += "-";
669 s += uiModeType;
670 }
671 if (this->uiModeNight != "") {
672 s += "-";
673 s += uiModeNight;
674 }
a534180c
TAOSP
675 if (this->density != "") {
676 s += "-";
677 s += density;
678 }
679 if (this->touchscreen != "") {
680 s += "-";
681 s += touchscreen;
682 }
683 if (this->keysHidden != "") {
684 s += "-";
685 s += keysHidden;
686 }
687 if (this->keyboard != "") {
688 s += "-";
689 s += keyboard;
690 }
0096feb5
DH
691 if (this->navHidden != "") {
692 s += "-";
693 s += navHidden;
694 }
a534180c
TAOSP
695 if (this->navigation != "") {
696 s += "-";
697 s += navigation;
698 }
699 if (this->screenSize != "") {
700 s += "-";
701 s += screenSize;
702 }
703 if (this->version != "") {
704 s += "-";
705 s += version;
706 }
707
708 return s;
709}
710
711bool AaptGroupEntry::getMccName(const char* name,
712 ResTable_config* out)
713{
714 if (strcmp(name, kWildcardName) == 0) {
715 if (out) out->mcc = 0;
716 return true;
717 }
718 const char* c = name;
719 if (tolower(*c) != 'm') return false;
720 c++;
721 if (tolower(*c) != 'c') return false;
722 c++;
723 if (tolower(*c) != 'c') return false;
724 c++;
725
726 const char* val = c;
727
728 while (*c >= '0' && *c <= '9') {
729 c++;
730 }
731 if (*c != 0) return false;
732 if (c-val != 3) return false;
733
734 int d = atoi(val);
735 if (d != 0) {
736 if (out) out->mcc = d;
737 return true;
738 }
739
740 return false;
741}
742
743bool AaptGroupEntry::getMncName(const char* name,
744 ResTable_config* out)
745{
746 if (strcmp(name, kWildcardName) == 0) {
747 if (out) out->mcc = 0;
748 return true;
749 }
750 const char* c = name;
751 if (tolower(*c) != 'm') return false;
752 c++;
753 if (tolower(*c) != 'n') return false;
754 c++;
755 if (tolower(*c) != 'c') return false;
756 c++;
757
758 const char* val = c;
759
760 while (*c >= '0' && *c <= '9') {
761 c++;
762 }
763 if (*c != 0) return false;
764 if (c-val == 0 || c-val > 3) return false;
765
a4424abe
JR
766 if (out) {
767 out->mnc = atoi(val);
a534180c
TAOSP
768 }
769
a4424abe 770 return true;
a534180c
TAOSP
771}
772
773/*
774 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
775 * "default")?
776 *
777 * TODO: Should insist that the first two letters are lower case, and the
778 * second two are upper.
779 */
780bool AaptGroupEntry::getLocaleName(const char* fileName,
781 ResTable_config* out)
782{
783 if (strcmp(fileName, kWildcardName) == 0
784 || strcmp(fileName, kDefaultLocale) == 0) {
785 if (out) {
786 out->language[0] = 0;
787 out->language[1] = 0;
788 out->country[0] = 0;
789 out->country[1] = 0;
790 }
791 return true;
792 }
793
794 if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
795 if (out) {
796 out->language[0] = fileName[0];
797 out->language[1] = fileName[1];
798 out->country[0] = 0;
799 out->country[1] = 0;
800 }
801 return true;
802 }
803
804 if (strlen(fileName) == 5 &&
805 isalpha(fileName[0]) &&
806 isalpha(fileName[1]) &&
807 fileName[2] == '-' &&
808 isalpha(fileName[3]) &&
809 isalpha(fileName[4])) {
810 if (out) {
811 out->language[0] = fileName[0];
812 out->language[1] = fileName[1];
813 out->country[0] = fileName[3];
814 out->country[1] = fileName[4];
815 }
816 return true;
817 }
818
819 return false;
820}
821
784c6915
DH
822bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
823 ResTable_config* out)
824{
825 if (strcmp(name, kWildcardName) == 0) {
826 if (out) out->screenLayout =
827 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
828 | ResTable_config::SCREENSIZE_ANY;
829 return true;
830 } else if (strcmp(name, "small") == 0) {
831 if (out) out->screenLayout =
832 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
833 | ResTable_config::SCREENSIZE_SMALL;
834 return true;
835 } else if (strcmp(name, "normal") == 0) {
836 if (out) out->screenLayout =
837 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
838 | ResTable_config::SCREENSIZE_NORMAL;
839 return true;
840 } else if (strcmp(name, "large") == 0) {
841 if (out) out->screenLayout =
842 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
843 | ResTable_config::SCREENSIZE_LARGE;
844 return true;
69e2fb69
DH
845 } else if (strcmp(name, "xlarge") == 0) {
846 if (out) out->screenLayout =
847 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
848 | ResTable_config::SCREENSIZE_XLARGE;
849 return true;
784c6915
DH
850 }
851
852 return false;
853}
854
855bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
856 ResTable_config* out)
857{
858 if (strcmp(name, kWildcardName) == 0) {
859 if (out) out->screenLayout =
860 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
861 | ResTable_config::SCREENLONG_ANY;
862 return true;
863 } else if (strcmp(name, "long") == 0) {
864 if (out) out->screenLayout =
865 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
866 | ResTable_config::SCREENLONG_YES;
867 return true;
868 } else if (strcmp(name, "notlong") == 0) {
869 if (out) out->screenLayout =
870 (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
871 | ResTable_config::SCREENLONG_NO;
872 return true;
873 }
874
875 return false;
876}
877
a534180c
TAOSP
878bool AaptGroupEntry::getOrientationName(const char* name,
879 ResTable_config* out)
880{
881 if (strcmp(name, kWildcardName) == 0) {
882 if (out) out->orientation = out->ORIENTATION_ANY;
883 return true;
884 } else if (strcmp(name, "port") == 0) {
885 if (out) out->orientation = out->ORIENTATION_PORT;
886 return true;
887 } else if (strcmp(name, "land") == 0) {
888 if (out) out->orientation = out->ORIENTATION_LAND;
889 return true;
890 } else if (strcmp(name, "square") == 0) {
891 if (out) out->orientation = out->ORIENTATION_SQUARE;
892 return true;
893 }
894
895 return false;
896}
897
2ca01a37
TH
898bool AaptGroupEntry::getUiModeTypeName(const char* name,
899 ResTable_config* out)
900{
901 if (strcmp(name, kWildcardName) == 0) {
902 if (out) out->uiMode =
903 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
e3c6837d
DH
904 | ResTable_config::UI_MODE_TYPE_ANY;
905 return true;
906 } else if (strcmp(name, "desk") == 0) {
907 if (out) out->uiMode =
908 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
909 | ResTable_config::UI_MODE_TYPE_DESK;
2ca01a37
TH
910 return true;
911 } else if (strcmp(name, "car") == 0) {
912 if (out) out->uiMode =
913 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
914 | ResTable_config::UI_MODE_TYPE_CAR;
915 return true;
718f0a85
DH
916 } else if (strcmp(name, "television") == 0) {
917 if (out) out->uiMode =
918 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
919 | ResTable_config::UI_MODE_TYPE_TELEVISION;
920 return true;
2ca01a37
TH
921 }
922
923 return false;
924}
925
926bool AaptGroupEntry::getUiModeNightName(const char* name,
927 ResTable_config* out)
928{
929 if (strcmp(name, kWildcardName) == 0) {
930 if (out) out->uiMode =
931 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
932 | ResTable_config::UI_MODE_NIGHT_ANY;
933 return true;
934 } else if (strcmp(name, "night") == 0) {
935 if (out) out->uiMode =
936 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
937 | ResTable_config::UI_MODE_NIGHT_YES;
938 return true;
939 } else if (strcmp(name, "notnight") == 0) {
940 if (out) out->uiMode =
941 (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
942 | ResTable_config::UI_MODE_NIGHT_NO;
943 return true;
944 }
945
946 return false;
947}
948
a534180c
TAOSP
949bool AaptGroupEntry::getDensityName(const char* name,
950 ResTable_config* out)
951{
952 if (strcmp(name, kWildcardName) == 0) {
41ff305d 953 if (out) out->density = ResTable_config::DENSITY_DEFAULT;
a534180c
TAOSP
954 return true;
955 }
41ff305d
DH
956
957 if (strcmp(name, "nodpi") == 0) {
958 if (out) out->density = ResTable_config::DENSITY_NONE;
959 return true;
960 }
961
784c6915
DH
962 if (strcmp(name, "ldpi") == 0) {
963 if (out) out->density = ResTable_config::DENSITY_LOW;
964 return true;
965 }
966
967 if (strcmp(name, "mdpi") == 0) {
968 if (out) out->density = ResTable_config::DENSITY_MEDIUM;
969 return true;
970 }
971
465c2ef5
DH
972 if (strcmp(name, "tvdpi") == 0) {
973 if (out) out->density = ResTable_config::DENSITY_TV;
974 return true;
975 }
976
784c6915
DH
977 if (strcmp(name, "hdpi") == 0) {
978 if (out) out->density = ResTable_config::DENSITY_HIGH;
979 return true;
980 }
981
bd8c05dd
DH
982 if (strcmp(name, "xhdpi") == 0) {
983 if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
984 return true;
985 }
986
a534180c
TAOSP
987 char* c = (char*)name;
988 while (*c >= '0' && *c <= '9') {
989 c++;
990 }
991
992 // check that we have 'dpi' after the last digit.
993 if (toupper(c[0]) != 'D' ||
994 toupper(c[1]) != 'P' ||
995 toupper(c[2]) != 'I' ||
996 c[3] != 0) {
997 return false;
998 }
999
1000 // temporarily replace the first letter with \0 to
1001 // use atoi.
1002 char tmp = c[0];
1003 c[0] = '\0';
1004
1005 int d = atoi(name);
1006 c[0] = tmp;
1007
1008 if (d != 0) {
1009 if (out) out->density = d;
1010 return true;
1011 }
1012
1013 return false;
1014}
1015
1016bool AaptGroupEntry::getTouchscreenName(const char* name,
1017 ResTable_config* out)
1018{
1019 if (strcmp(name, kWildcardName) == 0) {
1020 if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
1021 return true;
1022 } else if (strcmp(name, "notouch") == 0) {
1023 if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
1024 return true;
1025 } else if (strcmp(name, "stylus") == 0) {
1026 if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
1027 return true;
1028 } else if (strcmp(name, "finger") == 0) {
1029 if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
1030 return true;
1031 }
1032
1033 return false;
1034}
1035
1036bool AaptGroupEntry::getKeysHiddenName(const char* name,
1037 ResTable_config* out)
1038{
1039 uint8_t mask = 0;
1040 uint8_t value = 0;
1041 if (strcmp(name, kWildcardName) == 0) {
18a923f6
KR
1042 mask = ResTable_config::MASK_KEYSHIDDEN;
1043 value = ResTable_config::KEYSHIDDEN_ANY;
a534180c 1044 } else if (strcmp(name, "keysexposed") == 0) {
18a923f6
KR
1045 mask = ResTable_config::MASK_KEYSHIDDEN;
1046 value = ResTable_config::KEYSHIDDEN_NO;
a534180c 1047 } else if (strcmp(name, "keyshidden") == 0) {
18a923f6
KR
1048 mask = ResTable_config::MASK_KEYSHIDDEN;
1049 value = ResTable_config::KEYSHIDDEN_YES;
a534180c 1050 } else if (strcmp(name, "keyssoft") == 0) {
18a923f6
KR
1051 mask = ResTable_config::MASK_KEYSHIDDEN;
1052 value = ResTable_config::KEYSHIDDEN_SOFT;
a534180c
TAOSP
1053 }
1054
1055 if (mask != 0) {
1056 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1057 return true;
1058 }
1059
1060 return false;
1061}
1062
1063bool AaptGroupEntry::getKeyboardName(const char* name,
1064 ResTable_config* out)
1065{
1066 if (strcmp(name, kWildcardName) == 0) {
1067 if (out) out->keyboard = out->KEYBOARD_ANY;
1068 return true;
1069 } else if (strcmp(name, "nokeys") == 0) {
1070 if (out) out->keyboard = out->KEYBOARD_NOKEYS;
1071 return true;
1072 } else if (strcmp(name, "qwerty") == 0) {
1073 if (out) out->keyboard = out->KEYBOARD_QWERTY;
1074 return true;
1075 } else if (strcmp(name, "12key") == 0) {
1076 if (out) out->keyboard = out->KEYBOARD_12KEY;
1077 return true;
1078 }
1079
1080 return false;
1081}
1082
0096feb5
DH
1083bool AaptGroupEntry::getNavHiddenName(const char* name,
1084 ResTable_config* out)
1085{
1086 uint8_t mask = 0;
1087 uint8_t value = 0;
1088 if (strcmp(name, kWildcardName) == 0) {
491d9ef0
KR
1089 mask = ResTable_config::MASK_NAVHIDDEN;
1090 value = ResTable_config::NAVHIDDEN_ANY;
0096feb5 1091 } else if (strcmp(name, "navexposed") == 0) {
491d9ef0
KR
1092 mask = ResTable_config::MASK_NAVHIDDEN;
1093 value = ResTable_config::NAVHIDDEN_NO;
0096feb5 1094 } else if (strcmp(name, "navhidden") == 0) {
491d9ef0
KR
1095 mask = ResTable_config::MASK_NAVHIDDEN;
1096 value = ResTable_config::NAVHIDDEN_YES;
0096feb5
DH
1097 }
1098
1099 if (mask != 0) {
1100 if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1101 return true;
1102 }
1103
1104 return false;
1105}
1106
a534180c
TAOSP
1107bool AaptGroupEntry::getNavigationName(const char* name,
1108 ResTable_config* out)
1109{
1110 if (strcmp(name, kWildcardName) == 0) {
1111 if (out) out->navigation = out->NAVIGATION_ANY;
1112 return true;
1113 } else if (strcmp(name, "nonav") == 0) {
1114 if (out) out->navigation = out->NAVIGATION_NONAV;
1115 return true;
1116 } else if (strcmp(name, "dpad") == 0) {
1117 if (out) out->navigation = out->NAVIGATION_DPAD;
1118 return true;
1119 } else if (strcmp(name, "trackball") == 0) {
1120 if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1121 return true;
1122 } else if (strcmp(name, "wheel") == 0) {
1123 if (out) out->navigation = out->NAVIGATION_WHEEL;
1124 return true;
1125 }
1126
1127 return false;
1128}
1129
4ee37df2 1130bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
a534180c
TAOSP
1131{
1132 if (strcmp(name, kWildcardName) == 0) {
1133 if (out) {
1134 out->screenWidth = out->SCREENWIDTH_ANY;
1135 out->screenHeight = out->SCREENHEIGHT_ANY;
1136 }
1137 return true;
1138 }
1139
1140 const char* x = name;
1141 while (*x >= '0' && *x <= '9') x++;
1142 if (x == name || *x != 'x') return false;
1143 String8 xName(name, x-name);
1144 x++;
1145
1146 const char* y = x;
1147 while (*y >= '0' && *y <= '9') y++;
1148 if (y == name || *y != 0) return false;
1149 String8 yName(x, y-x);
1150
1151 uint16_t w = (uint16_t)atoi(xName.string());
1152 uint16_t h = (uint16_t)atoi(yName.string());
1153 if (w < h) {
1154 return false;
1155 }
1156
1157 if (out) {
1158 out->screenWidth = w;
1159 out->screenHeight = h;
1160 }
1161
1162 return true;
1163}
1164
f1f3915b
DH
1165bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
1166{
1167 if (strcmp(name, kWildcardName) == 0) {
1168 if (out) {
1169 out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
1170 }
1171 return true;
1172 }
1173
1174 if (*name != 's') return false;
1175 name++;
1176 if (*name != 'w') return false;
1177 name++;
1178 const char* x = name;
1179 while (*x >= '0' && *x <= '9') x++;
1180 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1181 String8 xName(name, x-name);
1182
1183 if (out) {
1184 out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
1185 }
1186
1187 return true;
1188}
1189
4ee37df2
DH
1190bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
1191{
1192 if (strcmp(name, kWildcardName) == 0) {
1193 if (out) {
1194 out->screenWidthDp = out->SCREENWIDTH_ANY;
1195 }
1196 return true;
1197 }
1198
1199 if (*name != 'w') return false;
1200 name++;
1201 const char* x = name;
1202 while (*x >= '0' && *x <= '9') x++;
1203 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1204 String8 xName(name, x-name);
1205
1206 if (out) {
1207 out->screenWidthDp = (uint16_t)atoi(xName.string());
1208 }
1209
1210 return true;
1211}
1212
1213bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
1214{
1215 if (strcmp(name, kWildcardName) == 0) {
1216 if (out) {
1217 out->screenHeightDp = out->SCREENWIDTH_ANY;
1218 }
1219 return true;
1220 }
1221
1222 if (*name != 'h') return false;
1223 name++;
1224 const char* x = name;
1225 while (*x >= '0' && *x <= '9') x++;
1226 if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
1227 String8 xName(name, x-name);
1228
1229 if (out) {
1230 out->screenHeightDp = (uint16_t)atoi(xName.string());
1231 }
1232
1233 return true;
1234}
1235
1236bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
a534180c
TAOSP
1237{
1238 if (strcmp(name, kWildcardName) == 0) {
1239 if (out) {
1240 out->sdkVersion = out->SDKVERSION_ANY;
1241 out->minorVersion = out->MINORVERSION_ANY;
1242 }
1243 return true;
1244 }
1245
1246 if (*name != 'v') {
1247 return false;
1248 }
1249
1250 name++;
1251 const char* s = name;
1252 while (*s >= '0' && *s <= '9') s++;
1253 if (s == name || *s != 0) return false;
1254 String8 sdkName(name, s-name);
1255
1256 if (out) {
1257 out->sdkVersion = (uint16_t)atoi(sdkName.string());
1258 out->minorVersion = 0;
1259 }
1260
1261 return true;
1262}
1263
1264int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1265{
1266 int v = mcc.compare(o.mcc);
1267 if (v == 0) v = mnc.compare(o.mnc);
1268 if (v == 0) v = locale.compare(o.locale);
1269 if (v == 0) v = vendor.compare(o.vendor);
f1f3915b 1270 if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
4ee37df2
DH
1271 if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
1272 if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
f1f3915b
DH
1273 if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1274 if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
a534180c 1275 if (v == 0) v = orientation.compare(o.orientation);
2ca01a37
TH
1276 if (v == 0) v = uiModeType.compare(o.uiModeType);
1277 if (v == 0) v = uiModeNight.compare(o.uiModeNight);
a534180c
TAOSP
1278 if (v == 0) v = density.compare(o.density);
1279 if (v == 0) v = touchscreen.compare(o.touchscreen);
1280 if (v == 0) v = keysHidden.compare(o.keysHidden);
1281 if (v == 0) v = keyboard.compare(o.keyboard);
0096feb5 1282 if (v == 0) v = navHidden.compare(o.navHidden);
a534180c
TAOSP
1283 if (v == 0) v = navigation.compare(o.navigation);
1284 if (v == 0) v = screenSize.compare(o.screenSize);
1285 if (v == 0) v = version.compare(o.version);
1286 return v;
1287}
1288
1289ResTable_config AaptGroupEntry::toParams() const
1290{
1291 ResTable_config params;
1292 memset(&params, 0, sizeof(params));
1293 getMccName(mcc.string(), &params);
1294 getMncName(mnc.string(), &params);
1295 getLocaleName(locale.string(), &params);
f1f3915b 1296 getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
4ee37df2
DH
1297 getScreenWidthDpName(screenWidthDp.string(), &params);
1298 getScreenHeightDpName(screenHeightDp.string(), &params);
f1f3915b
DH
1299 getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1300 getScreenLayoutLongName(screenLayoutLong.string(), &params);
a534180c 1301 getOrientationName(orientation.string(), &params);
2ca01a37
TH
1302 getUiModeTypeName(uiModeType.string(), &params);
1303 getUiModeNightName(uiModeNight.string(), &params);
a534180c
TAOSP
1304 getDensityName(density.string(), &params);
1305 getTouchscreenName(touchscreen.string(), &params);
1306 getKeysHiddenName(keysHidden.string(), &params);
1307 getKeyboardName(keyboard.string(), &params);
0096feb5 1308 getNavHiddenName(navHidden.string(), &params);
a534180c
TAOSP
1309 getNavigationName(navigation.string(), &params);
1310 getScreenSizeName(screenSize.string(), &params);
1311 getVersionName(version.string(), &params);
af945cf3
DH
1312
1313 // Fix up version number based on specified parameters.
1314 int minSdk = 0;
f1f3915b
DH
1315 if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
1316 || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
4ee37df2 1317 || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
f1f3915b 1318 minSdk = SDK_HONEYCOMB_MR2;
4ee37df2 1319 } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
af945cf3
DH
1320 != ResTable_config::UI_MODE_TYPE_ANY
1321 || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1322 != ResTable_config::UI_MODE_NIGHT_ANY) {
1323 minSdk = SDK_FROYO;
1324 } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1325 != ResTable_config::SCREENSIZE_ANY
1326 || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1327 != ResTable_config::SCREENLONG_ANY
1328 || params.density != ResTable_config::DENSITY_DEFAULT) {
1329 minSdk = SDK_DONUT;
1330 }
1331
1332 if (minSdk > params.sdkVersion) {
1333 params.sdkVersion = minSdk;
1334 }
1335
a534180c
TAOSP
1336 return params;
1337}
1338
1339// =========================================================================
1340// =========================================================================
1341// =========================================================================
1342
1343void* AaptFile::editData(size_t size)
1344{
1345 if (size <= mBufferSize) {
1346 mDataSize = size;
1347 return mData;
1348 }
1349 size_t allocSize = (size*3)/2;
1350 void* buf = realloc(mData, allocSize);
1351 if (buf == NULL) {
1352 return NULL;
1353 }
1354 mData = buf;
1355 mDataSize = size;
1356 mBufferSize = allocSize;
1357 return buf;
1358}
1359
1360void* AaptFile::editData(size_t* outSize)
1361{
1362 if (outSize) {
1363 *outSize = mDataSize;
1364 }
1365 return mData;
1366}
1367
1368void* AaptFile::padData(size_t wordSize)
1369{
1370 const size_t extra = mDataSize%wordSize;
1371 if (extra == 0) {
1372 return mData;
1373 }
1374
1375 size_t initial = mDataSize;
1376 void* data = editData(initial+(wordSize-extra));
1377 if (data != NULL) {
1378 memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1379 }
1380 return data;
1381}
1382
1383status_t AaptFile::writeData(const void* data, size_t size)
1384{
1385 size_t end = mDataSize;
1386 size_t total = size + end;
1387 void* buf = editData(total);
1388 if (buf == NULL) {
1389 return UNKNOWN_ERROR;
1390 }
1391 memcpy(((char*)buf)+end, data, size);
1392 return NO_ERROR;
1393}
1394
1395void AaptFile::clearData()
1396{
1397 if (mData != NULL) free(mData);
1398 mData = NULL;
1399 mDataSize = 0;
1400 mBufferSize = 0;
1401}
1402
1403String8 AaptFile::getPrintableSource() const
1404{
1405 if (hasData()) {
1406 String8 name(mGroupEntry.locale.string());
1407 name.appendPath(mGroupEntry.vendor.string());
1408 name.appendPath(mPath);
1409 name.append(" #generated");
1410 return name;
1411 }
1412 return mSourceFile;
1413}
1414
1415// =========================================================================
1416// =========================================================================
1417// =========================================================================
1418
1419status_t AaptGroup::addFile(const sp<AaptFile>& file)
1420{
1421 if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1422 file->mPath = mPath;
1423 mFiles.add(file->getGroupEntry(), file);
1424 return NO_ERROR;
1425 }
1426
1427 SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1428 getPrintableSource().string());
1429 return UNKNOWN_ERROR;
1430}
1431
1432void AaptGroup::removeFile(size_t index)
1433{
1434 mFiles.removeItemsAt(index);
1435}
1436
1437void AaptGroup::print() const
1438{
1439 printf(" %s\n", getPath().string());
1440 const size_t N=mFiles.size();
1441 size_t i;
1442 for (i=0; i<N; i++) {
1443 sp<AaptFile> file = mFiles.valueAt(i);
1444 const AaptGroupEntry& e = file->getGroupEntry();
1445 if (file->hasData()) {
1446 printf(" Gen: (%s) %d bytes\n", e.toString().string(),
1447 (int)file->getSize());
1448 } else {
1449 printf(" Src: %s\n", file->getPrintableSource().string());
1450 }
1451 }
1452}
1453
1454String8 AaptGroup::getPrintableSource() const
1455{
1456 if (mFiles.size() > 0) {
1457 // Arbitrarily pull the first source file out of the list.
1458 return mFiles.valueAt(0)->getPrintableSource();
1459 }
1460
1461 // Should never hit this case, but to be safe...
1462 return getPath();
1463
1464}
1465
1466// =========================================================================
1467// =========================================================================
1468// =========================================================================
1469
1470status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1471{
1472 if (mFiles.indexOfKey(name) >= 0) {
1473 return ALREADY_EXISTS;
1474 }
1475 mFiles.add(name, file);
1476 return NO_ERROR;
1477}
1478
1479status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1480{
1481 if (mDirs.indexOfKey(name) >= 0) {
1482 return ALREADY_EXISTS;
1483 }
1484 mDirs.add(name, dir);
1485 return NO_ERROR;
1486}
1487
1488sp<AaptDir> AaptDir::makeDir(const String8& path)
1489{
1490 String8 name;
1491 String8 remain = path;
1492
1493 sp<AaptDir> subdir = this;
1494 while (name = remain.walkPath(&remain), remain != "") {
1495 subdir = subdir->makeDir(name);
1496 }
1497
1498 ssize_t i = subdir->mDirs.indexOfKey(name);
1499 if (i >= 0) {
1500 return subdir->mDirs.valueAt(i);
1501 }
1502 sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1503 subdir->mDirs.add(name, dir);
1504 return dir;
1505}
1506
1507void AaptDir::removeFile(const String8& name)
1508{
1509 mFiles.removeItem(name);
1510}
1511
1512void AaptDir::removeDir(const String8& name)
1513{
1514 mDirs.removeItem(name);
1515}
1516
1517status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1518{
1519 sp<AaptGroup> origGroup;
1520
1521 // Find and remove the given file with shear, brute force!
1522 const size_t NG = mFiles.size();
1523 size_t i;
1524 for (i=0; origGroup == NULL && i<NG; i++) {
1525 sp<AaptGroup> g = mFiles.valueAt(i);
1526 const size_t NF = g->getFiles().size();
1527 for (size_t j=0; j<NF; j++) {
1528 if (g->getFiles().valueAt(j) == file) {
1529 origGroup = g;
1530 g->removeFile(j);
1531 if (NF == 1) {
1532 mFiles.removeItemsAt(i);
1533 }
1534 break;
1535 }
1536 }
1537 }
1538
1539 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1540
1541 // Place the file under its new name.
1542 if (origGroup != NULL) {
1543 return addLeafFile(newName, file);
1544 }
1545
1546 return NO_ERROR;
1547}
1548
1549status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1550{
1551 sp<AaptGroup> group;
1552 if (mFiles.indexOfKey(leafName) >= 0) {
1553 group = mFiles.valueFor(leafName);
1554 } else {
1555 group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1556 mFiles.add(leafName, group);
1557 }
1558
1559 return group->addFile(file);
1560}
1561
1562ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
52ffc169
JG
1563 const AaptGroupEntry& kind, const String8& resType,
1564 sp<FilePathStore>& fullResPaths)
a534180c
TAOSP
1565{
1566 Vector<String8> fileNames;
a534180c
TAOSP
1567 {
1568 DIR* dir = NULL;
1569
1570 dir = opendir(srcDir.string());
1571 if (dir == NULL) {
1572 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1573 return UNKNOWN_ERROR;
1574 }
1575
1576 /*
1577 * Slurp the filenames out of the directory.
1578 */
1579 while (1) {
1580 struct dirent* entry;
1581
1582 entry = readdir(dir);
1583 if (entry == NULL)
1584 break;
1585
1586 if (isHidden(srcDir.string(), entry->d_name))
1587 continue;
1588
52ffc169
JG
1589 String8 name(entry->d_name);
1590 fileNames.add(name);
1591 // Add fully qualified path for dependency purposes
1592 // if we're collecting them
1593 if (fullResPaths != NULL) {
1594 fullResPaths->add(srcDir.appendPathCopy(name));
1595 }
a534180c 1596 }
a534180c
TAOSP
1597 closedir(dir);
1598 }
1599
1600 ssize_t count = 0;
1601
1602 /*
1603 * Stash away the files and recursively descend into subdirectories.
1604 */
1605 const size_t N = fileNames.size();
1606 size_t i;
1607 for (i = 0; i < N; i++) {
1608 String8 pathName(srcDir);
1609 FileType type;
1610
1611 pathName.appendPath(fileNames[i].string());
1612 type = getFileType(pathName.string());
1613 if (type == kFileTypeDirectory) {
1614 sp<AaptDir> subdir;
1615 bool notAdded = false;
1616 if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1617 subdir = mDirs.valueFor(fileNames[i]);
1618 } else {
1619 subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1620 notAdded = true;
1621 }
1622 ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
52ffc169 1623 resType, fullResPaths);
a534180c
TAOSP
1624 if (res < NO_ERROR) {
1625 return res;
1626 }
1627 if (res > 0 && notAdded) {
1628 mDirs.add(fileNames[i], subdir);
1629 }
1630 count += res;
1631 } else if (type == kFileTypeRegular) {
1632 sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1633 status_t err = addLeafFile(fileNames[i], file);
1634 if (err != NO_ERROR) {
1635 return err;
1636 }
1637
1638 count++;
1639
1640 } else {
1641 if (bundle->getVerbose())
1642 printf(" (ignoring non-file/dir '%s')\n", pathName.string());
1643 }
1644 }
1645
1646 return count;
1647}
1648
1649status_t AaptDir::validate() const
1650{
1651 const size_t NF = mFiles.size();
1652 const size_t ND = mDirs.size();
1653 size_t i;
1654 for (i = 0; i < NF; i++) {
1655 if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1656 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1657 "Invalid filename. Unable to add.");
1658 return UNKNOWN_ERROR;
1659 }
1660
1661 size_t j;
1662 for (j = i+1; j < NF; j++) {
1663 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1664 mFiles.valueAt(j)->getLeaf().string()) == 0) {
1665 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1666 "File is case-insensitive equivalent to: %s",
1667 mFiles.valueAt(j)->getPrintableSource().string());
1668 return UNKNOWN_ERROR;
1669 }
1670
1671 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1672 // (this is mostly caught by the "marked" stuff, below)
1673 }
1674
1675 for (j = 0; j < ND; j++) {
1676 if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1677 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1678 SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1679 "File conflicts with dir from: %s",
1680 mDirs.valueAt(j)->getPrintableSource().string());
1681 return UNKNOWN_ERROR;
1682 }
1683 }
1684 }
1685
1686 for (i = 0; i < ND; i++) {
1687 if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1688 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1689 "Invalid directory name, unable to add.");
1690 return UNKNOWN_ERROR;
1691 }
1692
1693 size_t j;
1694 for (j = i+1; j < ND; j++) {
1695 if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1696 mDirs.valueAt(j)->getLeaf().string()) == 0) {
1697 SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1698 "Directory is case-insensitive equivalent to: %s",
1699 mDirs.valueAt(j)->getPrintableSource().string());
1700 return UNKNOWN_ERROR;
1701 }
1702 }
1703
1704 status_t err = mDirs.valueAt(i)->validate();
1705 if (err != NO_ERROR) {
1706 return err;
1707 }
1708 }
1709
1710 return NO_ERROR;
1711}
1712
1713void AaptDir::print() const
1714{
1715 const size_t ND=getDirs().size();
1716 size_t i;
1717 for (i=0; i<ND; i++) {
1718 getDirs().valueAt(i)->print();
1719 }
1720
1721 const size_t NF=getFiles().size();
1722 for (i=0; i<NF; i++) {
1723 getFiles().valueAt(i)->print();
1724 }
1725}
1726
1727String8 AaptDir::getPrintableSource() const
1728{
1729 if (mFiles.size() > 0) {
1730 // Arbitrarily pull the first file out of the list as the source dir.
1731 return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1732 }
1733 if (mDirs.size() > 0) {
1734 // Or arbitrarily pull the first dir out of the list as the source dir.
1735 return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1736 }
1737
1738 // Should never hit this case, but to be safe...
1739 return mPath;
1740
1741}
1742
1743// =========================================================================
1744// =========================================================================
1745// =========================================================================
1746
1747sp<AaptFile> AaptAssets::addFile(
1748 const String8& filePath, const AaptGroupEntry& entry,
1749 const String8& srcDir, sp<AaptGroup>* outGroup,
1750 const String8& resType)
1751{
1752 sp<AaptDir> dir = this;
1753 sp<AaptGroup> group;
1754 sp<AaptFile> file;
1755 String8 root, remain(filePath), partialPath;
1756 while (remain.length() > 0) {
1757 root = remain.walkPath(&remain);
1758 partialPath.appendPath(root);
1759
1760 const String8 rootStr(root);
1761
1762 if (remain.length() == 0) {
1763 ssize_t i = dir->getFiles().indexOfKey(rootStr);
1764 if (i >= 0) {
1765 group = dir->getFiles().valueAt(i);
1766 } else {
1767 group = new AaptGroup(rootStr, filePath);
1768 status_t res = dir->addFile(rootStr, group);
1769 if (res != NO_ERROR) {
1770 return NULL;
1771 }
1772 }
1773 file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1774 status_t res = group->addFile(file);
1775 if (res != NO_ERROR) {
1776 return NULL;
1777 }
1778 break;
1779
1780 } else {
1781 ssize_t i = dir->getDirs().indexOfKey(rootStr);
1782 if (i >= 0) {
1783 dir = dir->getDirs().valueAt(i);
1784 } else {
1785 sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1786 status_t res = dir->addDir(rootStr, subdir);
1787 if (res != NO_ERROR) {
1788 return NULL;
1789 }
1790 dir = subdir;
1791 }
1792 }
1793 }
1794
1795 mGroupEntries.add(entry);
1796 if (outGroup) *outGroup = group;
1797 return file;
1798}
1799
1800void AaptAssets::addResource(const String8& leafName, const String8& path,
1801 const sp<AaptFile>& file, const String8& resType)
1802{
1803 sp<AaptDir> res = AaptDir::makeDir(kResString);
1804 String8 dirname = file->getGroupEntry().toDirName(resType);
1805 sp<AaptDir> subdir = res->makeDir(dirname);
1806 sp<AaptGroup> grr = new AaptGroup(leafName, path);
1807 grr->addFile(file);
1808
1809 subdir->addFile(leafName, grr);
1810}
1811
1812
1813ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1814{
1815 int count;
1816 int totalCount = 0;
1817 FileType type;
1818 const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1819 const size_t dirCount =resDirs.size();
1820 sp<AaptAssets> current = this;
1821
1822 const int N = bundle->getFileSpecCount();
1823
1824 /*
1825 * If a package manifest was specified, include that first.
1826 */
1827 if (bundle->getAndroidManifestFile() != NULL) {
1828 // place at root of zip.
1829 String8 srcFile(bundle->getAndroidManifestFile());
1830 addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1831 NULL, String8());
1832 totalCount++;
1833 }
1834
1835 /*
1836 * If a directory of custom assets was supplied, slurp 'em up.
1837 */
1838 if (bundle->getAssetSourceDir()) {
1839 const char* assetDir = bundle->getAssetSourceDir();
1840
1841 FileType type = getFileType(assetDir);
1842 if (type == kFileTypeNonexistent) {
1843 fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1844 return UNKNOWN_ERROR;
1845 }
1846 if (type != kFileTypeDirectory) {
1847 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1848 return UNKNOWN_ERROR;
1849 }
1850
1851 String8 assetRoot(assetDir);
1852 sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1853 AaptGroupEntry group;
1854 count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
b5a473da 1855 String8(), mFullAssetPaths);
a534180c
TAOSP
1856 if (count < 0) {
1857 totalCount = count;
1858 goto bail;
1859 }
1860 if (count > 0) {
1861 mGroupEntries.add(group);
1862 }
1863 totalCount += count;
1864
1865 if (bundle->getVerbose())
1866 printf("Found %d custom asset file%s in %s\n",
1867 count, (count==1) ? "" : "s", assetDir);
1868 }
1869
1870 /*
1871 * If a directory of resource-specific assets was supplied, slurp 'em up.
1872 */
1873 for (size_t i=0; i<dirCount; i++) {
1874 const char *res = resDirs[i];
1875 if (res) {
1876 type = getFileType(res);
1877 if (type == kFileTypeNonexistent) {
1878 fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1879 return UNKNOWN_ERROR;
1880 }
1881 if (type == kFileTypeDirectory) {
1882 if (i>0) {
1883 sp<AaptAssets> nextOverlay = new AaptAssets();
1884 current->setOverlay(nextOverlay);
1885 current = nextOverlay;
52ffc169 1886 current->setFullResPaths(mFullResPaths);
a534180c
TAOSP
1887 }
1888 count = current->slurpResourceTree(bundle, String8(res));
1889
1890 if (count < 0) {
1891 totalCount = count;
1892 goto bail;
1893 }
1894 totalCount += count;
1895 }
1896 else {
1897 fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1898 return UNKNOWN_ERROR;
1899 }
1900 }
1901
1902 }
1903 /*
1904 * Now do any additional raw files.
1905 */
1906 for (int arg=0; arg<N; arg++) {
1907 const char* assetDir = bundle->getFileSpecEntry(arg);
1908
1909 FileType type = getFileType(assetDir);
1910 if (type == kFileTypeNonexistent) {
1911 fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1912 return UNKNOWN_ERROR;
1913 }
1914 if (type != kFileTypeDirectory) {
1915 fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1916 return UNKNOWN_ERROR;
1917 }
1918
1919 String8 assetRoot(assetDir);
1920
1921 if (bundle->getVerbose())
1922 printf("Processing raw dir '%s'\n", (const char*) assetDir);
1923
1924 /*
1925 * Do a recursive traversal of subdir tree. We don't make any
1926 * guarantees about ordering, so we're okay with an inorder search
1927 * using whatever order the OS happens to hand back to us.
1928 */
b5a473da 1929 count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
a534180c
TAOSP
1930 if (count < 0) {
1931 /* failure; report error and remove archive */
1932 totalCount = count;
1933 goto bail;
1934 }
1935 totalCount += count;
1936
1937 if (bundle->getVerbose())
1938 printf("Found %d asset file%s in %s\n",
1939 count, (count==1) ? "" : "s", assetDir);
1940 }
1941
1942 count = validate();
1943 if (count != NO_ERROR) {
1944 totalCount = count;
1945 goto bail;
1946 }
1947
1948
1949bail:
1950 return totalCount;
1951}
1952
1953ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1954 const AaptGroupEntry& kind,
52ffc169
JG
1955 const String8& resType,
1956 sp<FilePathStore>& fullResPaths)
a534180c 1957{
52ffc169 1958 ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
a534180c
TAOSP
1959 if (res > 0) {
1960 mGroupEntries.add(kind);
1961 }
1962
1963 return res;
1964}
1965
1966ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1967{
1968 ssize_t err = 0;
1969
1970 DIR* dir = opendir(srcDir.string());
1971 if (dir == NULL) {
1972 fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1973 return UNKNOWN_ERROR;
1974 }
1975
1976 status_t count = 0;
1977
1978 /*
1979 * Run through the directory, looking for dirs that match the
1980 * expected pattern.
1981 */
1982 while (1) {
1983 struct dirent* entry = readdir(dir);
1984 if (entry == NULL) {
1985 break;
1986 }
1987
1988 if (isHidden(srcDir.string(), entry->d_name)) {
1989 continue;
1990 }
1991
1992 String8 subdirName(srcDir);
1993 subdirName.appendPath(entry->d_name);
1994
1995 AaptGroupEntry group;
1996 String8 resType;
1997 bool b = group.initFromDirName(entry->d_name, &resType);
1998 if (!b) {
1999 fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
2000 entry->d_name);
2001 err = -1;
2002 continue;
2003 }
2004
73412e58
FK
2005 if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
2006 int maxResInt = atoi(bundle->getMaxResVersion());
2007 const char *verString = group.version.string();
2008 int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
2009 if (dirVersionInt > maxResInt) {
2010 fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
2011 continue;
2012 }
2013 }
2014
a534180c
TAOSP
2015 FileType type = getFileType(subdirName.string());
2016
2017 if (type == kFileTypeDirectory) {
2018 sp<AaptDir> dir = makeDir(String8(entry->d_name));
2019 ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
52ffc169 2020 resType, mFullResPaths);
a534180c
TAOSP
2021 if (res < 0) {
2022 count = res;
2023 goto bail;
2024 }
2025 if (res > 0) {
2026 mGroupEntries.add(group);
2027 count += res;
2028 }
2029
2030 mDirs.add(dir);
2031 } else {
2032 if (bundle->getVerbose()) {
2033 fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
2034 }
2035 }
2036 }
2037
2038bail:
2039 closedir(dir);
2040 dir = NULL;
2041
2042 if (err != 0) {
2043 return err;
2044 }
2045 return count;
2046}
2047
2048ssize_t
2049AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
2050{
2051 int count = 0;
2052 SortedVector<AaptGroupEntry> entries;
2053
2054 ZipFile* zip = new ZipFile;
2055 status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
2056 if (err != NO_ERROR) {
2057 fprintf(stderr, "error opening zip file %s\n", filename);
2058 count = err;
2059 delete zip;
2060 return -1;
2061 }
2062
2063 const int N = zip->getNumEntries();
2064 for (int i=0; i<N; i++) {
2065 ZipEntry* entry = zip->getEntryByIndex(i);
2066 if (entry->getDeleted()) {
2067 continue;
2068 }
2069
2070 String8 entryName(entry->getFileName());
2071
2072 String8 dirName = entryName.getPathDir();
2073 sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
2074
2075 String8 resType;
2076 AaptGroupEntry kind;
2077
2078 String8 remain;
2079 if (entryName.walkPath(&remain) == kResourceDir) {
2080 // these are the resources, pull their type out of the directory name
2081 kind.initFromDirName(remain.walkPath().string(), &resType);
2082 } else {
2083 // these are untyped and don't have an AaptGroupEntry
2084 }
2085 if (entries.indexOf(kind) < 0) {
2086 entries.add(kind);
2087 mGroupEntries.add(kind);
2088 }
2089
2090 // use the one from the zip file if they both exist.
2091 dir->removeFile(entryName.getPathLeaf());
2092
2093 sp<AaptFile> file = new AaptFile(entryName, kind, resType);
2094 status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
2095 if (err != NO_ERROR) {
2096 fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
2097 count = err;
2098 goto bail;
2099 }
2100 file->setCompressionMethod(entry->getCompressionMethod());
2101
2102#if 0
2103 if (entryName == "AndroidManifest.xml") {
2104 printf("AndroidManifest.xml\n");
2105 }
2106 printf("\n\nfile: %s\n", entryName.string());
2107#endif
2108
2109 size_t len = entry->getUncompressedLen();
2110 void* data = zip->uncompress(entry);
2111 void* buf = file->editData(len);
2112 memcpy(buf, data, len);
2113
2114#if 0
2115 const int OFF = 0;
2116 const unsigned char* p = (unsigned char*)data;
2117 const unsigned char* end = p+len;
2118 p += OFF;
2119 for (int i=0; i<32 && p < end; i++) {
2120 printf("0x%03x ", i*0x10 + OFF);
2121 for (int j=0; j<0x10 && p < end; j++) {
2122 printf(" %02x", *p);
2123 p++;
2124 }
2125 printf("\n");
2126 }
2127#endif
2128
2129 free(data);
2130
2131 count++;
2132 }
2133
2134bail:
2135 delete zip;
2136 return count;
2137}
2138
2139sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
2140{
2141 sp<AaptSymbols> sym = mSymbols.valueFor(name);
2142 if (sym == NULL) {
2143 sym = new AaptSymbols();
2144 mSymbols.add(name, sym);
2145 }
2146 return sym;
2147}
2148
2149status_t AaptAssets::buildIncludedResources(Bundle* bundle)
2150{
2151 if (!mHaveIncludedAssets) {
2152 // Add in all includes.
2153 const Vector<const char*>& incl = bundle->getPackageIncludes();
2154 const size_t N=incl.size();
2155 for (size_t i=0; i<N; i++) {
2156 if (bundle->getVerbose())
2157 printf("Including resources from package: %s\n", incl[i]);
2158 if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
2159 fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
2160 incl[i]);
2161 return UNKNOWN_ERROR;
2162 }
2163 }
2164 mHaveIncludedAssets = true;
2165 }
2166
2167 return NO_ERROR;
2168}
2169
2170status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2171{
2172 const ResTable& res = getIncludedResources();
2173 // XXX dirty!
2174 return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2175}
2176
2177const ResTable& AaptAssets::getIncludedResources() const
2178{
2179 return mIncludedAssets.getResources(false);
2180}
2181
2182void AaptAssets::print() const
2183{
2184 printf("Locale/Vendor pairs:\n");
2185 const size_t N=mGroupEntries.size();
2186 for (size_t i=0; i<N; i++) {
2187 printf(" %s/%s\n",
2188 mGroupEntries.itemAt(i).locale.string(),
2189 mGroupEntries.itemAt(i).vendor.string());
2190 }
2191
2192 printf("\nFiles:\n");
2193 AaptDir::print();
2194}
2195
6648ff78
JO
2196sp<AaptDir> AaptAssets::resDir(const String8& name)
2197{
2198 const Vector<sp<AaptDir> >& dirs = mDirs;
2199 const size_t N = dirs.size();
2200 for (size_t i=0; i<N; i++) {
2201 const sp<AaptDir>& d = dirs.itemAt(i);
2202 if (d->getLeaf() == name) {
2203 return d;
2204 }
2205 }
2206 return NULL;
2207}
2208
a534180c
TAOSP
2209bool
2210valid_symbol_name(const String8& symbol)
2211{
2212 static char const * const KEYWORDS[] = {
2213 "abstract", "assert", "boolean", "break",
2214 "byte", "case", "catch", "char", "class", "const", "continue",
2215 "default", "do", "double", "else", "enum", "extends", "final",
2216 "finally", "float", "for", "goto", "if", "implements", "import",
2217 "instanceof", "int", "interface", "long", "native", "new", "package",
2218 "private", "protected", "public", "return", "short", "static",
2219 "strictfp", "super", "switch", "synchronized", "this", "throw",
2220 "throws", "transient", "try", "void", "volatile", "while",
2221 "true", "false", "null",
2222 NULL
2223 };
2224 const char*const* k = KEYWORDS;
2225 const char*const s = symbol.string();
2226 while (*k) {
2227 if (0 == strcmp(s, *k)) {
2228 return false;
2229 }
2230 k++;
2231 }
2232 return true;
2233}