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