AaptAssets.cpp revision 03589cc65355220e0a4a0c816189a9fa25cc81fc
19682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall//
29682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall// Copyright 2006 The Android Open Source Project
39682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall//
49682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
59682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall#include "AaptAssets.h"
69682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall#include "Main.h"
79682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
89682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall#include <utils/misc.h>
99682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall#include <utils/SortedVector.h>
109682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
119682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall#include <ctype.h>
129682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall#include <dirent.h>
139682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall#include <errno.h>
149682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
159682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic const char* kDefaultLocale = "default";
169682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic const char* kWildcardName = "any";
179682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic const char* kAssetDir = "assets";
189682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic const char* kResourceDir = "res";
199682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic const char* kInvalidChars = "/\\:";
209682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic const size_t kMaxAssetFileName = 100;
219682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
229682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic const String8 kResString(kResourceDir);
239682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
249682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall/*
259682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall * Names of asset files must meet the following criteria:
269682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall *
279682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall *  - the filename length must be less than kMaxAssetFileName bytes long
289682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall *    (and can't be empty)
299682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall *  - all characters must be 7-bit printable ASCII
309682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall *  - none of { '/' '\\' ':' }
319682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall *
329682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall * Pass in just the filename, not the full path.
339682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall */
349682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic bool validateFileName(const char* fileName)
359682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall{
369682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    const char* cp = fileName;
379682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    size_t len = 0;
389682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
399682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    while (*cp != '\0') {
409682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        if ((*cp & 0x80) != 0)
419682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall            return false;           // reject high ASCII
429682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        if (*cp < 0x20 || *cp >= 0x7f)
439682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall            return false;           // reject control chars and 0x7f
449682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        if (strchr(kInvalidChars, *cp) != NULL)
459682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall            return false;           // reject path sep chars
469682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        cp++;
479682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        len++;
489682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    }
499682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
509682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    if (len < 1 || len > kMaxAssetFileName)
519682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        return false;               // reject empty or too long
529682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
539682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    return true;
549682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall}
559682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
569682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hallstatic bool isHidden(const char *root, const char *path)
579682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall{
589682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    const char *ext  = NULL;
599682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    const char *type = NULL;
609682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall
619682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    // Skip all hidden files.
629682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    if (path[0] == '.') {
639682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        // Skip ., .. and  .svn but don't chatter about it.
649682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        if (strcmp(path, ".") == 0
659682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall            || strcmp(path, "..") == 0
669682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall            || strcmp(path, ".svn") == 0) {
679682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall            return true;
689682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        }
699682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        type = "hidden";
709682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall    } else if (path[0] == '_') {
719682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        // skip directories starting with _ (don't chatter about it)
729682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        String8 subdirName(root);
739682c8870b8ff5e4ac2e4c70b759f791c6f38c1fJesse Hall        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
111status_t
112AaptGroupEntry::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
239bool
240AaptGroupEntry::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
500success:
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
524String8
525AaptGroupEntry::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
561String8
562AaptGroupEntry::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
633bool 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
665bool 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    if (out) {
689        out->mnc = atoi(val);
690    }
691
692    return true;
693}
694
695/*
696 * Does this directory name fit the pattern of a locale dir ("en-rUS" or
697 * "default")?
698 *
699 * TODO: Should insist that the first two letters are lower case, and the
700 * second two are upper.
701 */
702bool AaptGroupEntry::getLocaleName(const char* fileName,
703                                   ResTable_config* out)
704{
705    if (strcmp(fileName, kWildcardName) == 0
706            || strcmp(fileName, kDefaultLocale) == 0) {
707        if (out) {
708            out->language[0] = 0;
709            out->language[1] = 0;
710            out->country[0] = 0;
711            out->country[1] = 0;
712        }
713        return true;
714    }
715
716    if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
717        if (out) {
718            out->language[0] = fileName[0];
719            out->language[1] = fileName[1];
720            out->country[0] = 0;
721            out->country[1] = 0;
722        }
723        return true;
724    }
725
726    if (strlen(fileName) == 5 &&
727        isalpha(fileName[0]) &&
728        isalpha(fileName[1]) &&
729        fileName[2] == '-' &&
730        isalpha(fileName[3]) &&
731        isalpha(fileName[4])) {
732        if (out) {
733            out->language[0] = fileName[0];
734            out->language[1] = fileName[1];
735            out->country[0] = fileName[3];
736            out->country[1] = fileName[4];
737        }
738        return true;
739    }
740
741    return false;
742}
743
744bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
745                                     ResTable_config* out)
746{
747    if (strcmp(name, kWildcardName) == 0) {
748        if (out) out->screenLayout =
749                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
750                | ResTable_config::SCREENSIZE_ANY;
751        return true;
752    } else if (strcmp(name, "small") == 0) {
753        if (out) out->screenLayout =
754                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
755                | ResTable_config::SCREENSIZE_SMALL;
756        return true;
757    } else if (strcmp(name, "normal") == 0) {
758        if (out) out->screenLayout =
759                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
760                | ResTable_config::SCREENSIZE_NORMAL;
761        return true;
762    } else if (strcmp(name, "large") == 0) {
763        if (out) out->screenLayout =
764                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
765                | ResTable_config::SCREENSIZE_LARGE;
766        return true;
767    } else if (strcmp(name, "xlarge") == 0) {
768        if (out) out->screenLayout =
769                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
770                | ResTable_config::SCREENSIZE_XLARGE;
771        return true;
772    }
773
774    return false;
775}
776
777bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
778                                     ResTable_config* out)
779{
780    if (strcmp(name, kWildcardName) == 0) {
781        if (out) out->screenLayout =
782                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
783                | ResTable_config::SCREENLONG_ANY;
784        return true;
785    } else if (strcmp(name, "long") == 0) {
786        if (out) out->screenLayout =
787                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
788                | ResTable_config::SCREENLONG_YES;
789        return true;
790    } else if (strcmp(name, "notlong") == 0) {
791        if (out) out->screenLayout =
792                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
793                | ResTable_config::SCREENLONG_NO;
794        return true;
795    }
796
797    return false;
798}
799
800bool AaptGroupEntry::getOrientationName(const char* name,
801                                        ResTable_config* out)
802{
803    if (strcmp(name, kWildcardName) == 0) {
804        if (out) out->orientation = out->ORIENTATION_ANY;
805        return true;
806    } else if (strcmp(name, "port") == 0) {
807        if (out) out->orientation = out->ORIENTATION_PORT;
808        return true;
809    } else if (strcmp(name, "land") == 0) {
810        if (out) out->orientation = out->ORIENTATION_LAND;
811        return true;
812    } else if (strcmp(name, "square") == 0) {
813        if (out) out->orientation = out->ORIENTATION_SQUARE;
814        return true;
815    }
816
817    return false;
818}
819
820bool AaptGroupEntry::getUiModeTypeName(const char* name,
821                                       ResTable_config* out)
822{
823    if (strcmp(name, kWildcardName) == 0) {
824        if (out) out->uiMode =
825                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
826                | ResTable_config::UI_MODE_TYPE_ANY;
827        return true;
828    } else if (strcmp(name, "desk") == 0) {
829      if (out) out->uiMode =
830              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
831              | ResTable_config::UI_MODE_TYPE_DESK;
832        return true;
833    } else if (strcmp(name, "car") == 0) {
834      if (out) out->uiMode =
835              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
836              | ResTable_config::UI_MODE_TYPE_CAR;
837        return true;
838    }
839
840    return false;
841}
842
843bool AaptGroupEntry::getUiModeNightName(const char* name,
844                                          ResTable_config* out)
845{
846    if (strcmp(name, kWildcardName) == 0) {
847        if (out) out->uiMode =
848                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
849                | ResTable_config::UI_MODE_NIGHT_ANY;
850        return true;
851    } else if (strcmp(name, "night") == 0) {
852        if (out) out->uiMode =
853                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
854                | ResTable_config::UI_MODE_NIGHT_YES;
855        return true;
856    } else if (strcmp(name, "notnight") == 0) {
857      if (out) out->uiMode =
858              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
859              | ResTable_config::UI_MODE_NIGHT_NO;
860        return true;
861    }
862
863    return false;
864}
865
866bool AaptGroupEntry::getDensityName(const char* name,
867                                    ResTable_config* out)
868{
869    if (strcmp(name, kWildcardName) == 0) {
870        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
871        return true;
872    }
873
874    if (strcmp(name, "nodpi") == 0) {
875        if (out) out->density = ResTable_config::DENSITY_NONE;
876        return true;
877    }
878
879    if (strcmp(name, "ldpi") == 0) {
880        if (out) out->density = ResTable_config::DENSITY_LOW;
881        return true;
882    }
883
884    if (strcmp(name, "mdpi") == 0) {
885        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
886        return true;
887    }
888
889    if (strcmp(name, "hdpi") == 0) {
890        if (out) out->density = ResTable_config::DENSITY_HIGH;
891        return true;
892    }
893
894    if (strcmp(name, "xhdpi") == 0) {
895        if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
896        return true;
897    }
898
899    char* c = (char*)name;
900    while (*c >= '0' && *c <= '9') {
901        c++;
902    }
903
904    // check that we have 'dpi' after the last digit.
905    if (toupper(c[0]) != 'D' ||
906            toupper(c[1]) != 'P' ||
907            toupper(c[2]) != 'I' ||
908            c[3] != 0) {
909        return false;
910    }
911
912    // temporarily replace the first letter with \0 to
913    // use atoi.
914    char tmp = c[0];
915    c[0] = '\0';
916
917    int d = atoi(name);
918    c[0] = tmp;
919
920    if (d != 0) {
921        if (out) out->density = d;
922        return true;
923    }
924
925    return false;
926}
927
928bool AaptGroupEntry::getTouchscreenName(const char* name,
929                                        ResTable_config* out)
930{
931    if (strcmp(name, kWildcardName) == 0) {
932        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
933        return true;
934    } else if (strcmp(name, "notouch") == 0) {
935        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
936        return true;
937    } else if (strcmp(name, "stylus") == 0) {
938        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
939        return true;
940    } else if (strcmp(name, "finger") == 0) {
941        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
942        return true;
943    }
944
945    return false;
946}
947
948bool AaptGroupEntry::getKeysHiddenName(const char* name,
949                                       ResTable_config* out)
950{
951    uint8_t mask = 0;
952    uint8_t value = 0;
953    if (strcmp(name, kWildcardName) == 0) {
954        mask = ResTable_config::MASK_KEYSHIDDEN;
955        value = ResTable_config::KEYSHIDDEN_ANY;
956    } else if (strcmp(name, "keysexposed") == 0) {
957        mask = ResTable_config::MASK_KEYSHIDDEN;
958        value = ResTable_config::KEYSHIDDEN_NO;
959    } else if (strcmp(name, "keyshidden") == 0) {
960        mask = ResTable_config::MASK_KEYSHIDDEN;
961        value = ResTable_config::KEYSHIDDEN_YES;
962    } else if (strcmp(name, "keyssoft") == 0) {
963        mask = ResTable_config::MASK_KEYSHIDDEN;
964        value = ResTable_config::KEYSHIDDEN_SOFT;
965    }
966
967    if (mask != 0) {
968        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
969        return true;
970    }
971
972    return false;
973}
974
975bool AaptGroupEntry::getKeyboardName(const char* name,
976                                        ResTable_config* out)
977{
978    if (strcmp(name, kWildcardName) == 0) {
979        if (out) out->keyboard = out->KEYBOARD_ANY;
980        return true;
981    } else if (strcmp(name, "nokeys") == 0) {
982        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
983        return true;
984    } else if (strcmp(name, "qwerty") == 0) {
985        if (out) out->keyboard = out->KEYBOARD_QWERTY;
986        return true;
987    } else if (strcmp(name, "12key") == 0) {
988        if (out) out->keyboard = out->KEYBOARD_12KEY;
989        return true;
990    }
991
992    return false;
993}
994
995bool AaptGroupEntry::getNavHiddenName(const char* name,
996                                       ResTable_config* out)
997{
998    uint8_t mask = 0;
999    uint8_t value = 0;
1000    if (strcmp(name, kWildcardName) == 0) {
1001        mask = ResTable_config::MASK_NAVHIDDEN;
1002        value = ResTable_config::NAVHIDDEN_ANY;
1003    } else if (strcmp(name, "navexposed") == 0) {
1004        mask = ResTable_config::MASK_NAVHIDDEN;
1005        value = ResTable_config::NAVHIDDEN_NO;
1006    } else if (strcmp(name, "navhidden") == 0) {
1007        mask = ResTable_config::MASK_NAVHIDDEN;
1008        value = ResTable_config::NAVHIDDEN_YES;
1009    }
1010
1011    if (mask != 0) {
1012        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
1013        return true;
1014    }
1015
1016    return false;
1017}
1018
1019bool AaptGroupEntry::getNavigationName(const char* name,
1020                                     ResTable_config* out)
1021{
1022    if (strcmp(name, kWildcardName) == 0) {
1023        if (out) out->navigation = out->NAVIGATION_ANY;
1024        return true;
1025    } else if (strcmp(name, "nonav") == 0) {
1026        if (out) out->navigation = out->NAVIGATION_NONAV;
1027        return true;
1028    } else if (strcmp(name, "dpad") == 0) {
1029        if (out) out->navigation = out->NAVIGATION_DPAD;
1030        return true;
1031    } else if (strcmp(name, "trackball") == 0) {
1032        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
1033        return true;
1034    } else if (strcmp(name, "wheel") == 0) {
1035        if (out) out->navigation = out->NAVIGATION_WHEEL;
1036        return true;
1037    }
1038
1039    return false;
1040}
1041
1042bool AaptGroupEntry::getScreenSizeName(const char* name,
1043                                       ResTable_config* out)
1044{
1045    if (strcmp(name, kWildcardName) == 0) {
1046        if (out) {
1047            out->screenWidth = out->SCREENWIDTH_ANY;
1048            out->screenHeight = out->SCREENHEIGHT_ANY;
1049        }
1050        return true;
1051    }
1052
1053    const char* x = name;
1054    while (*x >= '0' && *x <= '9') x++;
1055    if (x == name || *x != 'x') return false;
1056    String8 xName(name, x-name);
1057    x++;
1058
1059    const char* y = x;
1060    while (*y >= '0' && *y <= '9') y++;
1061    if (y == name || *y != 0) return false;
1062    String8 yName(x, y-x);
1063
1064    uint16_t w = (uint16_t)atoi(xName.string());
1065    uint16_t h = (uint16_t)atoi(yName.string());
1066    if (w < h) {
1067        return false;
1068    }
1069
1070    if (out) {
1071        out->screenWidth = w;
1072        out->screenHeight = h;
1073    }
1074
1075    return true;
1076}
1077
1078bool AaptGroupEntry::getVersionName(const char* name,
1079                                    ResTable_config* out)
1080{
1081    if (strcmp(name, kWildcardName) == 0) {
1082        if (out) {
1083            out->sdkVersion = out->SDKVERSION_ANY;
1084            out->minorVersion = out->MINORVERSION_ANY;
1085        }
1086        return true;
1087    }
1088
1089    if (*name != 'v') {
1090        return false;
1091    }
1092
1093    name++;
1094    const char* s = name;
1095    while (*s >= '0' && *s <= '9') s++;
1096    if (s == name || *s != 0) return false;
1097    String8 sdkName(name, s-name);
1098
1099    if (out) {
1100        out->sdkVersion = (uint16_t)atoi(sdkName.string());
1101        out->minorVersion = 0;
1102    }
1103
1104    return true;
1105}
1106
1107int AaptGroupEntry::compare(const AaptGroupEntry& o) const
1108{
1109    int v = mcc.compare(o.mcc);
1110    if (v == 0) v = mnc.compare(o.mnc);
1111    if (v == 0) v = locale.compare(o.locale);
1112    if (v == 0) v = vendor.compare(o.vendor);
1113    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
1114    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
1115    if (v == 0) v = orientation.compare(o.orientation);
1116    if (v == 0) v = uiModeType.compare(o.uiModeType);
1117    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
1118    if (v == 0) v = density.compare(o.density);
1119    if (v == 0) v = touchscreen.compare(o.touchscreen);
1120    if (v == 0) v = keysHidden.compare(o.keysHidden);
1121    if (v == 0) v = keyboard.compare(o.keyboard);
1122    if (v == 0) v = navHidden.compare(o.navHidden);
1123    if (v == 0) v = navigation.compare(o.navigation);
1124    if (v == 0) v = screenSize.compare(o.screenSize);
1125    if (v == 0) v = version.compare(o.version);
1126    return v;
1127}
1128
1129ResTable_config AaptGroupEntry::toParams() const
1130{
1131    ResTable_config params;
1132    memset(&params, 0, sizeof(params));
1133    getMccName(mcc.string(), &params);
1134    getMncName(mnc.string(), &params);
1135    getLocaleName(locale.string(), &params);
1136    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
1137    getScreenLayoutLongName(screenLayoutLong.string(), &params);
1138    getOrientationName(orientation.string(), &params);
1139    getUiModeTypeName(uiModeType.string(), &params);
1140    getUiModeNightName(uiModeNight.string(), &params);
1141    getDensityName(density.string(), &params);
1142    getTouchscreenName(touchscreen.string(), &params);
1143    getKeysHiddenName(keysHidden.string(), &params);
1144    getKeyboardName(keyboard.string(), &params);
1145    getNavHiddenName(navHidden.string(), &params);
1146    getNavigationName(navigation.string(), &params);
1147    getScreenSizeName(screenSize.string(), &params);
1148    getVersionName(version.string(), &params);
1149
1150    // Fix up version number based on specified parameters.
1151    int minSdk = 0;
1152    if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
1153                != ResTable_config::UI_MODE_TYPE_ANY
1154            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
1155                != ResTable_config::UI_MODE_NIGHT_ANY) {
1156        minSdk = SDK_FROYO;
1157    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
1158                != ResTable_config::SCREENSIZE_ANY
1159            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
1160                != ResTable_config::SCREENLONG_ANY
1161            || params.density != ResTable_config::DENSITY_DEFAULT) {
1162        minSdk = SDK_DONUT;
1163    }
1164
1165    if (minSdk > params.sdkVersion) {
1166        params.sdkVersion = minSdk;
1167    }
1168
1169    return params;
1170}
1171
1172// =========================================================================
1173// =========================================================================
1174// =========================================================================
1175
1176void* AaptFile::editData(size_t size)
1177{
1178    if (size <= mBufferSize) {
1179        mDataSize = size;
1180        return mData;
1181    }
1182    size_t allocSize = (size*3)/2;
1183    void* buf = realloc(mData, allocSize);
1184    if (buf == NULL) {
1185        return NULL;
1186    }
1187    mData = buf;
1188    mDataSize = size;
1189    mBufferSize = allocSize;
1190    return buf;
1191}
1192
1193void* AaptFile::editData(size_t* outSize)
1194{
1195    if (outSize) {
1196        *outSize = mDataSize;
1197    }
1198    return mData;
1199}
1200
1201void* AaptFile::padData(size_t wordSize)
1202{
1203    const size_t extra = mDataSize%wordSize;
1204    if (extra == 0) {
1205        return mData;
1206    }
1207
1208    size_t initial = mDataSize;
1209    void* data = editData(initial+(wordSize-extra));
1210    if (data != NULL) {
1211        memset(((uint8_t*)data) + initial, 0, wordSize-extra);
1212    }
1213    return data;
1214}
1215
1216status_t AaptFile::writeData(const void* data, size_t size)
1217{
1218    size_t end = mDataSize;
1219    size_t total = size + end;
1220    void* buf = editData(total);
1221    if (buf == NULL) {
1222        return UNKNOWN_ERROR;
1223    }
1224    memcpy(((char*)buf)+end, data, size);
1225    return NO_ERROR;
1226}
1227
1228void AaptFile::clearData()
1229{
1230    if (mData != NULL) free(mData);
1231    mData = NULL;
1232    mDataSize = 0;
1233    mBufferSize = 0;
1234}
1235
1236String8 AaptFile::getPrintableSource() const
1237{
1238    if (hasData()) {
1239        String8 name(mGroupEntry.locale.string());
1240        name.appendPath(mGroupEntry.vendor.string());
1241        name.appendPath(mPath);
1242        name.append(" #generated");
1243        return name;
1244    }
1245    return mSourceFile;
1246}
1247
1248// =========================================================================
1249// =========================================================================
1250// =========================================================================
1251
1252status_t AaptGroup::addFile(const sp<AaptFile>& file)
1253{
1254    if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
1255        file->mPath = mPath;
1256        mFiles.add(file->getGroupEntry(), file);
1257        return NO_ERROR;
1258    }
1259
1260    SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
1261                                               getPrintableSource().string());
1262    return UNKNOWN_ERROR;
1263}
1264
1265void AaptGroup::removeFile(size_t index)
1266{
1267	mFiles.removeItemsAt(index);
1268}
1269
1270void AaptGroup::print() const
1271{
1272    printf("  %s\n", getPath().string());
1273    const size_t N=mFiles.size();
1274    size_t i;
1275    for (i=0; i<N; i++) {
1276        sp<AaptFile> file = mFiles.valueAt(i);
1277        const AaptGroupEntry& e = file->getGroupEntry();
1278        if (file->hasData()) {
1279            printf("      Gen: (%s) %d bytes\n", e.toString().string(),
1280                    (int)file->getSize());
1281        } else {
1282            printf("      Src: %s\n", file->getPrintableSource().string());
1283        }
1284    }
1285}
1286
1287String8 AaptGroup::getPrintableSource() const
1288{
1289    if (mFiles.size() > 0) {
1290        // Arbitrarily pull the first source file out of the list.
1291        return mFiles.valueAt(0)->getPrintableSource();
1292    }
1293
1294    // Should never hit this case, but to be safe...
1295    return getPath();
1296
1297}
1298
1299// =========================================================================
1300// =========================================================================
1301// =========================================================================
1302
1303status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
1304{
1305    if (mFiles.indexOfKey(name) >= 0) {
1306        return ALREADY_EXISTS;
1307    }
1308    mFiles.add(name, file);
1309    return NO_ERROR;
1310}
1311
1312status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
1313{
1314    if (mDirs.indexOfKey(name) >= 0) {
1315        return ALREADY_EXISTS;
1316    }
1317    mDirs.add(name, dir);
1318    return NO_ERROR;
1319}
1320
1321sp<AaptDir> AaptDir::makeDir(const String8& path)
1322{
1323    String8 name;
1324    String8 remain = path;
1325
1326    sp<AaptDir> subdir = this;
1327    while (name = remain.walkPath(&remain), remain != "") {
1328        subdir = subdir->makeDir(name);
1329    }
1330
1331    ssize_t i = subdir->mDirs.indexOfKey(name);
1332    if (i >= 0) {
1333        return subdir->mDirs.valueAt(i);
1334    }
1335    sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
1336    subdir->mDirs.add(name, dir);
1337    return dir;
1338}
1339
1340void AaptDir::removeFile(const String8& name)
1341{
1342    mFiles.removeItem(name);
1343}
1344
1345void AaptDir::removeDir(const String8& name)
1346{
1347    mDirs.removeItem(name);
1348}
1349
1350status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
1351{
1352	sp<AaptGroup> origGroup;
1353
1354	// Find and remove the given file with shear, brute force!
1355	const size_t NG = mFiles.size();
1356	size_t i;
1357	for (i=0; origGroup == NULL && i<NG; i++) {
1358		sp<AaptGroup> g = mFiles.valueAt(i);
1359		const size_t NF = g->getFiles().size();
1360		for (size_t j=0; j<NF; j++) {
1361			if (g->getFiles().valueAt(j) == file) {
1362				origGroup = g;
1363				g->removeFile(j);
1364				if (NF == 1) {
1365					mFiles.removeItemsAt(i);
1366				}
1367				break;
1368			}
1369		}
1370	}
1371
1372	//printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
1373
1374	// Place the file under its new name.
1375	if (origGroup != NULL) {
1376		return addLeafFile(newName, file);
1377	}
1378
1379	return NO_ERROR;
1380}
1381
1382status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
1383{
1384    sp<AaptGroup> group;
1385    if (mFiles.indexOfKey(leafName) >= 0) {
1386        group = mFiles.valueFor(leafName);
1387    } else {
1388        group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
1389        mFiles.add(leafName, group);
1390    }
1391
1392    return group->addFile(file);
1393}
1394
1395ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
1396                            const AaptGroupEntry& kind, const String8& resType,
1397                            sp<FilePathStore>& fullResPaths)
1398{
1399    Vector<String8> fileNames;
1400    {
1401        DIR* dir = NULL;
1402
1403        dir = opendir(srcDir.string());
1404        if (dir == NULL) {
1405            fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1406            return UNKNOWN_ERROR;
1407        }
1408
1409        /*
1410         * Slurp the filenames out of the directory.
1411         */
1412        while (1) {
1413            struct dirent* entry;
1414
1415            entry = readdir(dir);
1416            if (entry == NULL)
1417                break;
1418
1419            if (isHidden(srcDir.string(), entry->d_name))
1420                continue;
1421
1422            String8 name(entry->d_name);
1423            fileNames.add(name);
1424            // Add fully qualified path for dependency purposes
1425            // if we're collecting them
1426            if (fullResPaths != NULL) {
1427                fullResPaths->add(srcDir.appendPathCopy(name));
1428            }
1429        }
1430        closedir(dir);
1431    }
1432
1433    ssize_t count = 0;
1434
1435    /*
1436     * Stash away the files and recursively descend into subdirectories.
1437     */
1438    const size_t N = fileNames.size();
1439    size_t i;
1440    for (i = 0; i < N; i++) {
1441        String8 pathName(srcDir);
1442        FileType type;
1443
1444        pathName.appendPath(fileNames[i].string());
1445        type = getFileType(pathName.string());
1446        if (type == kFileTypeDirectory) {
1447            sp<AaptDir> subdir;
1448            bool notAdded = false;
1449            if (mDirs.indexOfKey(fileNames[i]) >= 0) {
1450                subdir = mDirs.valueFor(fileNames[i]);
1451            } else {
1452                subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
1453                notAdded = true;
1454            }
1455            ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
1456                                                resType, fullResPaths);
1457            if (res < NO_ERROR) {
1458                return res;
1459            }
1460            if (res > 0 && notAdded) {
1461                mDirs.add(fileNames[i], subdir);
1462            }
1463            count += res;
1464        } else if (type == kFileTypeRegular) {
1465            sp<AaptFile> file = new AaptFile(pathName, kind, resType);
1466            status_t err = addLeafFile(fileNames[i], file);
1467            if (err != NO_ERROR) {
1468                return err;
1469            }
1470
1471            count++;
1472
1473        } else {
1474            if (bundle->getVerbose())
1475                printf("   (ignoring non-file/dir '%s')\n", pathName.string());
1476        }
1477    }
1478
1479    return count;
1480}
1481
1482status_t AaptDir::validate() const
1483{
1484    const size_t NF = mFiles.size();
1485    const size_t ND = mDirs.size();
1486    size_t i;
1487    for (i = 0; i < NF; i++) {
1488        if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
1489            SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1490                    "Invalid filename.  Unable to add.");
1491            return UNKNOWN_ERROR;
1492        }
1493
1494        size_t j;
1495        for (j = i+1; j < NF; j++) {
1496            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1497                           mFiles.valueAt(j)->getLeaf().string()) == 0) {
1498                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1499                        "File is case-insensitive equivalent to: %s",
1500                        mFiles.valueAt(j)->getPrintableSource().string());
1501                return UNKNOWN_ERROR;
1502            }
1503
1504            // TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
1505            // (this is mostly caught by the "marked" stuff, below)
1506        }
1507
1508        for (j = 0; j < ND; j++) {
1509            if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
1510                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1511                SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
1512                        "File conflicts with dir from: %s",
1513                        mDirs.valueAt(j)->getPrintableSource().string());
1514                return UNKNOWN_ERROR;
1515            }
1516        }
1517    }
1518
1519    for (i = 0; i < ND; i++) {
1520        if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
1521            SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1522                    "Invalid directory name, unable to add.");
1523            return UNKNOWN_ERROR;
1524        }
1525
1526        size_t j;
1527        for (j = i+1; j < ND; j++) {
1528            if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
1529                           mDirs.valueAt(j)->getLeaf().string()) == 0) {
1530                SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
1531                        "Directory is case-insensitive equivalent to: %s",
1532                        mDirs.valueAt(j)->getPrintableSource().string());
1533                return UNKNOWN_ERROR;
1534            }
1535        }
1536
1537        status_t err = mDirs.valueAt(i)->validate();
1538        if (err != NO_ERROR) {
1539            return err;
1540        }
1541    }
1542
1543    return NO_ERROR;
1544}
1545
1546void AaptDir::print() const
1547{
1548    const size_t ND=getDirs().size();
1549    size_t i;
1550    for (i=0; i<ND; i++) {
1551        getDirs().valueAt(i)->print();
1552    }
1553
1554    const size_t NF=getFiles().size();
1555    for (i=0; i<NF; i++) {
1556        getFiles().valueAt(i)->print();
1557    }
1558}
1559
1560String8 AaptDir::getPrintableSource() const
1561{
1562    if (mFiles.size() > 0) {
1563        // Arbitrarily pull the first file out of the list as the source dir.
1564        return mFiles.valueAt(0)->getPrintableSource().getPathDir();
1565    }
1566    if (mDirs.size() > 0) {
1567        // Or arbitrarily pull the first dir out of the list as the source dir.
1568        return mDirs.valueAt(0)->getPrintableSource().getPathDir();
1569    }
1570
1571    // Should never hit this case, but to be safe...
1572    return mPath;
1573
1574}
1575
1576// =========================================================================
1577// =========================================================================
1578// =========================================================================
1579
1580sp<AaptFile> AaptAssets::addFile(
1581        const String8& filePath, const AaptGroupEntry& entry,
1582        const String8& srcDir, sp<AaptGroup>* outGroup,
1583        const String8& resType)
1584{
1585    sp<AaptDir> dir = this;
1586    sp<AaptGroup> group;
1587    sp<AaptFile> file;
1588    String8 root, remain(filePath), partialPath;
1589    while (remain.length() > 0) {
1590        root = remain.walkPath(&remain);
1591        partialPath.appendPath(root);
1592
1593        const String8 rootStr(root);
1594
1595        if (remain.length() == 0) {
1596            ssize_t i = dir->getFiles().indexOfKey(rootStr);
1597            if (i >= 0) {
1598                group = dir->getFiles().valueAt(i);
1599            } else {
1600                group = new AaptGroup(rootStr, filePath);
1601                status_t res = dir->addFile(rootStr, group);
1602                if (res != NO_ERROR) {
1603                    return NULL;
1604                }
1605            }
1606            file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
1607            status_t res = group->addFile(file);
1608            if (res != NO_ERROR) {
1609                return NULL;
1610            }
1611            break;
1612
1613        } else {
1614            ssize_t i = dir->getDirs().indexOfKey(rootStr);
1615            if (i >= 0) {
1616                dir = dir->getDirs().valueAt(i);
1617            } else {
1618                sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
1619                status_t res = dir->addDir(rootStr, subdir);
1620                if (res != NO_ERROR) {
1621                    return NULL;
1622                }
1623                dir = subdir;
1624            }
1625        }
1626    }
1627
1628    mGroupEntries.add(entry);
1629    if (outGroup) *outGroup = group;
1630    return file;
1631}
1632
1633void AaptAssets::addResource(const String8& leafName, const String8& path,
1634                const sp<AaptFile>& file, const String8& resType)
1635{
1636    sp<AaptDir> res = AaptDir::makeDir(kResString);
1637    String8 dirname = file->getGroupEntry().toDirName(resType);
1638    sp<AaptDir> subdir = res->makeDir(dirname);
1639    sp<AaptGroup> grr = new AaptGroup(leafName, path);
1640    grr->addFile(file);
1641
1642    subdir->addFile(leafName, grr);
1643}
1644
1645
1646ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
1647{
1648    int count;
1649    int totalCount = 0;
1650    FileType type;
1651    const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
1652    const size_t dirCount =resDirs.size();
1653    sp<AaptAssets> current = this;
1654
1655    const int N = bundle->getFileSpecCount();
1656
1657    /*
1658     * If a package manifest was specified, include that first.
1659     */
1660    if (bundle->getAndroidManifestFile() != NULL) {
1661        // place at root of zip.
1662        String8 srcFile(bundle->getAndroidManifestFile());
1663        addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
1664                NULL, String8());
1665        totalCount++;
1666    }
1667
1668    /*
1669     * If a directory of custom assets was supplied, slurp 'em up.
1670     */
1671    if (bundle->getAssetSourceDir()) {
1672        const char* assetDir = bundle->getAssetSourceDir();
1673
1674        FileType type = getFileType(assetDir);
1675        if (type == kFileTypeNonexistent) {
1676            fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
1677            return UNKNOWN_ERROR;
1678        }
1679        if (type != kFileTypeDirectory) {
1680            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1681            return UNKNOWN_ERROR;
1682        }
1683
1684        String8 assetRoot(assetDir);
1685        sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
1686        AaptGroupEntry group;
1687        count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
1688                                            String8(), mFullAssetPaths);
1689        if (count < 0) {
1690            totalCount = count;
1691            goto bail;
1692        }
1693        if (count > 0) {
1694            mGroupEntries.add(group);
1695        }
1696        totalCount += count;
1697
1698        if (bundle->getVerbose())
1699            printf("Found %d custom asset file%s in %s\n",
1700                   count, (count==1) ? "" : "s", assetDir);
1701    }
1702
1703    /*
1704     * If a directory of resource-specific assets was supplied, slurp 'em up.
1705     */
1706    for (size_t i=0; i<dirCount; i++) {
1707        const char *res = resDirs[i];
1708        if (res) {
1709            type = getFileType(res);
1710            if (type == kFileTypeNonexistent) {
1711                fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
1712                return UNKNOWN_ERROR;
1713            }
1714            if (type == kFileTypeDirectory) {
1715                if (i>0) {
1716                    sp<AaptAssets> nextOverlay = new AaptAssets();
1717                    current->setOverlay(nextOverlay);
1718                    current = nextOverlay;
1719                    current->setFullResPaths(mFullResPaths);
1720                }
1721                count = current->slurpResourceTree(bundle, String8(res));
1722
1723                if (count < 0) {
1724                    totalCount = count;
1725                    goto bail;
1726                }
1727                totalCount += count;
1728            }
1729            else {
1730                fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
1731                return UNKNOWN_ERROR;
1732            }
1733        }
1734
1735    }
1736    /*
1737     * Now do any additional raw files.
1738     */
1739    for (int arg=0; arg<N; arg++) {
1740        const char* assetDir = bundle->getFileSpecEntry(arg);
1741
1742        FileType type = getFileType(assetDir);
1743        if (type == kFileTypeNonexistent) {
1744            fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
1745            return UNKNOWN_ERROR;
1746        }
1747        if (type != kFileTypeDirectory) {
1748            fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
1749            return UNKNOWN_ERROR;
1750        }
1751
1752        String8 assetRoot(assetDir);
1753
1754        if (bundle->getVerbose())
1755            printf("Processing raw dir '%s'\n", (const char*) assetDir);
1756
1757        /*
1758         * Do a recursive traversal of subdir tree.  We don't make any
1759         * guarantees about ordering, so we're okay with an inorder search
1760         * using whatever order the OS happens to hand back to us.
1761         */
1762        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
1763        if (count < 0) {
1764            /* failure; report error and remove archive */
1765            totalCount = count;
1766            goto bail;
1767        }
1768        totalCount += count;
1769
1770        if (bundle->getVerbose())
1771            printf("Found %d asset file%s in %s\n",
1772                   count, (count==1) ? "" : "s", assetDir);
1773    }
1774
1775    count = validate();
1776    if (count != NO_ERROR) {
1777        totalCount = count;
1778        goto bail;
1779    }
1780
1781
1782bail:
1783    return totalCount;
1784}
1785
1786ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
1787                                    const AaptGroupEntry& kind,
1788                                    const String8& resType,
1789                                    sp<FilePathStore>& fullResPaths)
1790{
1791    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
1792    if (res > 0) {
1793        mGroupEntries.add(kind);
1794    }
1795
1796    return res;
1797}
1798
1799ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
1800{
1801    ssize_t err = 0;
1802
1803    DIR* dir = opendir(srcDir.string());
1804    if (dir == NULL) {
1805        fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
1806        return UNKNOWN_ERROR;
1807    }
1808
1809    status_t count = 0;
1810
1811    /*
1812     * Run through the directory, looking for dirs that match the
1813     * expected pattern.
1814     */
1815    while (1) {
1816        struct dirent* entry = readdir(dir);
1817        if (entry == NULL) {
1818            break;
1819        }
1820
1821        if (isHidden(srcDir.string(), entry->d_name)) {
1822            continue;
1823        }
1824
1825        String8 subdirName(srcDir);
1826        subdirName.appendPath(entry->d_name);
1827
1828        AaptGroupEntry group;
1829        String8 resType;
1830        bool b = group.initFromDirName(entry->d_name, &resType);
1831        if (!b) {
1832            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
1833                    entry->d_name);
1834            err = -1;
1835            continue;
1836        }
1837
1838        if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
1839            int maxResInt = atoi(bundle->getMaxResVersion());
1840            const char *verString = group.version.string();
1841            int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
1842            if (dirVersionInt > maxResInt) {
1843              fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
1844              continue;
1845            }
1846        }
1847
1848        FileType type = getFileType(subdirName.string());
1849
1850        if (type == kFileTypeDirectory) {
1851            sp<AaptDir> dir = makeDir(String8(entry->d_name));
1852            ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
1853                                                resType, mFullResPaths);
1854            if (res < 0) {
1855                count = res;
1856                goto bail;
1857            }
1858            if (res > 0) {
1859                mGroupEntries.add(group);
1860                count += res;
1861            }
1862
1863            mDirs.add(dir);
1864        } else {
1865            if (bundle->getVerbose()) {
1866                fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
1867            }
1868        }
1869    }
1870
1871bail:
1872    closedir(dir);
1873    dir = NULL;
1874
1875    if (err != 0) {
1876        return err;
1877    }
1878    return count;
1879}
1880
1881ssize_t
1882AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
1883{
1884    int count = 0;
1885    SortedVector<AaptGroupEntry> entries;
1886
1887    ZipFile* zip = new ZipFile;
1888    status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
1889    if (err != NO_ERROR) {
1890        fprintf(stderr, "error opening zip file %s\n", filename);
1891        count = err;
1892        delete zip;
1893        return -1;
1894    }
1895
1896    const int N = zip->getNumEntries();
1897    for (int i=0; i<N; i++) {
1898        ZipEntry* entry = zip->getEntryByIndex(i);
1899        if (entry->getDeleted()) {
1900            continue;
1901        }
1902
1903        String8 entryName(entry->getFileName());
1904
1905        String8 dirName = entryName.getPathDir();
1906        sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
1907
1908        String8 resType;
1909        AaptGroupEntry kind;
1910
1911        String8 remain;
1912        if (entryName.walkPath(&remain) == kResourceDir) {
1913            // these are the resources, pull their type out of the directory name
1914            kind.initFromDirName(remain.walkPath().string(), &resType);
1915        } else {
1916            // these are untyped and don't have an AaptGroupEntry
1917        }
1918        if (entries.indexOf(kind) < 0) {
1919            entries.add(kind);
1920            mGroupEntries.add(kind);
1921        }
1922
1923        // use the one from the zip file if they both exist.
1924        dir->removeFile(entryName.getPathLeaf());
1925
1926        sp<AaptFile> file = new AaptFile(entryName, kind, resType);
1927        status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
1928        if (err != NO_ERROR) {
1929            fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
1930            count = err;
1931            goto bail;
1932        }
1933        file->setCompressionMethod(entry->getCompressionMethod());
1934
1935#if 0
1936        if (entryName == "AndroidManifest.xml") {
1937            printf("AndroidManifest.xml\n");
1938        }
1939        printf("\n\nfile: %s\n", entryName.string());
1940#endif
1941
1942        size_t len = entry->getUncompressedLen();
1943        void* data = zip->uncompress(entry);
1944        void* buf = file->editData(len);
1945        memcpy(buf, data, len);
1946
1947#if 0
1948        const int OFF = 0;
1949        const unsigned char* p = (unsigned char*)data;
1950        const unsigned char* end = p+len;
1951        p += OFF;
1952        for (int i=0; i<32 && p < end; i++) {
1953            printf("0x%03x ", i*0x10 + OFF);
1954            for (int j=0; j<0x10 && p < end; j++) {
1955                printf(" %02x", *p);
1956                p++;
1957            }
1958            printf("\n");
1959        }
1960#endif
1961
1962        free(data);
1963
1964        count++;
1965    }
1966
1967bail:
1968    delete zip;
1969    return count;
1970}
1971
1972sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
1973{
1974    sp<AaptSymbols> sym = mSymbols.valueFor(name);
1975    if (sym == NULL) {
1976        sym = new AaptSymbols();
1977        mSymbols.add(name, sym);
1978    }
1979    return sym;
1980}
1981
1982status_t AaptAssets::buildIncludedResources(Bundle* bundle)
1983{
1984    if (!mHaveIncludedAssets) {
1985        // Add in all includes.
1986        const Vector<const char*>& incl = bundle->getPackageIncludes();
1987        const size_t N=incl.size();
1988        for (size_t i=0; i<N; i++) {
1989            if (bundle->getVerbose())
1990                printf("Including resources from package: %s\n", incl[i]);
1991            if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
1992                fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
1993                        incl[i]);
1994                return UNKNOWN_ERROR;
1995            }
1996        }
1997        mHaveIncludedAssets = true;
1998    }
1999
2000    return NO_ERROR;
2001}
2002
2003status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
2004{
2005    const ResTable& res = getIncludedResources();
2006    // XXX dirty!
2007    return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
2008}
2009
2010const ResTable& AaptAssets::getIncludedResources() const
2011{
2012    return mIncludedAssets.getResources(false);
2013}
2014
2015void AaptAssets::print() const
2016{
2017    printf("Locale/Vendor pairs:\n");
2018    const size_t N=mGroupEntries.size();
2019    for (size_t i=0; i<N; i++) {
2020        printf("   %s/%s\n",
2021               mGroupEntries.itemAt(i).locale.string(),
2022               mGroupEntries.itemAt(i).vendor.string());
2023    }
2024
2025    printf("\nFiles:\n");
2026    AaptDir::print();
2027}
2028
2029sp<AaptDir> AaptAssets::resDir(const String8& name)
2030{
2031    const Vector<sp<AaptDir> >& dirs = mDirs;
2032    const size_t N = dirs.size();
2033    for (size_t i=0; i<N; i++) {
2034        const sp<AaptDir>& d = dirs.itemAt(i);
2035        if (d->getLeaf() == name) {
2036            return d;
2037        }
2038    }
2039    return NULL;
2040}
2041
2042bool
2043valid_symbol_name(const String8& symbol)
2044{
2045    static char const * const KEYWORDS[] = {
2046        "abstract", "assert", "boolean", "break",
2047        "byte", "case", "catch", "char", "class", "const", "continue",
2048        "default", "do", "double", "else", "enum", "extends", "final",
2049        "finally", "float", "for", "goto", "if", "implements", "import",
2050        "instanceof", "int", "interface", "long", "native", "new", "package",
2051        "private", "protected", "public", "return", "short", "static",
2052        "strictfp", "super", "switch", "synchronized", "this", "throw",
2053        "throws", "transient", "try", "void", "volatile", "while",
2054        "true", "false", "null",
2055        NULL
2056    };
2057    const char*const* k = KEYWORDS;
2058    const char*const s = symbol.string();
2059    while (*k) {
2060        if (0 == strcmp(s, *k)) {
2061            return false;
2062        }
2063        k++;
2064    }
2065    return true;
2066}
2067