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