]>
git.saurik.com Git - android/aapt.git/blob - AaptAssets.cpp
2 // Copyright 2006 The Android Open Source Project
5 #include "AaptAssets.h"
8 #include <utils/misc.h>
9 #include <utils/SortedVector.h>
15 static const char* kDefaultLocale
= "default";
16 static const char* kWildcardName
= "any";
17 static const char* kAssetDir
= "assets";
18 static const char* kResourceDir
= "res";
19 static const char* kInvalidChars
= "/\\:";
20 static const size_t kMaxAssetFileName
= 100;
22 static const String8
kResString(kResourceDir
);
25 * Names of asset files must meet the following criteria:
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 { '/' '\\' ':' }
32 * Pass in just the filename, not the full path.
34 static bool validateFileName(const char* fileName
)
36 const char* cp
= fileName
;
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
50 if (len
< 1 || len
> kMaxAssetFileName
)
51 return false; // reject empty or too long
56 static bool isHidden(const char *root
, const char *path
)
58 const char *ext
= NULL
;
59 const char *type
= NULL
;
61 // Skip all hidden files.
63 // Skip ., .. and .svn but don't chatter about it.
64 if (strcmp(path
, ".") == 0
65 || strcmp(path
, "..") == 0
66 || strcmp(path
, ".svn") == 0) {
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
) {
77 } else if (strcmp(path
, "CVS") == 0) {
78 // Skip CVS but don't chatter about it.
80 } else if (strcasecmp(path
, "thumbs.db") == 0
81 || strcasecmp(path
, "picasa.ini") == 0) {
82 // Skip suspected image indexes files.
84 } else if (path
[strlen(path
)-1] == '~') {
85 // Skip suspected emacs backup files.
87 } else if ((ext
= strrchr(path
, '.')) != NULL
&& strcmp(ext
, ".scc") == 0) {
88 // Skip VisualSourceSafe files and don't chatter about it
91 // Let everything else through.
95 /* If we get this far, "type" should be set and the file
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());
107 // =========================================================================
108 // =========================================================================
109 // =========================================================================
112 AaptGroupEntry::parseNamePart(const String8
& part
, int* axis
, uint32_t* value
)
114 ResTable_config config
;
117 if (getMccName(part
.string(), &config
)) {
124 if (getMncName(part
.string(), &config
)) {
131 if (part
.length() == 2 && isalpha(part
[0]) && isalpha(part
[1])) {
132 *axis
= AXIS_LANGUAGE
;
133 *value
= part
[1] << 8 | part
[0];
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]);
145 // screen layout size
146 if (getScreenLayoutSizeName(part
.string(), &config
)) {
147 *axis
= AXIS_SCREENLAYOUTSIZE
;
148 *value
= (config
.screenLayout
&ResTable_config::MASK_SCREENSIZE
);
152 // screen layout long
153 if (getScreenLayoutLongName(part
.string(), &config
)) {
154 *axis
= AXIS_SCREENLAYOUTLONG
;
155 *value
= (config
.screenLayout
&ResTable_config::MASK_SCREENLONG
);
160 if (getOrientationName(part
.string(), &config
)) {
161 *axis
= AXIS_ORIENTATION
;
162 *value
= config
.orientation
;
167 if (getUiModeTypeName(part
.string(), &config
)) {
168 *axis
= AXIS_UIMODETYPE
;
169 *value
= (config
.uiMode
&ResTable_config::MASK_UI_MODE_TYPE
);
174 if (getUiModeNightName(part
.string(), &config
)) {
175 *axis
= AXIS_UIMODENIGHT
;
176 *value
= (config
.uiMode
&ResTable_config::MASK_UI_MODE_NIGHT
);
181 if (getDensityName(part
.string(), &config
)) {
182 *axis
= AXIS_DENSITY
;
183 *value
= config
.density
;
188 if (getTouchscreenName(part
.string(), &config
)) {
189 *axis
= AXIS_TOUCHSCREEN
;
190 *value
= config
.touchscreen
;
195 if (getKeysHiddenName(part
.string(), &config
)) {
196 *axis
= AXIS_KEYSHIDDEN
;
197 *value
= config
.inputFlags
;
202 if (getKeyboardName(part
.string(), &config
)) {
203 *axis
= AXIS_KEYBOARD
;
204 *value
= config
.keyboard
;
209 if (getNavHiddenName(part
.string(), &config
)) {
210 *axis
= AXIS_NAVHIDDEN
;
211 *value
= config
.inputFlags
;
216 if (getNavigationName(part
.string(), &config
)) {
217 *axis
= AXIS_NAVIGATION
;
218 *value
= config
.navigation
;
223 if (getScreenSizeName(part
.string(), &config
)) {
224 *axis
= AXIS_SCREENSIZE
;
225 *value
= config
.screenSize
;
230 if (getVersionName(part
.string(), &config
)) {
231 *axis
= AXIS_VERSION
;
232 *value
= config
.version
;
240 AaptGroupEntry::initFromDirName(const char* dir
, String8
* resType
)
242 Vector
<String8
> parts
;
244 String8 mcc
, mnc
, loc
, layoutsize
, layoutlong
, orient
, den
;
245 String8 touch
, key
, keysHidden
, nav
, navHidden
, size
, vers
;
246 String8 uiModeType
, uiModeNight
;
250 while (NULL
!= (q
= strchr(p
, '-'))) {
254 //printf("part: %s\n", parts[parts.size()-1].string());
260 //printf("part: %s\n", parts[parts.size()-1].string());
262 const int N
= parts
.size();
264 String8 part
= parts
[index
];
267 if (!isValidResourceType(part
)) {
279 if (getMccName(part
.string())) {
288 //printf("not mcc: %s\n", part.string());
292 if (getMncName(part
.string())) {
301 //printf("not mcc: %s\n", part.string());
305 if (part
.length() == 2 && isalpha(part
[0]) && isalpha(part
[1])) {
314 //printf("not language: %s\n", part.string());
319 && part
.length() == 3 && part
[0] == 'r' && part
[0] && part
[1]) {
322 loc
+= part
.string() + 1;
330 //printf("not region: %s\n", part.string());
333 if (getScreenLayoutSizeName(part
.string())) {
342 //printf("not screen layout size: %s\n", part.string());
345 if (getScreenLayoutLongName(part
.string())) {
354 //printf("not screen layout long: %s\n", part.string());
358 if (getOrientationName(part
.string())) {
367 //printf("not orientation: %s\n", part.string());
371 if (getUiModeTypeName(part
.string())) {
380 //printf("not ui mode type: %s\n", part.string());
384 if (getUiModeNightName(part
.string())) {
393 //printf("not ui mode night: %s\n", part.string());
397 if (getDensityName(part
.string())) {
406 //printf("not density: %s\n", part.string());
410 if (getTouchscreenName(part
.string())) {
419 //printf("not touchscreen: %s\n", part.string());
423 if (getKeysHiddenName(part
.string())) {
432 //printf("not keysHidden: %s\n", part.string());
436 if (getKeyboardName(part
.string())) {
445 //printf("not keyboard: %s\n", part.string());
449 if (getNavHiddenName(part
.string())) {
458 //printf("not navHidden: %s\n", part.string());
461 if (getNavigationName(part
.string())) {
470 //printf("not navigation: %s\n", part.string());
473 if (getScreenSizeName(part
.string())) {
482 //printf("not screen size: %s\n", part.string());
485 if (getVersionName(part
.string())) {
494 //printf("not version: %s\n", part.string());
497 // if there are extra parts, it doesn't match
504 this->screenLayoutSize
= layoutsize
;
505 this->screenLayoutLong
= layoutlong
;
506 this->orientation
= orient
;
507 this->uiModeType
= uiModeType
;
508 this->uiModeNight
= uiModeNight
;
510 this->touchscreen
= touch
;
511 this->keysHidden
= keysHidden
;
512 this->keyboard
= key
;
513 this->navHidden
= navHidden
;
514 this->navigation
= nav
;
515 this->screenSize
= size
;
516 this->version
= vers
;
518 // what is this anyway?
525 AaptGroupEntry::toString() const
527 String8 s
= this->mcc
;
533 s
+= screenLayoutSize
;
535 s
+= screenLayoutLong
;
537 s
+= this->orientation
;
562 AaptGroupEntry::toDirName(const String8
& resType
) const
565 if (this->mcc
!= "") {
569 if (this->mnc
!= "") {
573 if (this->locale
!= "") {
577 if (this->screenLayoutSize
!= "") {
579 s
+= screenLayoutSize
;
581 if (this->screenLayoutLong
!= "") {
583 s
+= screenLayoutLong
;
585 if (this->orientation
!= "") {
589 if (this->uiModeType
!= "") {
593 if (this->uiModeNight
!= "") {
597 if (this->density
!= "") {
601 if (this->touchscreen
!= "") {
605 if (this->keysHidden
!= "") {
609 if (this->keyboard
!= "") {
613 if (this->navHidden
!= "") {
617 if (this->navigation
!= "") {
621 if (this->screenSize
!= "") {
625 if (this->version
!= "") {
633 bool AaptGroupEntry::getMccName(const char* name
,
634 ResTable_config
* out
)
636 if (strcmp(name
, kWildcardName
) == 0) {
637 if (out
) out
->mcc
= 0;
640 const char* c
= name
;
641 if (tolower(*c
) != 'm') return false;
643 if (tolower(*c
) != 'c') return false;
645 if (tolower(*c
) != 'c') return false;
650 while (*c
>= '0' && *c
<= '9') {
653 if (*c
!= 0) return false;
654 if (c
-val
!= 3) return false;
658 if (out
) out
->mcc
= d
;
665 bool AaptGroupEntry::getMncName(const char* name
,
666 ResTable_config
* out
)
668 if (strcmp(name
, kWildcardName
) == 0) {
669 if (out
) out
->mcc
= 0;
672 const char* c
= name
;
673 if (tolower(*c
) != 'm') return false;
675 if (tolower(*c
) != 'n') return false;
677 if (tolower(*c
) != 'c') return false;
682 while (*c
>= '0' && *c
<= '9') {
685 if (*c
!= 0) return false;
686 if (c
-val
== 0 || c
-val
> 3) return false;
690 if (out
) out
->mnc
= d
;
698 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
701 * TODO: Should insist that the first two letters are lower case, and the
702 * second two are upper.
704 bool AaptGroupEntry::getLocaleName(const char* fileName
,
705 ResTable_config
* out
)
707 if (strcmp(fileName
, kWildcardName
) == 0
708 || strcmp(fileName
, kDefaultLocale
) == 0) {
710 out
->language
[0] = 0;
711 out
->language
[1] = 0;
718 if (strlen(fileName
) == 2 && isalpha(fileName
[0]) && isalpha(fileName
[1])) {
720 out
->language
[0] = fileName
[0];
721 out
->language
[1] = fileName
[1];
728 if (strlen(fileName
) == 5 &&
729 isalpha(fileName
[0]) &&
730 isalpha(fileName
[1]) &&
731 fileName
[2] == '-' &&
732 isalpha(fileName
[3]) &&
733 isalpha(fileName
[4])) {
735 out
->language
[0] = fileName
[0];
736 out
->language
[1] = fileName
[1];
737 out
->country
[0] = fileName
[3];
738 out
->country
[1] = fileName
[4];
746 bool AaptGroupEntry::getScreenLayoutSizeName(const char* name
,
747 ResTable_config
* out
)
749 if (strcmp(name
, kWildcardName
) == 0) {
750 if (out
) out
->screenLayout
=
751 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
752 | ResTable_config::SCREENSIZE_ANY
;
754 } else if (strcmp(name
, "small") == 0) {
755 if (out
) out
->screenLayout
=
756 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
757 | ResTable_config::SCREENSIZE_SMALL
;
759 } else if (strcmp(name
, "normal") == 0) {
760 if (out
) out
->screenLayout
=
761 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
762 | ResTable_config::SCREENSIZE_NORMAL
;
764 } else if (strcmp(name
, "large") == 0) {
765 if (out
) out
->screenLayout
=
766 (out
->screenLayout
&~ResTable_config::MASK_SCREENSIZE
)
767 | ResTable_config::SCREENSIZE_LARGE
;
774 bool AaptGroupEntry::getScreenLayoutLongName(const char* name
,
775 ResTable_config
* out
)
777 if (strcmp(name
, kWildcardName
) == 0) {
778 if (out
) out
->screenLayout
=
779 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
780 | ResTable_config::SCREENLONG_ANY
;
782 } else if (strcmp(name
, "long") == 0) {
783 if (out
) out
->screenLayout
=
784 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
785 | ResTable_config::SCREENLONG_YES
;
787 } else if (strcmp(name
, "notlong") == 0) {
788 if (out
) out
->screenLayout
=
789 (out
->screenLayout
&~ResTable_config::MASK_SCREENLONG
)
790 | ResTable_config::SCREENLONG_NO
;
797 bool AaptGroupEntry::getOrientationName(const char* name
,
798 ResTable_config
* out
)
800 if (strcmp(name
, kWildcardName
) == 0) {
801 if (out
) out
->orientation
= out
->ORIENTATION_ANY
;
803 } else if (strcmp(name
, "port") == 0) {
804 if (out
) out
->orientation
= out
->ORIENTATION_PORT
;
806 } else if (strcmp(name
, "land") == 0) {
807 if (out
) out
->orientation
= out
->ORIENTATION_LAND
;
809 } else if (strcmp(name
, "square") == 0) {
810 if (out
) out
->orientation
= out
->ORIENTATION_SQUARE
;
817 bool AaptGroupEntry::getUiModeTypeName(const char* name
,
818 ResTable_config
* out
)
820 if (strcmp(name
, kWildcardName
) == 0) {
821 if (out
) out
->uiMode
=
822 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_TYPE
)
823 | ResTable_config::UI_MODE_TYPE_NORMAL
;
825 } else if (strcmp(name
, "car") == 0) {
826 if (out
) out
->uiMode
=
827 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_TYPE
)
828 | ResTable_config::UI_MODE_TYPE_CAR
;
835 bool AaptGroupEntry::getUiModeNightName(const char* name
,
836 ResTable_config
* out
)
838 if (strcmp(name
, kWildcardName
) == 0) {
839 if (out
) out
->uiMode
=
840 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_NIGHT
)
841 | ResTable_config::UI_MODE_NIGHT_ANY
;
843 } else if (strcmp(name
, "night") == 0) {
844 if (out
) out
->uiMode
=
845 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_NIGHT
)
846 | ResTable_config::UI_MODE_NIGHT_YES
;
848 } else if (strcmp(name
, "notnight") == 0) {
849 if (out
) out
->uiMode
=
850 (out
->uiMode
&~ResTable_config::MASK_UI_MODE_NIGHT
)
851 | ResTable_config::UI_MODE_NIGHT_NO
;
858 bool AaptGroupEntry::getDensityName(const char* name
,
859 ResTable_config
* out
)
861 if (strcmp(name
, kWildcardName
) == 0) {
862 if (out
) out
->density
= ResTable_config::DENSITY_DEFAULT
;
866 if (strcmp(name
, "nodpi") == 0) {
867 if (out
) out
->density
= ResTable_config::DENSITY_NONE
;
871 if (strcmp(name
, "ldpi") == 0) {
872 if (out
) out
->density
= ResTable_config::DENSITY_LOW
;
876 if (strcmp(name
, "mdpi") == 0) {
877 if (out
) out
->density
= ResTable_config::DENSITY_MEDIUM
;
881 if (strcmp(name
, "hdpi") == 0) {
882 if (out
) out
->density
= ResTable_config::DENSITY_HIGH
;
886 char* c
= (char*)name
;
887 while (*c
>= '0' && *c
<= '9') {
891 // check that we have 'dpi' after the last digit.
892 if (toupper(c
[0]) != 'D' ||
893 toupper(c
[1]) != 'P' ||
894 toupper(c
[2]) != 'I' ||
899 // temporarily replace the first letter with \0 to
908 if (out
) out
->density
= d
;
915 bool AaptGroupEntry::getTouchscreenName(const char* name
,
916 ResTable_config
* out
)
918 if (strcmp(name
, kWildcardName
) == 0) {
919 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_ANY
;
921 } else if (strcmp(name
, "notouch") == 0) {
922 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_NOTOUCH
;
924 } else if (strcmp(name
, "stylus") == 0) {
925 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_STYLUS
;
927 } else if (strcmp(name
, "finger") == 0) {
928 if (out
) out
->touchscreen
= out
->TOUCHSCREEN_FINGER
;
935 bool AaptGroupEntry::getKeysHiddenName(const char* name
,
936 ResTable_config
* out
)
940 if (strcmp(name
, kWildcardName
) == 0) {
941 mask
= out
->MASK_KEYSHIDDEN
;
942 value
= out
->KEYSHIDDEN_ANY
;
943 } else if (strcmp(name
, "keysexposed") == 0) {
944 mask
= out
->MASK_KEYSHIDDEN
;
945 value
= out
->KEYSHIDDEN_NO
;
946 } else if (strcmp(name
, "keyshidden") == 0) {
947 mask
= out
->MASK_KEYSHIDDEN
;
948 value
= out
->KEYSHIDDEN_YES
;
949 } else if (strcmp(name
, "keyssoft") == 0) {
950 mask
= out
->MASK_KEYSHIDDEN
;
951 value
= out
->KEYSHIDDEN_SOFT
;
955 if (out
) out
->inputFlags
= (out
->inputFlags
&~mask
) | value
;
962 bool AaptGroupEntry::getKeyboardName(const char* name
,
963 ResTable_config
* out
)
965 if (strcmp(name
, kWildcardName
) == 0) {
966 if (out
) out
->keyboard
= out
->KEYBOARD_ANY
;
968 } else if (strcmp(name
, "nokeys") == 0) {
969 if (out
) out
->keyboard
= out
->KEYBOARD_NOKEYS
;
971 } else if (strcmp(name
, "qwerty") == 0) {
972 if (out
) out
->keyboard
= out
->KEYBOARD_QWERTY
;
974 } else if (strcmp(name
, "12key") == 0) {
975 if (out
) out
->keyboard
= out
->KEYBOARD_12KEY
;
982 bool AaptGroupEntry::getNavHiddenName(const char* name
,
983 ResTable_config
* out
)
987 if (strcmp(name
, kWildcardName
) == 0) {
988 mask
= out
->MASK_NAVHIDDEN
;
989 value
= out
->NAVHIDDEN_ANY
;
990 } else if (strcmp(name
, "navexposed") == 0) {
991 mask
= out
->MASK_NAVHIDDEN
;
992 value
= out
->NAVHIDDEN_NO
;
993 } else if (strcmp(name
, "navhidden") == 0) {
994 mask
= out
->MASK_NAVHIDDEN
;
995 value
= out
->NAVHIDDEN_YES
;
999 if (out
) out
->inputFlags
= (out
->inputFlags
&~mask
) | value
;
1006 bool AaptGroupEntry::getNavigationName(const char* name
,
1007 ResTable_config
* out
)
1009 if (strcmp(name
, kWildcardName
) == 0) {
1010 if (out
) out
->navigation
= out
->NAVIGATION_ANY
;
1012 } else if (strcmp(name
, "nonav") == 0) {
1013 if (out
) out
->navigation
= out
->NAVIGATION_NONAV
;
1015 } else if (strcmp(name
, "dpad") == 0) {
1016 if (out
) out
->navigation
= out
->NAVIGATION_DPAD
;
1018 } else if (strcmp(name
, "trackball") == 0) {
1019 if (out
) out
->navigation
= out
->NAVIGATION_TRACKBALL
;
1021 } else if (strcmp(name
, "wheel") == 0) {
1022 if (out
) out
->navigation
= out
->NAVIGATION_WHEEL
;
1029 bool AaptGroupEntry::getScreenSizeName(const char* name
,
1030 ResTable_config
* out
)
1032 if (strcmp(name
, kWildcardName
) == 0) {
1034 out
->screenWidth
= out
->SCREENWIDTH_ANY
;
1035 out
->screenHeight
= out
->SCREENHEIGHT_ANY
;
1040 const char* x
= name
;
1041 while (*x
>= '0' && *x
<= '9') x
++;
1042 if (x
== name
|| *x
!= 'x') return false;
1043 String8
xName(name
, x
-name
);
1047 while (*y
>= '0' && *y
<= '9') y
++;
1048 if (y
== name
|| *y
!= 0) return false;
1049 String8
yName(x
, y
-x
);
1051 uint16_t w
= (uint16_t)atoi(xName
.string());
1052 uint16_t h
= (uint16_t)atoi(yName
.string());
1058 out
->screenWidth
= w
;
1059 out
->screenHeight
= h
;
1065 bool AaptGroupEntry::getVersionName(const char* name
,
1066 ResTable_config
* out
)
1068 if (strcmp(name
, kWildcardName
) == 0) {
1070 out
->sdkVersion
= out
->SDKVERSION_ANY
;
1071 out
->minorVersion
= out
->MINORVERSION_ANY
;
1081 const char* s
= name
;
1082 while (*s
>= '0' && *s
<= '9') s
++;
1083 if (s
== name
|| *s
!= 0) return false;
1084 String8
sdkName(name
, s
-name
);
1087 out
->sdkVersion
= (uint16_t)atoi(sdkName
.string());
1088 out
->minorVersion
= 0;
1094 int AaptGroupEntry::compare(const AaptGroupEntry
& o
) const
1096 int v
= mcc
.compare(o
.mcc
);
1097 if (v
== 0) v
= mnc
.compare(o
.mnc
);
1098 if (v
== 0) v
= locale
.compare(o
.locale
);
1099 if (v
== 0) v
= vendor
.compare(o
.vendor
);
1100 if (v
== 0) v
= screenLayoutSize
.compare(o
.screenLayoutSize
);
1101 if (v
== 0) v
= screenLayoutLong
.compare(o
.screenLayoutLong
);
1102 if (v
== 0) v
= orientation
.compare(o
.orientation
);
1103 if (v
== 0) v
= uiModeType
.compare(o
.uiModeType
);
1104 if (v
== 0) v
= uiModeNight
.compare(o
.uiModeNight
);
1105 if (v
== 0) v
= density
.compare(o
.density
);
1106 if (v
== 0) v
= touchscreen
.compare(o
.touchscreen
);
1107 if (v
== 0) v
= keysHidden
.compare(o
.keysHidden
);
1108 if (v
== 0) v
= keyboard
.compare(o
.keyboard
);
1109 if (v
== 0) v
= navHidden
.compare(o
.navHidden
);
1110 if (v
== 0) v
= navigation
.compare(o
.navigation
);
1111 if (v
== 0) v
= screenSize
.compare(o
.screenSize
);
1112 if (v
== 0) v
= version
.compare(o
.version
);
1116 ResTable_config
AaptGroupEntry::toParams() const
1118 ResTable_config params
;
1119 memset(¶ms
, 0, sizeof(params
));
1120 getMccName(mcc
.string(), ¶ms
);
1121 getMncName(mnc
.string(), ¶ms
);
1122 getLocaleName(locale
.string(), ¶ms
);
1123 getScreenLayoutSizeName(screenLayoutSize
.string(), ¶ms
);
1124 getScreenLayoutLongName(screenLayoutLong
.string(), ¶ms
);
1125 getOrientationName(orientation
.string(), ¶ms
);
1126 getUiModeTypeName(uiModeType
.string(), ¶ms
);
1127 getUiModeNightName(uiModeNight
.string(), ¶ms
);
1128 getDensityName(density
.string(), ¶ms
);
1129 getTouchscreenName(touchscreen
.string(), ¶ms
);
1130 getKeysHiddenName(keysHidden
.string(), ¶ms
);
1131 getKeyboardName(keyboard
.string(), ¶ms
);
1132 getNavHiddenName(navHidden
.string(), ¶ms
);
1133 getNavigationName(navigation
.string(), ¶ms
);
1134 getScreenSizeName(screenSize
.string(), ¶ms
);
1135 getVersionName(version
.string(), ¶ms
);
1139 // =========================================================================
1140 // =========================================================================
1141 // =========================================================================
1143 void* AaptFile::editData(size_t size
)
1145 if (size
<= mBufferSize
) {
1149 size_t allocSize
= (size
*3)/2;
1150 void* buf
= realloc(mData
, allocSize
);
1156 mBufferSize
= allocSize
;
1160 void* AaptFile::editData(size_t* outSize
)
1163 *outSize
= mDataSize
;
1168 void* AaptFile::padData(size_t wordSize
)
1170 const size_t extra
= mDataSize%wordSize
;
1175 size_t initial
= mDataSize
;
1176 void* data
= editData(initial
+(wordSize
-extra
));
1178 memset(((uint8_t*)data
) + initial
, 0, wordSize
-extra
);
1183 status_t
AaptFile::writeData(const void* data
, size_t size
)
1185 size_t end
= mDataSize
;
1186 size_t total
= size
+ end
;
1187 void* buf
= editData(total
);
1189 return UNKNOWN_ERROR
;
1191 memcpy(((char*)buf
)+end
, data
, size
);
1195 void AaptFile::clearData()
1197 if (mData
!= NULL
) free(mData
);
1203 String8
AaptFile::getPrintableSource() const
1206 String8
name(mGroupEntry
.locale
.string());
1207 name
.appendPath(mGroupEntry
.vendor
.string());
1208 name
.appendPath(mPath
);
1209 name
.append(" #generated");
1215 // =========================================================================
1216 // =========================================================================
1217 // =========================================================================
1219 status_t
AaptGroup::addFile(const sp
<AaptFile
>& file
)
1221 if (mFiles
.indexOfKey(file
->getGroupEntry()) < 0) {
1222 file
->mPath
= mPath
;
1223 mFiles
.add(file
->getGroupEntry(), file
);
1227 SourcePos(file
->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1228 getPrintableSource().string());
1229 return UNKNOWN_ERROR
;
1232 void AaptGroup::removeFile(size_t index
)
1234 mFiles
.removeItemsAt(index
);
1237 void AaptGroup::print() const
1239 printf(" %s\n", getPath().string());
1240 const size_t N
=mFiles
.size();
1242 for (i
=0; i
<N
; i
++) {
1243 sp
<AaptFile
> file
= mFiles
.valueAt(i
);
1244 const AaptGroupEntry
& e
= file
->getGroupEntry();
1245 if (file
->hasData()) {
1246 printf(" Gen: (%s) %d bytes\n", e
.toString().string(),
1247 (int)file
->getSize());
1249 printf(" Src: %s\n", file
->getPrintableSource().string());
1254 String8
AaptGroup::getPrintableSource() const
1256 if (mFiles
.size() > 0) {
1257 // Arbitrarily pull the first source file out of the list.
1258 return mFiles
.valueAt(0)->getPrintableSource();
1261 // Should never hit this case, but to be safe...
1266 // =========================================================================
1267 // =========================================================================
1268 // =========================================================================
1270 status_t
AaptDir::addFile(const String8
& name
, const sp
<AaptGroup
>& file
)
1272 if (mFiles
.indexOfKey(name
) >= 0) {
1273 return ALREADY_EXISTS
;
1275 mFiles
.add(name
, file
);
1279 status_t
AaptDir::addDir(const String8
& name
, const sp
<AaptDir
>& dir
)
1281 if (mDirs
.indexOfKey(name
) >= 0) {
1282 return ALREADY_EXISTS
;
1284 mDirs
.add(name
, dir
);
1288 sp
<AaptDir
> AaptDir::makeDir(const String8
& path
)
1291 String8 remain
= path
;
1293 sp
<AaptDir
> subdir
= this;
1294 while (name
= remain
.walkPath(&remain
), remain
!= "") {
1295 subdir
= subdir
->makeDir(name
);
1298 ssize_t i
= subdir
->mDirs
.indexOfKey(name
);
1300 return subdir
->mDirs
.valueAt(i
);
1302 sp
<AaptDir
> dir
= new AaptDir(name
, subdir
->mPath
.appendPathCopy(name
));
1303 subdir
->mDirs
.add(name
, dir
);
1307 void AaptDir::removeFile(const String8
& name
)
1309 mFiles
.removeItem(name
);
1312 void AaptDir::removeDir(const String8
& name
)
1314 mDirs
.removeItem(name
);
1317 status_t
AaptDir::renameFile(const sp
<AaptFile
>& file
, const String8
& newName
)
1319 sp
<AaptGroup
> origGroup
;
1321 // Find and remove the given file with shear, brute force!
1322 const size_t NG
= mFiles
.size();
1324 for (i
=0; origGroup
== NULL
&& i
<NG
; i
++) {
1325 sp
<AaptGroup
> g
= mFiles
.valueAt(i
);
1326 const size_t NF
= g
->getFiles().size();
1327 for (size_t j
=0; j
<NF
; j
++) {
1328 if (g
->getFiles().valueAt(j
) == file
) {
1332 mFiles
.removeItemsAt(i
);
1339 //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1341 // Place the file under its new name.
1342 if (origGroup
!= NULL
) {
1343 return addLeafFile(newName
, file
);
1349 status_t
AaptDir::addLeafFile(const String8
& leafName
, const sp
<AaptFile
>& file
)
1351 sp
<AaptGroup
> group
;
1352 if (mFiles
.indexOfKey(leafName
) >= 0) {
1353 group
= mFiles
.valueFor(leafName
);
1355 group
= new AaptGroup(leafName
, mPath
.appendPathCopy(leafName
));
1356 mFiles
.add(leafName
, group
);
1359 return group
->addFile(file
);
1362 ssize_t
AaptDir::slurpFullTree(Bundle
* bundle
, const String8
& srcDir
,
1363 const AaptGroupEntry
& kind
, const String8
& resType
)
1365 Vector
<String8
> fileNames
;
1370 dir
= opendir(srcDir
.string());
1372 fprintf(stderr
, "ERROR: opendir(%s): %s\n", srcDir
.string(), strerror(errno
));
1373 return UNKNOWN_ERROR
;
1377 * Slurp the filenames out of the directory.
1380 struct dirent
* entry
;
1382 entry
= readdir(dir
);
1386 if (isHidden(srcDir
.string(), entry
->d_name
))
1389 fileNames
.add(String8(entry
->d_name
));
1398 * Stash away the files and recursively descend into subdirectories.
1400 const size_t N
= fileNames
.size();
1402 for (i
= 0; i
< N
; i
++) {
1403 String8
pathName(srcDir
);
1406 pathName
.appendPath(fileNames
[i
].string());
1407 type
= getFileType(pathName
.string());
1408 if (type
== kFileTypeDirectory
) {
1410 bool notAdded
= false;
1411 if (mDirs
.indexOfKey(fileNames
[i
]) >= 0) {
1412 subdir
= mDirs
.valueFor(fileNames
[i
]);
1414 subdir
= new AaptDir(fileNames
[i
], mPath
.appendPathCopy(fileNames
[i
]));
1417 ssize_t res
= subdir
->slurpFullTree(bundle
, pathName
, kind
,
1419 if (res
< NO_ERROR
) {
1422 if (res
> 0 && notAdded
) {
1423 mDirs
.add(fileNames
[i
], subdir
);
1426 } else if (type
== kFileTypeRegular
) {
1427 sp
<AaptFile
> file
= new AaptFile(pathName
, kind
, resType
);
1428 status_t err
= addLeafFile(fileNames
[i
], file
);
1429 if (err
!= NO_ERROR
) {
1436 if (bundle
->getVerbose())
1437 printf(" (ignoring non-file/dir '%s')\n", pathName
.string());
1444 status_t
AaptDir::validate() const
1446 const size_t NF
= mFiles
.size();
1447 const size_t ND
= mDirs
.size();
1449 for (i
= 0; i
< NF
; i
++) {
1450 if (!validateFileName(mFiles
.valueAt(i
)->getLeaf().string())) {
1451 SourcePos(mFiles
.valueAt(i
)->getPrintableSource(), -1).error(
1452 "Invalid filename. Unable to add.");
1453 return UNKNOWN_ERROR
;
1457 for (j
= i
+1; j
< NF
; j
++) {
1458 if (strcasecmp(mFiles
.valueAt(i
)->getLeaf().string(),
1459 mFiles
.valueAt(j
)->getLeaf().string()) == 0) {
1460 SourcePos(mFiles
.valueAt(i
)->getPrintableSource(), -1).error(
1461 "File is case-insensitive equivalent to: %s",
1462 mFiles
.valueAt(j
)->getPrintableSource().string());
1463 return UNKNOWN_ERROR
;
1466 // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1467 // (this is mostly caught by the "marked" stuff, below)
1470 for (j
= 0; j
< ND
; j
++) {
1471 if (strcasecmp(mFiles
.valueAt(i
)->getLeaf().string(),
1472 mDirs
.valueAt(j
)->getLeaf().string()) == 0) {
1473 SourcePos(mFiles
.valueAt(i
)->getPrintableSource(), -1).error(
1474 "File conflicts with dir from: %s",
1475 mDirs
.valueAt(j
)->getPrintableSource().string());
1476 return UNKNOWN_ERROR
;
1481 for (i
= 0; i
< ND
; i
++) {
1482 if (!validateFileName(mDirs
.valueAt(i
)->getLeaf().string())) {
1483 SourcePos(mDirs
.valueAt(i
)->getPrintableSource(), -1).error(
1484 "Invalid directory name, unable to add.");
1485 return UNKNOWN_ERROR
;
1489 for (j
= i
+1; j
< ND
; j
++) {
1490 if (strcasecmp(mDirs
.valueAt(i
)->getLeaf().string(),
1491 mDirs
.valueAt(j
)->getLeaf().string()) == 0) {
1492 SourcePos(mDirs
.valueAt(i
)->getPrintableSource(), -1).error(
1493 "Directory is case-insensitive equivalent to: %s",
1494 mDirs
.valueAt(j
)->getPrintableSource().string());
1495 return UNKNOWN_ERROR
;
1499 status_t err
= mDirs
.valueAt(i
)->validate();
1500 if (err
!= NO_ERROR
) {
1508 void AaptDir::print() const
1510 const size_t ND
=getDirs().size();
1512 for (i
=0; i
<ND
; i
++) {
1513 getDirs().valueAt(i
)->print();
1516 const size_t NF
=getFiles().size();
1517 for (i
=0; i
<NF
; i
++) {
1518 getFiles().valueAt(i
)->print();
1522 String8
AaptDir::getPrintableSource() const
1524 if (mFiles
.size() > 0) {
1525 // Arbitrarily pull the first file out of the list as the source dir.
1526 return mFiles
.valueAt(0)->getPrintableSource().getPathDir();
1528 if (mDirs
.size() > 0) {
1529 // Or arbitrarily pull the first dir out of the list as the source dir.
1530 return mDirs
.valueAt(0)->getPrintableSource().getPathDir();
1533 // Should never hit this case, but to be safe...
1538 // =========================================================================
1539 // =========================================================================
1540 // =========================================================================
1542 sp
<AaptFile
> AaptAssets::addFile(
1543 const String8
& filePath
, const AaptGroupEntry
& entry
,
1544 const String8
& srcDir
, sp
<AaptGroup
>* outGroup
,
1545 const String8
& resType
)
1547 sp
<AaptDir
> dir
= this;
1548 sp
<AaptGroup
> group
;
1550 String8 root
, remain(filePath
), partialPath
;
1551 while (remain
.length() > 0) {
1552 root
= remain
.walkPath(&remain
);
1553 partialPath
.appendPath(root
);
1555 const String8
rootStr(root
);
1557 if (remain
.length() == 0) {
1558 ssize_t i
= dir
->getFiles().indexOfKey(rootStr
);
1560 group
= dir
->getFiles().valueAt(i
);
1562 group
= new AaptGroup(rootStr
, filePath
);
1563 status_t res
= dir
->addFile(rootStr
, group
);
1564 if (res
!= NO_ERROR
) {
1568 file
= new AaptFile(srcDir
.appendPathCopy(filePath
), entry
, resType
);
1569 status_t res
= group
->addFile(file
);
1570 if (res
!= NO_ERROR
) {
1576 ssize_t i
= dir
->getDirs().indexOfKey(rootStr
);
1578 dir
= dir
->getDirs().valueAt(i
);
1580 sp
<AaptDir
> subdir
= new AaptDir(rootStr
, partialPath
);
1581 status_t res
= dir
->addDir(rootStr
, subdir
);
1582 if (res
!= NO_ERROR
) {
1590 mGroupEntries
.add(entry
);
1591 if (outGroup
) *outGroup
= group
;
1595 void AaptAssets::addResource(const String8
& leafName
, const String8
& path
,
1596 const sp
<AaptFile
>& file
, const String8
& resType
)
1598 sp
<AaptDir
> res
= AaptDir::makeDir(kResString
);
1599 String8 dirname
= file
->getGroupEntry().toDirName(resType
);
1600 sp
<AaptDir
> subdir
= res
->makeDir(dirname
);
1601 sp
<AaptGroup
> grr
= new AaptGroup(leafName
, path
);
1604 subdir
->addFile(leafName
, grr
);
1608 ssize_t
AaptAssets::slurpFromArgs(Bundle
* bundle
)
1613 const Vector
<const char *>& resDirs
= bundle
->getResourceSourceDirs();
1614 const size_t dirCount
=resDirs
.size();
1615 sp
<AaptAssets
> current
= this;
1617 const int N
= bundle
->getFileSpecCount();
1620 * If a package manifest was specified, include that first.
1622 if (bundle
->getAndroidManifestFile() != NULL
) {
1623 // place at root of zip.
1624 String8
srcFile(bundle
->getAndroidManifestFile());
1625 addFile(srcFile
.getPathLeaf(), AaptGroupEntry(), srcFile
.getPathDir(),
1631 * If a directory of custom assets was supplied, slurp 'em up.
1633 if (bundle
->getAssetSourceDir()) {
1634 const char* assetDir
= bundle
->getAssetSourceDir();
1636 FileType type
= getFileType(assetDir
);
1637 if (type
== kFileTypeNonexistent
) {
1638 fprintf(stderr
, "ERROR: asset directory '%s' does not exist\n", assetDir
);
1639 return UNKNOWN_ERROR
;
1641 if (type
!= kFileTypeDirectory
) {
1642 fprintf(stderr
, "ERROR: '%s' is not a directory\n", assetDir
);
1643 return UNKNOWN_ERROR
;
1646 String8
assetRoot(assetDir
);
1647 sp
<AaptDir
> assetAaptDir
= makeDir(String8(kAssetDir
));
1648 AaptGroupEntry group
;
1649 count
= assetAaptDir
->slurpFullTree(bundle
, assetRoot
, group
,
1656 mGroupEntries
.add(group
);
1658 totalCount
+= count
;
1660 if (bundle
->getVerbose())
1661 printf("Found %d custom asset file%s in %s\n",
1662 count
, (count
==1) ? "" : "s", assetDir
);
1666 * If a directory of resource-specific assets was supplied, slurp 'em up.
1668 for (size_t i
=0; i
<dirCount
; i
++) {
1669 const char *res
= resDirs
[i
];
1671 type
= getFileType(res
);
1672 if (type
== kFileTypeNonexistent
) {
1673 fprintf(stderr
, "ERROR: resource directory '%s' does not exist\n", res
);
1674 return UNKNOWN_ERROR
;
1676 if (type
== kFileTypeDirectory
) {
1678 sp
<AaptAssets
> nextOverlay
= new AaptAssets();
1679 current
->setOverlay(nextOverlay
);
1680 current
= nextOverlay
;
1682 count
= current
->slurpResourceTree(bundle
, String8(res
));
1688 totalCount
+= count
;
1691 fprintf(stderr
, "ERROR: '%s' is not a directory\n", res
);
1692 return UNKNOWN_ERROR
;
1698 * Now do any additional raw files.
1700 for (int arg
=0; arg
<N
; arg
++) {
1701 const char* assetDir
= bundle
->getFileSpecEntry(arg
);
1703 FileType type
= getFileType(assetDir
);
1704 if (type
== kFileTypeNonexistent
) {
1705 fprintf(stderr
, "ERROR: input directory '%s' does not exist\n", assetDir
);
1706 return UNKNOWN_ERROR
;
1708 if (type
!= kFileTypeDirectory
) {
1709 fprintf(stderr
, "ERROR: '%s' is not a directory\n", assetDir
);
1710 return UNKNOWN_ERROR
;
1713 String8
assetRoot(assetDir
);
1715 if (bundle
->getVerbose())
1716 printf("Processing raw dir '%s'\n", (const char*) assetDir
);
1719 * Do a recursive traversal of subdir tree. We don't make any
1720 * guarantees about ordering, so we're okay with an inorder search
1721 * using whatever order the OS happens to hand back to us.
1723 count
= slurpFullTree(bundle
, assetRoot
, AaptGroupEntry(), String8());
1725 /* failure; report error and remove archive */
1729 totalCount
+= count
;
1731 if (bundle
->getVerbose())
1732 printf("Found %d asset file%s in %s\n",
1733 count
, (count
==1) ? "" : "s", assetDir
);
1737 if (count
!= NO_ERROR
) {
1747 ssize_t
AaptAssets::slurpFullTree(Bundle
* bundle
, const String8
& srcDir
,
1748 const AaptGroupEntry
& kind
,
1749 const String8
& resType
)
1751 ssize_t res
= AaptDir::slurpFullTree(bundle
, srcDir
, kind
, resType
);
1753 mGroupEntries
.add(kind
);
1759 ssize_t
AaptAssets::slurpResourceTree(Bundle
* bundle
, const String8
& srcDir
)
1763 DIR* dir
= opendir(srcDir
.string());
1765 fprintf(stderr
, "ERROR: opendir(%s): %s\n", srcDir
.string(), strerror(errno
));
1766 return UNKNOWN_ERROR
;
1772 * Run through the directory, looking for dirs that match the
1776 struct dirent
* entry
= readdir(dir
);
1777 if (entry
== NULL
) {
1781 if (isHidden(srcDir
.string(), entry
->d_name
)) {
1785 String8
subdirName(srcDir
);
1786 subdirName
.appendPath(entry
->d_name
);
1788 AaptGroupEntry group
;
1790 bool b
= group
.initFromDirName(entry
->d_name
, &resType
);
1792 fprintf(stderr
, "invalid resource directory name: %s/%s\n", srcDir
.string(),
1798 FileType type
= getFileType(subdirName
.string());
1800 if (type
== kFileTypeDirectory
) {
1801 sp
<AaptDir
> dir
= makeDir(String8(entry
->d_name
));
1802 ssize_t res
= dir
->slurpFullTree(bundle
, subdirName
, group
,
1809 mGroupEntries
.add(group
);
1815 if (bundle
->getVerbose()) {
1816 fprintf(stderr
, " (ignoring file '%s')\n", subdirName
.string());
1832 AaptAssets::slurpResourceZip(Bundle
* bundle
, const char* filename
)
1835 SortedVector
<AaptGroupEntry
> entries
;
1837 ZipFile
* zip
= new ZipFile
;
1838 status_t err
= zip
->open(filename
, ZipFile::kOpenReadOnly
);
1839 if (err
!= NO_ERROR
) {
1840 fprintf(stderr
, "error opening zip file %s\n", filename
);
1846 const int N
= zip
->getNumEntries();
1847 for (int i
=0; i
<N
; i
++) {
1848 ZipEntry
* entry
= zip
->getEntryByIndex(i
);
1849 if (entry
->getDeleted()) {
1853 String8
entryName(entry
->getFileName());
1855 String8 dirName
= entryName
.getPathDir();
1856 sp
<AaptDir
> dir
= dirName
== "" ? this : makeDir(dirName
);
1859 AaptGroupEntry kind
;
1862 if (entryName
.walkPath(&remain
) == kResourceDir
) {
1863 // these are the resources, pull their type out of the directory name
1864 kind
.initFromDirName(remain
.walkPath().string(), &resType
);
1866 // these are untyped and don't have an AaptGroupEntry
1868 if (entries
.indexOf(kind
) < 0) {
1870 mGroupEntries
.add(kind
);
1873 // use the one from the zip file if they both exist.
1874 dir
->removeFile(entryName
.getPathLeaf());
1876 sp
<AaptFile
> file
= new AaptFile(entryName
, kind
, resType
);
1877 status_t err
= dir
->addLeafFile(entryName
.getPathLeaf(), file
);
1878 if (err
!= NO_ERROR
) {
1879 fprintf(stderr
, "err=%s entryName=%s\n", strerror(err
), entryName
.string());
1883 file
->setCompressionMethod(entry
->getCompressionMethod());
1886 if (entryName
== "AndroidManifest.xml") {
1887 printf("AndroidManifest.xml\n");
1889 printf("\n\nfile: %s\n", entryName
.string());
1892 size_t len
= entry
->getUncompressedLen();
1893 void* data
= zip
->uncompress(entry
);
1894 void* buf
= file
->editData(len
);
1895 memcpy(buf
, data
, len
);
1899 const unsigned char* p
= (unsigned char*)data
;
1900 const unsigned char* end
= p
+len
;
1902 for (int i
=0; i
<32 && p
< end
; i
++) {
1903 printf("0x%03x ", i
*0x10 + OFF
);
1904 for (int j
=0; j
<0x10 && p
< end
; j
++) {
1905 printf(" %02x", *p
);
1922 sp
<AaptSymbols
> AaptAssets::getSymbolsFor(const String8
& name
)
1924 sp
<AaptSymbols
> sym
= mSymbols
.valueFor(name
);
1926 sym
= new AaptSymbols();
1927 mSymbols
.add(name
, sym
);
1932 status_t
AaptAssets::buildIncludedResources(Bundle
* bundle
)
1934 if (!mHaveIncludedAssets
) {
1935 // Add in all includes.
1936 const Vector
<const char*>& incl
= bundle
->getPackageIncludes();
1937 const size_t N
=incl
.size();
1938 for (size_t i
=0; i
<N
; i
++) {
1939 if (bundle
->getVerbose())
1940 printf("Including resources from package: %s\n", incl
[i
]);
1941 if (!mIncludedAssets
.addAssetPath(String8(incl
[i
]), NULL
)) {
1942 fprintf(stderr
, "ERROR: Asset package include '%s' not found.\n",
1944 return UNKNOWN_ERROR
;
1947 mHaveIncludedAssets
= true;
1953 status_t
AaptAssets::addIncludedResources(const sp
<AaptFile
>& file
)
1955 const ResTable
& res
= getIncludedResources();
1957 return const_cast<ResTable
&>(res
).add(file
->getData(), file
->getSize(), NULL
);
1960 const ResTable
& AaptAssets::getIncludedResources() const
1962 return mIncludedAssets
.getResources(false);
1965 void AaptAssets::print() const
1967 printf("Locale/Vendor pairs:\n");
1968 const size_t N
=mGroupEntries
.size();
1969 for (size_t i
=0; i
<N
; i
++) {
1971 mGroupEntries
.itemAt(i
).locale
.string(),
1972 mGroupEntries
.itemAt(i
).vendor
.string());
1975 printf("\nFiles:\n");
1979 sp
<AaptDir
> AaptAssets::resDir(const String8
& name
)
1981 const Vector
<sp
<AaptDir
> >& dirs
= mDirs
;
1982 const size_t N
= dirs
.size();
1983 for (size_t i
=0; i
<N
; i
++) {
1984 const sp
<AaptDir
>& d
= dirs
.itemAt(i
);
1985 if (d
->getLeaf() == name
) {
1993 valid_symbol_name(const String8
& symbol
)
1995 static char const * const KEYWORDS
[] = {
1996 "abstract", "assert", "boolean", "break",
1997 "byte", "case", "catch", "char", "class", "const", "continue",
1998 "default", "do", "double", "else", "enum", "extends", "final",
1999 "finally", "float", "for", "goto", "if", "implements", "import",
2000 "instanceof", "int", "interface", "long", "native", "new", "package",
2001 "private", "protected", "public", "return", "short", "static",
2002 "strictfp", "super", "switch", "synchronized", "this", "throw",
2003 "throws", "transient", "try", "void", "volatile", "while",
2004 "true", "false", "null",
2007 const char*const* k
= KEYWORDS
;
2008 const char*const s
= symbol
.string();
2010 if (0 == strcmp(s
, *k
)) {