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