auto import from //depot/cupcake/@135843
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
new file mode 100644
index 0000000..6bc1ee6
--- /dev/null
+++ b/tools/aapt/AaptAssets.cpp
@@ -0,0 +1,1717 @@
+//
+// Copyright 2006 The Android Open Source Project
+//
+
+#include "AaptAssets.h"
+#include "Main.h"
+
+#include <utils/misc.h>
+#include <utils/SortedVector.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+
+static const char* kDefaultLocale = "default";
+static const char* kWildcardName = "any";
+static const char* kAssetDir = "assets";
+static const char* kResourceDir = "res";
+static const char* kInvalidChars = "/\\:";
+static const size_t kMaxAssetFileName = 100;
+
+static const String8 kResString(kResourceDir);
+
+/*
+ * Names of asset files must meet the following criteria:
+ *
+ * - the filename length must be less than kMaxAssetFileName bytes long
+ * (and can't be empty)
+ * - all characters must be 7-bit printable ASCII
+ * - none of { '/' '\\' ':' }
+ *
+ * Pass in just the filename, not the full path.
+ */
+static bool validateFileName(const char* fileName)
+{
+ const char* cp = fileName;
+ size_t len = 0;
+
+ while (*cp != '\0') {
+ if ((*cp & 0x80) != 0)
+ return false; // reject high ASCII
+ if (*cp < 0x20 || *cp >= 0x7f)
+ return false; // reject control chars and 0x7f
+ if (strchr(kInvalidChars, *cp) != NULL)
+ return false; // reject path sep chars
+ cp++;
+ len++;
+ }
+
+ if (len < 1 || len > kMaxAssetFileName)
+ return false; // reject empty or too long
+
+ return true;
+}
+
+static bool isHidden(const char *root, const char *path)
+{
+ const char *type = NULL;
+
+ // Skip all hidden files.
+ if (path[0] == '.') {
+ // Skip ., .. and .svn but don't chatter about it.
+ if (strcmp(path, ".") == 0
+ || strcmp(path, "..") == 0
+ || strcmp(path, ".svn") == 0) {
+ return true;
+ }
+ type = "hidden";
+ } else if (path[0] == '_') {
+ // skip directories starting with _ (don't chatter about it)
+ String8 subdirName(root);
+ subdirName.appendPath(path);
+ if (getFileType(subdirName.string()) == kFileTypeDirectory) {
+ return true;
+ }
+ } else if (strcmp(path, "CVS") == 0) {
+ // Skip CVS but don't chatter about it.
+ return true;
+ } else if (strcasecmp(path, "thumbs.db") == 0
+ || strcasecmp(path, "picasa.ini") == 0) {
+ // Skip suspected image indexes files.
+ type = "index";
+ } else if (path[strlen(path)-1] == '~') {
+ // Skip suspected emacs backup files.
+ type = "backup";
+ } else {
+ // Let everything else through.
+ return false;
+ }
+
+ /* If we get this far, "type" should be set and the file
+ * should be skipped.
+ */
+ String8 subdirName(root);
+ subdirName.appendPath(path);
+ fprintf(stderr, " (skipping %s %s '%s')\n", type,
+ getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
+ subdirName.string());
+
+ return true;
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+status_t
+AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
+{
+ ResTable_config config;
+
+ // IMSI - MCC
+ if (getMccName(part.string(), &config)) {
+ *axis = AXIS_MCC;
+ *value = config.mcc;
+ return 0;
+ }
+
+ // IMSI - MNC
+ if (getMncName(part.string(), &config)) {
+ *axis = AXIS_MNC;
+ *value = config.mnc;
+ return 0;
+ }
+
+ // locale - language
+ if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
+ *axis = AXIS_LANGUAGE;
+ *value = part[1] << 8 | part[0];
+ return 0;
+ }
+
+ // locale - language_REGION
+ if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
+ && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
+ *axis = AXIS_LANGUAGE;
+ *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
+ return 0;
+ }
+
+ // orientation
+ if (getOrientationName(part.string(), &config)) {
+ *axis = AXIS_ORIENTATION;
+ *value = config.orientation;
+ return 0;
+ }
+
+ // density
+ if (getDensityName(part.string(), &config)) {
+ *axis = AXIS_DENSITY;
+ *value = config.density;
+ return 0;
+ }
+
+ // touchscreen
+ if (getTouchscreenName(part.string(), &config)) {
+ *axis = AXIS_TOUCHSCREEN;
+ *value = config.touchscreen;
+ return 0;
+ }
+
+ // keyboard hidden
+ if (getKeysHiddenName(part.string(), &config)) {
+ *axis = AXIS_KEYSHIDDEN;
+ *value = config.inputFlags;
+ return 0;
+ }
+
+ // keyboard
+ if (getKeyboardName(part.string(), &config)) {
+ *axis = AXIS_KEYBOARD;
+ *value = config.keyboard;
+ return 0;
+ }
+
+ // navigation
+ if (getNavigationName(part.string(), &config)) {
+ *axis = AXIS_NAVIGATION;
+ *value = config.navigation;
+ return 0;
+ }
+
+ // screen size
+ if (getScreenSizeName(part.string(), &config)) {
+ *axis = AXIS_SCREENSIZE;
+ *value = config.screenSize;
+ return 0;
+ }
+
+ // version
+ if (getVersionName(part.string(), &config)) {
+ *axis = AXIS_VERSION;
+ *value = config.version;
+ return 0;
+ }
+
+ return 1;
+}
+
+bool
+AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
+{
+ Vector<String8> parts;
+
+ String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, vers;
+
+ const char *p = dir;
+ const char *q;
+ while (NULL != (q = strchr(p, '-'))) {
+ String8 val(p, q-p);
+ val.toLower();
+ parts.add(val);
+ //printf("part: %s\n", parts[parts.size()-1].string());
+ p = q+1;
+ }
+ String8 val(p);
+ val.toLower();
+ parts.add(val);
+ //printf("part: %s\n", parts[parts.size()-1].string());
+
+ const int N = parts.size();
+ int index = 0;
+ String8 part = parts[index];
+
+ // resource type
+ if (!isValidResourceType(part)) {
+ return false;
+ }
+ *resType = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+
+ // imsi - mcc
+ if (getMccName(part.string())) {
+ mcc = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not mcc: %s\n", part.string());
+ }
+
+ // imsi - mnc
+ if (getMncName(part.string())) {
+ mnc = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not mcc: %s\n", part.string());
+ }
+
+ // locale - language
+ if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
+ loc = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not language: %s\n", part.string());
+ }
+
+ // locale - region
+ if (loc.length() > 0
+ && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
+ loc += "-";
+ part.toUpper();
+ loc += part.string() + 1;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not region: %s\n", part.string());
+ }
+
+ // orientation
+ if (getOrientationName(part.string())) {
+ orient = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not orientation: %s\n", part.string());
+ }
+
+ // density
+ if (getDensityName(part.string())) {
+ den = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not density: %s\n", part.string());
+ }
+
+ // touchscreen
+ if (getTouchscreenName(part.string())) {
+ touch = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not touchscreen: %s\n", part.string());
+ }
+
+ // keyboard hidden
+ if (getKeysHiddenName(part.string())) {
+ keysHidden = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not keysHidden: %s\n", part.string());
+ }
+
+ // keyboard
+ if (getKeyboardName(part.string())) {
+ key = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not keyboard: %s\n", part.string());
+ }
+
+ if (getNavigationName(part.string())) {
+ nav = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not navigation: %s\n", part.string());
+ }
+
+ if (getScreenSizeName(part.string())) {
+ size = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not screen size: %s\n", part.string());
+ }
+
+ if (getVersionName(part.string())) {
+ vers = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not version: %s\n", part.string());
+ }
+
+ // if there are extra parts, it doesn't match
+ return false;
+
+success:
+ this->mcc = mcc;
+ this->mnc = mnc;
+ this->locale = loc;
+ this->orientation = orient;
+ this->density = den;
+ this->touchscreen = touch;
+ this->keysHidden = keysHidden;
+ this->keyboard = key;
+ this->navigation = nav;
+ this->screenSize = size;
+ this->version = vers;
+
+ // what is this anyway?
+ this->vendor = "";
+
+ return true;
+}
+
+String8
+AaptGroupEntry::toString() const
+{
+ String8 s = this->mcc;
+ s += ",";
+ s += this->mnc;
+ s += ",";
+ s += this->locale;
+ s += ",";
+ s += this->orientation;
+ s += ",";
+ s += density;
+ s += ",";
+ s += touchscreen;
+ s += ",";
+ s += keysHidden;
+ s += ",";
+ s += keyboard;
+ s += ",";
+ s += navigation;
+ s += ",";
+ s += screenSize;
+ s += ",";
+ s += version;
+ return s;
+}
+
+String8
+AaptGroupEntry::toDirName(const String8& resType) const
+{
+ String8 s = resType;
+ if (this->mcc != "") {
+ s += "-";
+ s += mcc;
+ }
+ if (this->mnc != "") {
+ s += "-";
+ s += mnc;
+ }
+ if (this->locale != "") {
+ s += "-";
+ s += locale;
+ }
+ if (this->orientation != "") {
+ s += "-";
+ s += orientation;
+ }
+ if (this->density != "") {
+ s += "-";
+ s += density;
+ }
+ if (this->touchscreen != "") {
+ s += "-";
+ s += touchscreen;
+ }
+ if (this->keysHidden != "") {
+ s += "-";
+ s += keysHidden;
+ }
+ if (this->keyboard != "") {
+ s += "-";
+ s += keyboard;
+ }
+ if (this->navigation != "") {
+ s += "-";
+ s += navigation;
+ }
+ if (this->screenSize != "") {
+ s += "-";
+ s += screenSize;
+ }
+ if (this->version != "") {
+ s += "-";
+ s += version;
+ }
+
+ return s;
+}
+
+bool AaptGroupEntry::getMccName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c-val != 3) return false;
+
+ int d = atoi(val);
+ if (d != 0) {
+ if (out) out->mcc = d;
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getMncName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'n') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c-val == 0 || c-val > 3) return false;
+
+ int d = atoi(val);
+ if (d != 0) {
+ if (out) out->mnc = d;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Does this directory name fit the pattern of a locale dir ("en-rUS" or
+ * "default")?
+ *
+ * TODO: Should insist that the first two letters are lower case, and the
+ * second two are upper.
+ */
+bool AaptGroupEntry::getLocaleName(const char* fileName,
+ ResTable_config* out)
+{
+ if (strcmp(fileName, kWildcardName) == 0
+ || strcmp(fileName, kDefaultLocale) == 0) {
+ if (out) {
+ out->language[0] = 0;
+ out->language[1] = 0;
+ out->country[0] = 0;
+ out->country[1] = 0;
+ }
+ return true;
+ }
+
+ if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
+ if (out) {
+ out->language[0] = fileName[0];
+ out->language[1] = fileName[1];
+ out->country[0] = 0;
+ out->country[1] = 0;
+ }
+ return true;
+ }
+
+ if (strlen(fileName) == 5 &&
+ isalpha(fileName[0]) &&
+ isalpha(fileName[1]) &&
+ fileName[2] == '-' &&
+ isalpha(fileName[3]) &&
+ isalpha(fileName[4])) {
+ if (out) {
+ out->language[0] = fileName[0];
+ out->language[1] = fileName[1];
+ out->country[0] = fileName[3];
+ out->country[1] = fileName[4];
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getOrientationName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->orientation = out->ORIENTATION_ANY;
+ return true;
+ } else if (strcmp(name, "port") == 0) {
+ if (out) out->orientation = out->ORIENTATION_PORT;
+ return true;
+ } else if (strcmp(name, "land") == 0) {
+ if (out) out->orientation = out->ORIENTATION_LAND;
+ return true;
+ } else if (strcmp(name, "square") == 0) {
+ if (out) out->orientation = out->ORIENTATION_SQUARE;
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getDensityName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->density = 0;
+ return true;
+ }
+ char* c = (char*)name;
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+
+ // check that we have 'dpi' after the last digit.
+ if (toupper(c[0]) != 'D' ||
+ toupper(c[1]) != 'P' ||
+ toupper(c[2]) != 'I' ||
+ c[3] != 0) {
+ return false;
+ }
+
+ // temporarily replace the first letter with \0 to
+ // use atoi.
+ char tmp = c[0];
+ c[0] = '\0';
+
+ int d = atoi(name);
+ c[0] = tmp;
+
+ if (d != 0) {
+ if (out) out->density = d;
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getTouchscreenName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+ return true;
+ } else if (strcmp(name, "notouch") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+ return true;
+ } else if (strcmp(name, "stylus") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+ return true;
+ } else if (strcmp(name, "finger") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getKeysHiddenName(const char* name,
+ ResTable_config* out)
+{
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = out->MASK_KEYSHIDDEN;
+ value = out->KEYSHIDDEN_ANY;
+ } else if (strcmp(name, "keysexposed") == 0) {
+ mask = out->MASK_KEYSHIDDEN;
+ value = out->KEYSHIDDEN_NO;
+ } else if (strcmp(name, "keyshidden") == 0) {
+ mask = out->MASK_KEYSHIDDEN;
+ value = out->KEYSHIDDEN_YES;
+ } else if (strcmp(name, "keyssoft") == 0) {
+ mask = out->MASK_KEYSHIDDEN;
+ value = out->KEYSHIDDEN_SOFT;
+ }
+
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getKeyboardName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->keyboard = out->KEYBOARD_ANY;
+ return true;
+ } else if (strcmp(name, "nokeys") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+ return true;
+ } else if (strcmp(name, "qwerty") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_QWERTY;
+ return true;
+ } else if (strcmp(name, "12key") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_12KEY;
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getNavigationName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->navigation = out->NAVIGATION_ANY;
+ return true;
+ } else if (strcmp(name, "nonav") == 0) {
+ if (out) out->navigation = out->NAVIGATION_NONAV;
+ return true;
+ } else if (strcmp(name, "dpad") == 0) {
+ if (out) out->navigation = out->NAVIGATION_DPAD;
+ return true;
+ } else if (strcmp(name, "trackball") == 0) {
+ if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+ return true;
+ } else if (strcmp(name, "wheel") == 0) {
+ if (out) out->navigation = out->NAVIGATION_WHEEL;
+ return true;
+ }
+
+ return false;
+}
+
+bool AaptGroupEntry::getScreenSizeName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenWidth = out->SCREENWIDTH_ANY;
+ out->screenHeight = out->SCREENHEIGHT_ANY;
+ }
+ return true;
+ }
+
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || *x != 'x') return false;
+ String8 xName(name, x-name);
+ x++;
+
+ const char* y = x;
+ while (*y >= '0' && *y <= '9') y++;
+ if (y == name || *y != 0) return false;
+ String8 yName(x, y-x);
+
+ uint16_t w = (uint16_t)atoi(xName.string());
+ uint16_t h = (uint16_t)atoi(yName.string());
+ if (w < h) {
+ return false;
+ }
+
+ if (out) {
+ out->screenWidth = w;
+ out->screenHeight = h;
+ }
+
+ return true;
+}
+
+bool AaptGroupEntry::getVersionName(const char* name,
+ ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->sdkVersion = out->SDKVERSION_ANY;
+ out->minorVersion = out->MINORVERSION_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'v') {
+ return false;
+ }
+
+ name++;
+ const char* s = name;
+ while (*s >= '0' && *s <= '9') s++;
+ if (s == name || *s != 0) return false;
+ String8 sdkName(name, s-name);
+
+ if (out) {
+ out->sdkVersion = (uint16_t)atoi(sdkName.string());
+ out->minorVersion = 0;
+ }
+
+ return true;
+}
+
+int AaptGroupEntry::compare(const AaptGroupEntry& o) const
+{
+ int v = mcc.compare(o.mcc);
+ if (v == 0) v = mnc.compare(o.mnc);
+ if (v == 0) v = locale.compare(o.locale);
+ if (v == 0) v = vendor.compare(o.vendor);
+ if (v == 0) v = orientation.compare(o.orientation);
+ if (v == 0) v = density.compare(o.density);
+ if (v == 0) v = touchscreen.compare(o.touchscreen);
+ if (v == 0) v = keysHidden.compare(o.keysHidden);
+ if (v == 0) v = keyboard.compare(o.keyboard);
+ if (v == 0) v = navigation.compare(o.navigation);
+ if (v == 0) v = screenSize.compare(o.screenSize);
+ if (v == 0) v = version.compare(o.version);
+ return v;
+}
+
+ResTable_config AaptGroupEntry::toParams() const
+{
+ ResTable_config params;
+ memset(¶ms, 0, sizeof(params));
+ getMccName(mcc.string(), ¶ms);
+ getMncName(mnc.string(), ¶ms);
+ getLocaleName(locale.string(), ¶ms);
+ getOrientationName(orientation.string(), ¶ms);
+ getDensityName(density.string(), ¶ms);
+ getTouchscreenName(touchscreen.string(), ¶ms);
+ getKeysHiddenName(keysHidden.string(), ¶ms);
+ getKeyboardName(keyboard.string(), ¶ms);
+ getNavigationName(navigation.string(), ¶ms);
+ getScreenSizeName(screenSize.string(), ¶ms);
+ getVersionName(version.string(), ¶ms);
+ return params;
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+void* AaptFile::editData(size_t size)
+{
+ if (size <= mBufferSize) {
+ mDataSize = size;
+ return mData;
+ }
+ size_t allocSize = (size*3)/2;
+ void* buf = realloc(mData, allocSize);
+ if (buf == NULL) {
+ return NULL;
+ }
+ mData = buf;
+ mDataSize = size;
+ mBufferSize = allocSize;
+ return buf;
+}
+
+void* AaptFile::editData(size_t* outSize)
+{
+ if (outSize) {
+ *outSize = mDataSize;
+ }
+ return mData;
+}
+
+void* AaptFile::padData(size_t wordSize)
+{
+ const size_t extra = mDataSize%wordSize;
+ if (extra == 0) {
+ return mData;
+ }
+
+ size_t initial = mDataSize;
+ void* data = editData(initial+(wordSize-extra));
+ if (data != NULL) {
+ memset(((uint8_t*)data) + initial, 0, wordSize-extra);
+ }
+ return data;
+}
+
+status_t AaptFile::writeData(const void* data, size_t size)
+{
+ size_t end = mDataSize;
+ size_t total = size + end;
+ void* buf = editData(total);
+ if (buf == NULL) {
+ return UNKNOWN_ERROR;
+ }
+ memcpy(((char*)buf)+end, data, size);
+ return NO_ERROR;
+}
+
+void AaptFile::clearData()
+{
+ if (mData != NULL) free(mData);
+ mData = NULL;
+ mDataSize = 0;
+ mBufferSize = 0;
+}
+
+String8 AaptFile::getPrintableSource() const
+{
+ if (hasData()) {
+ String8 name(mGroupEntry.locale.string());
+ name.appendPath(mGroupEntry.vendor.string());
+ name.appendPath(mPath);
+ name.append(" #generated");
+ return name;
+ }
+ return mSourceFile;
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+status_t AaptGroup::addFile(const sp<AaptFile>& file)
+{
+ if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
+ file->mPath = mPath;
+ mFiles.add(file->getGroupEntry(), file);
+ return NO_ERROR;
+ }
+
+ SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
+ getPrintableSource().string());
+ return UNKNOWN_ERROR;
+}
+
+void AaptGroup::removeFile(size_t index)
+{
+ mFiles.removeItemsAt(index);
+}
+
+void AaptGroup::print() const
+{
+ printf(" %s\n", getPath().string());
+ const size_t N=mFiles.size();
+ size_t i;
+ for (i=0; i<N; i++) {
+ sp<AaptFile> file = mFiles.valueAt(i);
+ const AaptGroupEntry& e = file->getGroupEntry();
+ if (file->hasData()) {
+ printf(" Gen: (%s) %d bytes\n", e.toString().string(),
+ (int)file->getSize());
+ } else {
+ printf(" Src: %s\n", file->getPrintableSource().string());
+ }
+ }
+}
+
+String8 AaptGroup::getPrintableSource() const
+{
+ if (mFiles.size() > 0) {
+ // Arbitrarily pull the first source file out of the list.
+ return mFiles.valueAt(0)->getPrintableSource();
+ }
+
+ // Should never hit this case, but to be safe...
+ return getPath();
+
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
+{
+ if (mFiles.indexOfKey(name) >= 0) {
+ return ALREADY_EXISTS;
+ }
+ mFiles.add(name, file);
+ return NO_ERROR;
+}
+
+status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
+{
+ if (mDirs.indexOfKey(name) >= 0) {
+ return ALREADY_EXISTS;
+ }
+ mDirs.add(name, dir);
+ return NO_ERROR;
+}
+
+sp<AaptDir> AaptDir::makeDir(const String8& path)
+{
+ String8 name;
+ String8 remain = path;
+
+ sp<AaptDir> subdir = this;
+ while (name = remain.walkPath(&remain), remain != "") {
+ subdir = subdir->makeDir(name);
+ }
+
+ ssize_t i = subdir->mDirs.indexOfKey(name);
+ if (i >= 0) {
+ return subdir->mDirs.valueAt(i);
+ }
+ sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
+ subdir->mDirs.add(name, dir);
+ return dir;
+}
+
+void AaptDir::removeFile(const String8& name)
+{
+ mFiles.removeItem(name);
+}
+
+void AaptDir::removeDir(const String8& name)
+{
+ mDirs.removeItem(name);
+}
+
+status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
+{
+ sp<AaptGroup> origGroup;
+
+ // Find and remove the given file with shear, brute force!
+ const size_t NG = mFiles.size();
+ size_t i;
+ for (i=0; origGroup == NULL && i<NG; i++) {
+ sp<AaptGroup> g = mFiles.valueAt(i);
+ const size_t NF = g->getFiles().size();
+ for (size_t j=0; j<NF; j++) {
+ if (g->getFiles().valueAt(j) == file) {
+ origGroup = g;
+ g->removeFile(j);
+ if (NF == 1) {
+ mFiles.removeItemsAt(i);
+ }
+ break;
+ }
+ }
+ }
+
+ //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
+
+ // Place the file under its new name.
+ if (origGroup != NULL) {
+ return addLeafFile(newName, file);
+ }
+
+ return NO_ERROR;
+}
+
+status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
+{
+ sp<AaptGroup> group;
+ if (mFiles.indexOfKey(leafName) >= 0) {
+ group = mFiles.valueFor(leafName);
+ } else {
+ group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
+ mFiles.add(leafName, group);
+ }
+
+ return group->addFile(file);
+}
+
+ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
+ const AaptGroupEntry& kind, const String8& resType)
+{
+ Vector<String8> fileNames;
+
+ {
+ DIR* dir = NULL;
+
+ dir = opendir(srcDir.string());
+ if (dir == NULL) {
+ fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+
+ /*
+ * Slurp the filenames out of the directory.
+ */
+ while (1) {
+ struct dirent* entry;
+
+ entry = readdir(dir);
+ if (entry == NULL)
+ break;
+
+ if (isHidden(srcDir.string(), entry->d_name))
+ continue;
+
+ fileNames.add(String8(entry->d_name));
+ }
+
+ closedir(dir);
+ }
+
+ ssize_t count = 0;
+
+ /*
+ * Stash away the files and recursively descend into subdirectories.
+ */
+ const size_t N = fileNames.size();
+ size_t i;
+ for (i = 0; i < N; i++) {
+ String8 pathName(srcDir);
+ FileType type;
+
+ pathName.appendPath(fileNames[i].string());
+ type = getFileType(pathName.string());
+ if (type == kFileTypeDirectory) {
+ sp<AaptDir> subdir;
+ bool notAdded = false;
+ if (mDirs.indexOfKey(fileNames[i]) >= 0) {
+ subdir = mDirs.valueFor(fileNames[i]);
+ } else {
+ subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
+ notAdded = true;
+ }
+ ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
+ resType);
+ if (res < NO_ERROR) {
+ return res;
+ }
+ if (res > 0 && notAdded) {
+ mDirs.add(fileNames[i], subdir);
+ }
+ count += res;
+ } else if (type == kFileTypeRegular) {
+ sp<AaptFile> file = new AaptFile(pathName, kind, resType);
+ status_t err = addLeafFile(fileNames[i], file);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ count++;
+
+ } else {
+ if (bundle->getVerbose())
+ printf(" (ignoring non-file/dir '%s')\n", pathName.string());
+ }
+ }
+
+ return count;
+}
+
+status_t AaptDir::validate() const
+{
+ const size_t NF = mFiles.size();
+ const size_t ND = mDirs.size();
+ size_t i;
+ for (i = 0; i < NF; i++) {
+ if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
+ SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
+ "Invalid filename. Unable to add.");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t j;
+ for (j = i+1; j < NF; j++) {
+ if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
+ mFiles.valueAt(j)->getLeaf().string()) == 0) {
+ SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
+ "File is case-insensitive equivalent to: %s",
+ mFiles.valueAt(j)->getPrintableSource().string());
+ return UNKNOWN_ERROR;
+ }
+
+ // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
+ // (this is mostly caught by the "marked" stuff, below)
+ }
+
+ for (j = 0; j < ND; j++) {
+ if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
+ mDirs.valueAt(j)->getLeaf().string()) == 0) {
+ SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
+ "File conflicts with dir from: %s",
+ mDirs.valueAt(j)->getPrintableSource().string());
+ return UNKNOWN_ERROR;
+ }
+ }
+ }
+
+ for (i = 0; i < ND; i++) {
+ if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
+ SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
+ "Invalid directory name, unable to add.");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t j;
+ for (j = i+1; j < ND; j++) {
+ if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
+ mDirs.valueAt(j)->getLeaf().string()) == 0) {
+ SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
+ "Directory is case-insensitive equivalent to: %s",
+ mDirs.valueAt(j)->getPrintableSource().string());
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ status_t err = mDirs.valueAt(i)->validate();
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void AaptDir::print() const
+{
+ const size_t ND=getDirs().size();
+ size_t i;
+ for (i=0; i<ND; i++) {
+ getDirs().valueAt(i)->print();
+ }
+
+ const size_t NF=getFiles().size();
+ for (i=0; i<NF; i++) {
+ getFiles().valueAt(i)->print();
+ }
+}
+
+String8 AaptDir::getPrintableSource() const
+{
+ if (mFiles.size() > 0) {
+ // Arbitrarily pull the first file out of the list as the source dir.
+ return mFiles.valueAt(0)->getPrintableSource().getPathDir();
+ }
+ if (mDirs.size() > 0) {
+ // Or arbitrarily pull the first dir out of the list as the source dir.
+ return mDirs.valueAt(0)->getPrintableSource().getPathDir();
+ }
+
+ // Should never hit this case, but to be safe...
+ return mPath;
+
+}
+
+// =========================================================================
+// =========================================================================
+// =========================================================================
+
+sp<AaptFile> AaptAssets::addFile(
+ const String8& filePath, const AaptGroupEntry& entry,
+ const String8& srcDir, sp<AaptGroup>* outGroup,
+ const String8& resType)
+{
+ sp<AaptDir> dir = this;
+ sp<AaptGroup> group;
+ sp<AaptFile> file;
+ String8 root, remain(filePath), partialPath;
+ while (remain.length() > 0) {
+ root = remain.walkPath(&remain);
+ partialPath.appendPath(root);
+
+ const String8 rootStr(root);
+
+ if (remain.length() == 0) {
+ ssize_t i = dir->getFiles().indexOfKey(rootStr);
+ if (i >= 0) {
+ group = dir->getFiles().valueAt(i);
+ } else {
+ group = new AaptGroup(rootStr, filePath);
+ status_t res = dir->addFile(rootStr, group);
+ if (res != NO_ERROR) {
+ return NULL;
+ }
+ }
+ file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
+ status_t res = group->addFile(file);
+ if (res != NO_ERROR) {
+ return NULL;
+ }
+ break;
+
+ } else {
+ ssize_t i = dir->getDirs().indexOfKey(rootStr);
+ if (i >= 0) {
+ dir = dir->getDirs().valueAt(i);
+ } else {
+ sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
+ status_t res = dir->addDir(rootStr, subdir);
+ if (res != NO_ERROR) {
+ return NULL;
+ }
+ dir = subdir;
+ }
+ }
+ }
+
+ mGroupEntries.add(entry);
+ if (outGroup) *outGroup = group;
+ return file;
+}
+
+void AaptAssets::addResource(const String8& leafName, const String8& path,
+ const sp<AaptFile>& file, const String8& resType)
+{
+ sp<AaptDir> res = AaptDir::makeDir(kResString);
+ String8 dirname = file->getGroupEntry().toDirName(resType);
+ sp<AaptDir> subdir = res->makeDir(dirname);
+ sp<AaptGroup> grr = new AaptGroup(leafName, path);
+ grr->addFile(file);
+
+ subdir->addFile(leafName, grr);
+}
+
+
+ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
+{
+ int count;
+ int totalCount = 0;
+ FileType type;
+ const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
+ const size_t dirCount =resDirs.size();
+ sp<AaptAssets> current = this;
+
+ const int N = bundle->getFileSpecCount();
+
+ /*
+ * If a package manifest was specified, include that first.
+ */
+ if (bundle->getAndroidManifestFile() != NULL) {
+ // place at root of zip.
+ String8 srcFile(bundle->getAndroidManifestFile());
+ addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
+ NULL, String8());
+ totalCount++;
+ }
+
+ /*
+ * If a directory of custom assets was supplied, slurp 'em up.
+ */
+ if (bundle->getAssetSourceDir()) {
+ const char* assetDir = bundle->getAssetSourceDir();
+
+ FileType type = getFileType(assetDir);
+ if (type == kFileTypeNonexistent) {
+ fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
+ return UNKNOWN_ERROR;
+ }
+ if (type != kFileTypeDirectory) {
+ fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
+ return UNKNOWN_ERROR;
+ }
+
+ String8 assetRoot(assetDir);
+ sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
+ AaptGroupEntry group;
+ count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
+ String8());
+ if (count < 0) {
+ totalCount = count;
+ goto bail;
+ }
+ if (count > 0) {
+ mGroupEntries.add(group);
+ }
+ totalCount += count;
+
+ if (bundle->getVerbose())
+ printf("Found %d custom asset file%s in %s\n",
+ count, (count==1) ? "" : "s", assetDir);
+ }
+
+ /*
+ * If a directory of resource-specific assets was supplied, slurp 'em up.
+ */
+ for (size_t i=0; i<dirCount; i++) {
+ const char *res = resDirs[i];
+ if (res) {
+ type = getFileType(res);
+ if (type == kFileTypeNonexistent) {
+ fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
+ return UNKNOWN_ERROR;
+ }
+ if (type == kFileTypeDirectory) {
+ if (i>0) {
+ sp<AaptAssets> nextOverlay = new AaptAssets();
+ current->setOverlay(nextOverlay);
+ current = nextOverlay;
+ }
+ count = current->slurpResourceTree(bundle, String8(res));
+
+ if (count < 0) {
+ totalCount = count;
+ goto bail;
+ }
+ totalCount += count;
+ }
+ else {
+ fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ }
+ /*
+ * Now do any additional raw files.
+ */
+ for (int arg=0; arg<N; arg++) {
+ const char* assetDir = bundle->getFileSpecEntry(arg);
+
+ FileType type = getFileType(assetDir);
+ if (type == kFileTypeNonexistent) {
+ fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
+ return UNKNOWN_ERROR;
+ }
+ if (type != kFileTypeDirectory) {
+ fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
+ return UNKNOWN_ERROR;
+ }
+
+ String8 assetRoot(assetDir);
+
+ if (bundle->getVerbose())
+ printf("Processing raw dir '%s'\n", (const char*) assetDir);
+
+ /*
+ * Do a recursive traversal of subdir tree. We don't make any
+ * guarantees about ordering, so we're okay with an inorder search
+ * using whatever order the OS happens to hand back to us.
+ */
+ count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
+ if (count < 0) {
+ /* failure; report error and remove archive */
+ totalCount = count;
+ goto bail;
+ }
+ totalCount += count;
+
+ if (bundle->getVerbose())
+ printf("Found %d asset file%s in %s\n",
+ count, (count==1) ? "" : "s", assetDir);
+ }
+
+ count = validate();
+ if (count != NO_ERROR) {
+ totalCount = count;
+ goto bail;
+ }
+
+
+bail:
+ return totalCount;
+}
+
+ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
+ const AaptGroupEntry& kind,
+ const String8& resType)
+{
+ ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
+ if (res > 0) {
+ mGroupEntries.add(kind);
+ }
+
+ return res;
+}
+
+ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
+{
+ ssize_t err = 0;
+
+ DIR* dir = opendir(srcDir.string());
+ if (dir == NULL) {
+ fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+
+ status_t count = 0;
+
+ /*
+ * Run through the directory, looking for dirs that match the
+ * expected pattern.
+ */
+ while (1) {
+ struct dirent* entry = readdir(dir);
+ if (entry == NULL) {
+ break;
+ }
+
+ if (isHidden(srcDir.string(), entry->d_name)) {
+ continue;
+ }
+
+ String8 subdirName(srcDir);
+ subdirName.appendPath(entry->d_name);
+
+ AaptGroupEntry group;
+ String8 resType;
+ bool b = group.initFromDirName(entry->d_name, &resType);
+ if (!b) {
+ fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
+ entry->d_name);
+ err = -1;
+ continue;
+ }
+
+ FileType type = getFileType(subdirName.string());
+
+ if (type == kFileTypeDirectory) {
+ sp<AaptDir> dir = makeDir(String8(entry->d_name));
+ ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
+ resType);
+ if (res < 0) {
+ count = res;
+ goto bail;
+ }
+ if (res > 0) {
+ mGroupEntries.add(group);
+ count += res;
+ }
+
+ mDirs.add(dir);
+ } else {
+ if (bundle->getVerbose()) {
+ fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
+ }
+ }
+ }
+
+bail:
+ closedir(dir);
+ dir = NULL;
+
+ if (err != 0) {
+ return err;
+ }
+ return count;
+}
+
+ssize_t
+AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
+{
+ int count = 0;
+ SortedVector<AaptGroupEntry> entries;
+
+ ZipFile* zip = new ZipFile;
+ status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "error opening zip file %s\n", filename);
+ count = err;
+ delete zip;
+ return -1;
+ }
+
+ const int N = zip->getNumEntries();
+ for (int i=0; i<N; i++) {
+ ZipEntry* entry = zip->getEntryByIndex(i);
+ if (entry->getDeleted()) {
+ continue;
+ }
+
+ String8 entryName(entry->getFileName());
+
+ String8 dirName = entryName.getPathDir();
+ sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
+
+ String8 resType;
+ AaptGroupEntry kind;
+
+ String8 remain;
+ if (entryName.walkPath(&remain) == kResourceDir) {
+ // these are the resources, pull their type out of the directory name
+ kind.initFromDirName(remain.walkPath().string(), &resType);
+ } else {
+ // these are untyped and don't have an AaptGroupEntry
+ }
+ if (entries.indexOf(kind) < 0) {
+ entries.add(kind);
+ mGroupEntries.add(kind);
+ }
+
+ // use the one from the zip file if they both exist.
+ dir->removeFile(entryName.getPathLeaf());
+
+ sp<AaptFile> file = new AaptFile(entryName, kind, resType);
+ status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
+ count = err;
+ goto bail;
+ }
+ file->setCompressionMethod(entry->getCompressionMethod());
+
+#if 0
+ if (entryName == "AndroidManifest.xml") {
+ printf("AndroidManifest.xml\n");
+ }
+ printf("\n\nfile: %s\n", entryName.string());
+#endif
+
+ size_t len = entry->getUncompressedLen();
+ void* data = zip->uncompress(entry);
+ void* buf = file->editData(len);
+ memcpy(buf, data, len);
+
+#if 0
+ const int OFF = 0;
+ const unsigned char* p = (unsigned char*)data;
+ const unsigned char* end = p+len;
+ p += OFF;
+ for (int i=0; i<32 && p < end; i++) {
+ printf("0x%03x ", i*0x10 + OFF);
+ for (int j=0; j<0x10 && p < end; j++) {
+ printf(" %02x", *p);
+ p++;
+ }
+ printf("\n");
+ }
+#endif
+
+ free(data);
+
+ count++;
+ }
+
+bail:
+ delete zip;
+ return count;
+}
+
+sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
+{
+ sp<AaptSymbols> sym = mSymbols.valueFor(name);
+ if (sym == NULL) {
+ sym = new AaptSymbols();
+ mSymbols.add(name, sym);
+ }
+ return sym;
+}
+
+status_t AaptAssets::buildIncludedResources(Bundle* bundle)
+{
+ if (!mHaveIncludedAssets) {
+ // Add in all includes.
+ const Vector<const char*>& incl = bundle->getPackageIncludes();
+ const size_t N=incl.size();
+ for (size_t i=0; i<N; i++) {
+ if (bundle->getVerbose())
+ printf("Including resources from package: %s\n", incl[i]);
+ if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
+ fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
+ incl[i]);
+ return UNKNOWN_ERROR;
+ }
+ }
+ mHaveIncludedAssets = true;
+ }
+
+ return NO_ERROR;
+}
+
+status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
+{
+ const ResTable& res = getIncludedResources();
+ // XXX dirty!
+ return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
+}
+
+const ResTable& AaptAssets::getIncludedResources() const
+{
+ return mIncludedAssets.getResources(false);
+}
+
+void AaptAssets::print() const
+{
+ printf("Locale/Vendor pairs:\n");
+ const size_t N=mGroupEntries.size();
+ for (size_t i=0; i<N; i++) {
+ printf(" %s/%s\n",
+ mGroupEntries.itemAt(i).locale.string(),
+ mGroupEntries.itemAt(i).vendor.string());
+ }
+
+ printf("\nFiles:\n");
+ AaptDir::print();
+}
+
+bool
+valid_symbol_name(const String8& symbol)
+{
+ static char const * const KEYWORDS[] = {
+ "abstract", "assert", "boolean", "break",
+ "byte", "case", "catch", "char", "class", "const", "continue",
+ "default", "do", "double", "else", "enum", "extends", "final",
+ "finally", "float", "for", "goto", "if", "implements", "import",
+ "instanceof", "int", "interface", "long", "native", "new", "package",
+ "private", "protected", "public", "return", "short", "static",
+ "strictfp", "super", "switch", "synchronized", "this", "throw",
+ "throws", "transient", "try", "void", "volatile", "while",
+ "true", "false", "null",
+ NULL
+ };
+ const char*const* k = KEYWORDS;
+ const char*const s = symbol.string();
+ while (*k) {
+ if (0 == strcmp(s, *k)) {
+ return false;
+ }
+ k++;
+ }
+ return true;
+}