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