1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "ResourceUtils.h"
18
19 #include <sstream>
20
21 #include "android-base/stringprintf.h"
22 #include "androidfw/ResourceTypes.h"
23 #include "androidfw/ResourceUtils.h"
24
25 #include "NameMangler.h"
26 #include "SdkConstants.h"
27 #include "format/binary/ResourceTypeExtensions.h"
28 #include "text/Unicode.h"
29 #include "text/Utf8Iterator.h"
30 #include "util/Files.h"
31 #include "util/Util.h"
32
33 using ::aapt::text::Utf8Iterator;
34 using ::android::StringPiece;
35 using ::android::StringPiece16;
36 using ::android::base::StringPrintf;
37
38 namespace aapt {
39 namespace ResourceUtils {
40
ToResourceName(const android::ResTable::resource_name & name_in)41 Maybe<ResourceName> ToResourceName(
42 const android::ResTable::resource_name& name_in) {
43 ResourceName name_out;
44 if (!name_in.package) {
45 return {};
46 }
47
48 name_out.package =
49 util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
50
51 const ResourceType* type;
52 if (name_in.type) {
53 type = ParseResourceType(
54 util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
55 } else if (name_in.type8) {
56 type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
57 } else {
58 return {};
59 }
60
61 if (!type) {
62 return {};
63 }
64
65 name_out.type = *type;
66
67 if (name_in.name) {
68 name_out.entry =
69 util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
70 } else if (name_in.name8) {
71 name_out.entry.assign(name_in.name8, name_in.nameLen);
72 } else {
73 return {};
74 }
75 return name_out;
76 }
77
ParseResourceName(const StringPiece & str,ResourceNameRef * out_ref,bool * out_private)78 bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
79 bool* out_private) {
80 if (str.empty()) {
81 return false;
82 }
83
84 size_t offset = 0;
85 bool priv = false;
86 if (str.data()[0] == '*') {
87 priv = true;
88 offset = 1;
89 }
90
91 StringPiece package;
92 StringPiece type;
93 StringPiece entry;
94 if (!android::ExtractResourceName(str.substr(offset, str.size() - offset), &package, &type,
95 &entry)) {
96 return false;
97 }
98
99 const ResourceType* parsed_type = ParseResourceType(type);
100 if (!parsed_type) {
101 return false;
102 }
103
104 if (entry.empty()) {
105 return false;
106 }
107
108 if (out_ref) {
109 out_ref->package = package;
110 out_ref->type = *parsed_type;
111 out_ref->entry = entry;
112 }
113
114 if (out_private) {
115 *out_private = priv;
116 }
117 return true;
118 }
119
ParseReference(const StringPiece & str,ResourceNameRef * out_ref,bool * out_create,bool * out_private)120 bool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
121 bool* out_create, bool* out_private) {
122 StringPiece trimmed_str(util::TrimWhitespace(str));
123 if (trimmed_str.empty()) {
124 return false;
125 }
126
127 bool create = false;
128 bool priv = false;
129 if (trimmed_str.data()[0] == '@') {
130 size_t offset = 1;
131 if (trimmed_str.data()[1] == '+') {
132 create = true;
133 offset += 1;
134 }
135
136 ResourceNameRef name;
137 if (!ParseResourceName(
138 trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
139 &priv)) {
140 return false;
141 }
142
143 if (create && priv) {
144 return false;
145 }
146
147 if (create && name.type != ResourceType::kId) {
148 return false;
149 }
150
151 if (out_ref) {
152 *out_ref = name;
153 }
154
155 if (out_create) {
156 *out_create = create;
157 }
158
159 if (out_private) {
160 *out_private = priv;
161 }
162 return true;
163 }
164 return false;
165 }
166
IsReference(const StringPiece & str)167 bool IsReference(const StringPiece& str) {
168 return ParseReference(str, nullptr, nullptr, nullptr);
169 }
170
ParseAttributeReference(const StringPiece & str,ResourceNameRef * out_ref)171 bool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
172 StringPiece trimmed_str(util::TrimWhitespace(str));
173 if (trimmed_str.empty()) {
174 return false;
175 }
176
177 if (*trimmed_str.data() == '?') {
178 StringPiece package;
179 StringPiece type;
180 StringPiece entry;
181 if (!android::ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), &package,
182 &type, &entry)) {
183 return false;
184 }
185
186 if (!type.empty() && type != "attr") {
187 return false;
188 }
189
190 if (entry.empty()) {
191 return false;
192 }
193
194 if (out_ref) {
195 out_ref->package = package;
196 out_ref->type = ResourceType::kAttr;
197 out_ref->entry = entry;
198 }
199 return true;
200 }
201 return false;
202 }
203
IsAttributeReference(const StringPiece & str)204 bool IsAttributeReference(const StringPiece& str) {
205 return ParseAttributeReference(str, nullptr);
206 }
207
208 /*
209 * Style parent's are a bit different. We accept the following formats:
210 *
211 * @[[*]package:][style/]<entry>
212 * ?[[*]package:]style/<entry>
213 * <[*]package>:[style/]<entry>
214 * [[*]package:style/]<entry>
215 */
ParseStyleParentReference(const StringPiece & str,std::string * out_error)216 Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
217 std::string* out_error) {
218 if (str.empty()) {
219 return {};
220 }
221
222 StringPiece name = str;
223
224 bool has_leading_identifiers = false;
225 bool private_ref = false;
226
227 // Skip over these identifiers. A style's parent is a normal reference.
228 if (name.data()[0] == '@' || name.data()[0] == '?') {
229 has_leading_identifiers = true;
230 name = name.substr(1, name.size() - 1);
231 }
232
233 if (name.data()[0] == '*') {
234 private_ref = true;
235 name = name.substr(1, name.size() - 1);
236 }
237
238 ResourceNameRef ref;
239 ref.type = ResourceType::kStyle;
240
241 StringPiece type_str;
242 android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
243 if (!type_str.empty()) {
244 // If we have a type, make sure it is a Style.
245 const ResourceType* parsed_type = ParseResourceType(type_str);
246 if (!parsed_type || *parsed_type != ResourceType::kStyle) {
247 std::stringstream err;
248 err << "invalid resource type '" << type_str << "' for parent of style";
249 *out_error = err.str();
250 return {};
251 }
252 }
253
254 if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
255 std::stringstream err;
256 err << "invalid parent reference '" << str << "'";
257 *out_error = err.str();
258 return {};
259 }
260
261 Reference result(ref);
262 result.private_reference = private_ref;
263 return result;
264 }
265
ParseXmlAttributeName(const StringPiece & str)266 Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
267 StringPiece trimmed_str = util::TrimWhitespace(str);
268 const char* start = trimmed_str.data();
269 const char* const end = start + trimmed_str.size();
270 const char* p = start;
271
272 Reference ref;
273 if (p != end && *p == '*') {
274 ref.private_reference = true;
275 start++;
276 p++;
277 }
278
279 StringPiece package;
280 StringPiece name;
281 while (p != end) {
282 if (*p == ':') {
283 package = StringPiece(start, p - start);
284 name = StringPiece(p + 1, end - (p + 1));
285 break;
286 }
287 p++;
288 }
289
290 ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
291 return Maybe<Reference>(std::move(ref));
292 }
293
TryParseReference(const StringPiece & str,bool * out_create)294 std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
295 bool* out_create) {
296 ResourceNameRef ref;
297 bool private_ref = false;
298 if (ParseReference(str, &ref, out_create, &private_ref)) {
299 std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
300 value->private_reference = private_ref;
301 return value;
302 }
303
304 if (ParseAttributeReference(str, &ref)) {
305 if (out_create) {
306 *out_create = false;
307 }
308 return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
309 }
310 return {};
311 }
312
TryParseNullOrEmpty(const StringPiece & str)313 std::unique_ptr<Item> TryParseNullOrEmpty(const StringPiece& str) {
314 const StringPiece trimmed_str(util::TrimWhitespace(str));
315 if (trimmed_str == "@null") {
316 return MakeNull();
317 } else if (trimmed_str == "@empty") {
318 return MakeEmpty();
319 }
320 return {};
321 }
322
MakeNull()323 std::unique_ptr<Reference> MakeNull() {
324 // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
325 // Instead we set the data type to TYPE_REFERENCE with a value of 0.
326 return util::make_unique<Reference>();
327 }
328
MakeEmpty()329 std::unique_ptr<BinaryPrimitive> MakeEmpty() {
330 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_NULL,
331 android::Res_value::DATA_NULL_EMPTY);
332 }
333
TryParseEnumSymbol(const Attribute * enum_attr,const StringPiece & str)334 std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
335 const StringPiece& str) {
336 StringPiece trimmed_str(util::TrimWhitespace(str));
337 for (const Attribute::Symbol& symbol : enum_attr->symbols) {
338 // Enum symbols are stored as @package:id/symbol resources,
339 // so we need to match against the 'entry' part of the identifier.
340 const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
341 if (trimmed_str == enum_symbol_resource_name.entry) {
342 android::Res_value value = {};
343 value.dataType = android::Res_value::TYPE_INT_DEC;
344 value.data = symbol.value;
345 return util::make_unique<BinaryPrimitive>(value);
346 }
347 }
348 return {};
349 }
350
TryParseFlagSymbol(const Attribute * flag_attr,const StringPiece & str)351 std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
352 const StringPiece& str) {
353 android::Res_value flags = {};
354 flags.dataType = android::Res_value::TYPE_INT_HEX;
355 flags.data = 0u;
356
357 if (util::TrimWhitespace(str).empty()) {
358 // Empty string is a valid flag (0).
359 return util::make_unique<BinaryPrimitive>(flags);
360 }
361
362 for (StringPiece part : util::Tokenize(str, '|')) {
363 StringPiece trimmed_part = util::TrimWhitespace(part);
364
365 bool flag_set = false;
366 for (const Attribute::Symbol& symbol : flag_attr->symbols) {
367 // Flag symbols are stored as @package:id/symbol resources,
368 // so we need to match against the 'entry' part of the identifier.
369 const ResourceName& flag_symbol_resource_name =
370 symbol.symbol.name.value();
371 if (trimmed_part == flag_symbol_resource_name.entry) {
372 flags.data |= symbol.value;
373 flag_set = true;
374 break;
375 }
376 }
377
378 if (!flag_set) {
379 return {};
380 }
381 }
382 return util::make_unique<BinaryPrimitive>(flags);
383 }
384
ParseHex(char c,bool * out_error)385 static uint32_t ParseHex(char c, bool* out_error) {
386 if (c >= '0' && c <= '9') {
387 return c - '0';
388 } else if (c >= 'a' && c <= 'f') {
389 return c - 'a' + 0xa;
390 } else if (c >= 'A' && c <= 'F') {
391 return c - 'A' + 0xa;
392 } else {
393 *out_error = true;
394 return 0xffffffffu;
395 }
396 }
397
TryParseColor(const StringPiece & str)398 std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
399 StringPiece color_str(util::TrimWhitespace(str));
400 const char* start = color_str.data();
401 const size_t len = color_str.size();
402 if (len == 0 || start[0] != '#') {
403 return {};
404 }
405
406 android::Res_value value = {};
407 bool error = false;
408 if (len == 4) {
409 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
410 value.data = 0xff000000u;
411 value.data |= ParseHex(start[1], &error) << 20;
412 value.data |= ParseHex(start[1], &error) << 16;
413 value.data |= ParseHex(start[2], &error) << 12;
414 value.data |= ParseHex(start[2], &error) << 8;
415 value.data |= ParseHex(start[3], &error) << 4;
416 value.data |= ParseHex(start[3], &error);
417 } else if (len == 5) {
418 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
419 value.data |= ParseHex(start[1], &error) << 28;
420 value.data |= ParseHex(start[1], &error) << 24;
421 value.data |= ParseHex(start[2], &error) << 20;
422 value.data |= ParseHex(start[2], &error) << 16;
423 value.data |= ParseHex(start[3], &error) << 12;
424 value.data |= ParseHex(start[3], &error) << 8;
425 value.data |= ParseHex(start[4], &error) << 4;
426 value.data |= ParseHex(start[4], &error);
427 } else if (len == 7) {
428 value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
429 value.data = 0xff000000u;
430 value.data |= ParseHex(start[1], &error) << 20;
431 value.data |= ParseHex(start[2], &error) << 16;
432 value.data |= ParseHex(start[3], &error) << 12;
433 value.data |= ParseHex(start[4], &error) << 8;
434 value.data |= ParseHex(start[5], &error) << 4;
435 value.data |= ParseHex(start[6], &error);
436 } else if (len == 9) {
437 value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
438 value.data |= ParseHex(start[1], &error) << 28;
439 value.data |= ParseHex(start[2], &error) << 24;
440 value.data |= ParseHex(start[3], &error) << 20;
441 value.data |= ParseHex(start[4], &error) << 16;
442 value.data |= ParseHex(start[5], &error) << 12;
443 value.data |= ParseHex(start[6], &error) << 8;
444 value.data |= ParseHex(start[7], &error) << 4;
445 value.data |= ParseHex(start[8], &error);
446 } else {
447 return {};
448 }
449 return error ? std::unique_ptr<BinaryPrimitive>()
450 : util::make_unique<BinaryPrimitive>(value);
451 }
452
ParseBool(const StringPiece & str)453 Maybe<bool> ParseBool(const StringPiece& str) {
454 StringPiece trimmed_str(util::TrimWhitespace(str));
455 if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
456 return Maybe<bool>(true);
457 } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
458 trimmed_str == "False") {
459 return Maybe<bool>(false);
460 }
461 return {};
462 }
463
ParseInt(const StringPiece & str)464 Maybe<uint32_t> ParseInt(const StringPiece& str) {
465 std::u16string str16 = util::Utf8ToUtf16(str);
466 android::Res_value value;
467 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
468 return value.data;
469 }
470 return {};
471 }
472
ParseResourceId(const StringPiece & str)473 Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
474 StringPiece trimmed_str(util::TrimWhitespace(str));
475
476 std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
477 android::Res_value value;
478 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
479 if (value.dataType == android::Res_value::TYPE_INT_HEX) {
480 ResourceId id(value.data);
481 if (id.is_valid_dynamic()) {
482 return id;
483 }
484 }
485 }
486 return {};
487 }
488
ParseSdkVersion(const StringPiece & str)489 Maybe<int> ParseSdkVersion(const StringPiece& str) {
490 StringPiece trimmed_str(util::TrimWhitespace(str));
491
492 std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
493 android::Res_value value;
494 if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
495 return static_cast<int>(value.data);
496 }
497
498 // Try parsing the code name.
499 std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
500 if (entry.first == trimmed_str) {
501 return entry.second;
502 }
503 return {};
504 }
505
TryParseBool(const StringPiece & str)506 std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
507 if (Maybe<bool> maybe_result = ParseBool(str)) {
508 const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
509 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
510 }
511 return {};
512 }
513
MakeBool(bool val)514 std::unique_ptr<BinaryPrimitive> MakeBool(bool val) {
515 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN,
516 val ? 0xffffffffu : 0u);
517 }
518
TryParseInt(const StringPiece & str)519 std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
520 std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
521 android::Res_value value;
522 if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
523 return {};
524 }
525 return util::make_unique<BinaryPrimitive>(value);
526 }
527
MakeInt(uint32_t val)528 std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t val) {
529 return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, val);
530 }
531
TryParseFloat(const StringPiece & str)532 std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
533 std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
534 android::Res_value value;
535 if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
536 return {};
537 }
538 return util::make_unique<BinaryPrimitive>(value);
539 }
540
AndroidTypeToAttributeTypeMask(uint16_t type)541 uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
542 switch (type) {
543 case android::Res_value::TYPE_NULL:
544 case android::Res_value::TYPE_REFERENCE:
545 case android::Res_value::TYPE_ATTRIBUTE:
546 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
547 case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE:
548 return android::ResTable_map::TYPE_REFERENCE;
549
550 case android::Res_value::TYPE_STRING:
551 return android::ResTable_map::TYPE_STRING;
552
553 case android::Res_value::TYPE_FLOAT:
554 return android::ResTable_map::TYPE_FLOAT;
555
556 case android::Res_value::TYPE_DIMENSION:
557 return android::ResTable_map::TYPE_DIMENSION;
558
559 case android::Res_value::TYPE_FRACTION:
560 return android::ResTable_map::TYPE_FRACTION;
561
562 case android::Res_value::TYPE_INT_DEC:
563 case android::Res_value::TYPE_INT_HEX:
564 return android::ResTable_map::TYPE_INTEGER |
565 android::ResTable_map::TYPE_ENUM |
566 android::ResTable_map::TYPE_FLAGS;
567
568 case android::Res_value::TYPE_INT_BOOLEAN:
569 return android::ResTable_map::TYPE_BOOLEAN;
570
571 case android::Res_value::TYPE_INT_COLOR_ARGB8:
572 case android::Res_value::TYPE_INT_COLOR_RGB8:
573 case android::Res_value::TYPE_INT_COLOR_ARGB4:
574 case android::Res_value::TYPE_INT_COLOR_RGB4:
575 return android::ResTable_map::TYPE_COLOR;
576
577 default:
578 return 0;
579 };
580 }
581
TryParseItemForAttribute(const StringPiece & value,uint32_t type_mask,const std::function<void (const ResourceName &)> & on_create_reference)582 std::unique_ptr<Item> TryParseItemForAttribute(
583 const StringPiece& value, uint32_t type_mask,
584 const std::function<void(const ResourceName&)>& on_create_reference) {
585 using android::ResTable_map;
586
587 auto null_or_empty = TryParseNullOrEmpty(value);
588 if (null_or_empty) {
589 return null_or_empty;
590 }
591
592 bool create = false;
593 auto reference = TryParseReference(value, &create);
594 if (reference) {
595 if (create && on_create_reference) {
596 on_create_reference(reference->name.value());
597 }
598 return std::move(reference);
599 }
600
601 if (type_mask & ResTable_map::TYPE_COLOR) {
602 // Try parsing this as a color.
603 auto color = TryParseColor(value);
604 if (color) {
605 return std::move(color);
606 }
607 }
608
609 if (type_mask & ResTable_map::TYPE_BOOLEAN) {
610 // Try parsing this as a boolean.
611 auto boolean = TryParseBool(value);
612 if (boolean) {
613 return std::move(boolean);
614 }
615 }
616
617 if (type_mask & ResTable_map::TYPE_INTEGER) {
618 // Try parsing this as an integer.
619 auto integer = TryParseInt(value);
620 if (integer) {
621 return std::move(integer);
622 }
623 }
624
625 const uint32_t float_mask =
626 ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION;
627 if (type_mask & float_mask) {
628 // Try parsing this as a float.
629 auto floating_point = TryParseFloat(value);
630 if (floating_point) {
631 if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
632 return std::move(floating_point);
633 }
634 }
635 }
636 return {};
637 }
638
639 /**
640 * We successively try to parse the string as a resource type that the Attribute
641 * allows.
642 */
TryParseItemForAttribute(const StringPiece & str,const Attribute * attr,const std::function<void (const ResourceName &)> & on_create_reference)643 std::unique_ptr<Item> TryParseItemForAttribute(
644 const StringPiece& str, const Attribute* attr,
645 const std::function<void(const ResourceName&)>& on_create_reference) {
646 using android::ResTable_map;
647
648 const uint32_t type_mask = attr->type_mask;
649 auto value = TryParseItemForAttribute(str, type_mask, on_create_reference);
650 if (value) {
651 return value;
652 }
653
654 if (type_mask & ResTable_map::TYPE_ENUM) {
655 // Try parsing this as an enum.
656 auto enum_value = TryParseEnumSymbol(attr, str);
657 if (enum_value) {
658 return std::move(enum_value);
659 }
660 }
661
662 if (type_mask & ResTable_map::TYPE_FLAGS) {
663 // Try parsing this as a flag.
664 auto flag_value = TryParseFlagSymbol(attr, str);
665 if (flag_value) {
666 return std::move(flag_value);
667 }
668 }
669 return {};
670 }
671
BuildResourceFileName(const ResourceFile & res_file,const NameMangler * mangler)672 std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) {
673 std::stringstream out;
674 out << "res/" << res_file.name.type;
675 if (res_file.config != ConfigDescription{}) {
676 out << "-" << res_file.config;
677 }
678 out << "/";
679
680 if (mangler && mangler->ShouldMangle(res_file.name.package)) {
681 out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
682 } else {
683 out << res_file.name.entry;
684 }
685 out << file::GetExtension(res_file.source.path);
686 return out.str();
687 }
688
ParseBinaryResValue(const ResourceType & type,const ConfigDescription & config,const android::ResStringPool & src_pool,const android::Res_value & res_value,StringPool * dst_pool)689 std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config,
690 const android::ResStringPool& src_pool,
691 const android::Res_value& res_value,
692 StringPool* dst_pool) {
693 if (type == ResourceType::kId) {
694 return util::make_unique<Id>();
695 }
696
697 const uint32_t data = util::DeviceToHost32(res_value.data);
698 switch (res_value.dataType) {
699 case android::Res_value::TYPE_STRING: {
700 const std::string str = util::GetString(src_pool, data);
701 const android::ResStringPool_span* spans = src_pool.styleAt(data);
702
703 // Check if the string has a valid style associated with it.
704 if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
705 StyleString style_str = {str};
706 while (spans->name.index != android::ResStringPool_span::END) {
707 style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
708 spans->firstChar, spans->lastChar});
709 spans++;
710 }
711 return util::make_unique<StyledString>(dst_pool->MakeRef(
712 style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
713 } else {
714 if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
715 // This must be a FileReference.
716 std::unique_ptr<FileReference> file_ref =
717 util::make_unique<FileReference>(dst_pool->MakeRef(
718 str, StringPool::Context(StringPool::Context::kHighPriority, config)));
719 if (type == ResourceType::kRaw) {
720 file_ref->type = ResourceFile::Type::kUnknown;
721 } else if (util::EndsWith(*file_ref->path, ".xml")) {
722 file_ref->type = ResourceFile::Type::kBinaryXml;
723 } else if (util::EndsWith(*file_ref->path, ".png")) {
724 file_ref->type = ResourceFile::Type::kPng;
725 }
726 return std::move(file_ref);
727 }
728
729 // There are no styles associated with this string, so treat it as a simple string.
730 return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
731 }
732 } break;
733
734 case android::Res_value::TYPE_REFERENCE:
735 case android::Res_value::TYPE_ATTRIBUTE:
736 case android::Res_value::TYPE_DYNAMIC_REFERENCE:
737 case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
738 Reference::Type ref_type = Reference::Type::kResource;
739 if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE ||
740 res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
741 ref_type = Reference::Type::kAttribute;
742 }
743
744 if (data == 0u) {
745 // A reference of 0, must be the magic @null reference.
746 return util::make_unique<Reference>();
747 }
748
749 // This is a normal reference.
750 return util::make_unique<Reference>(data, ref_type);
751 } break;
752 }
753
754 // Treat this as a raw binary primitive.
755 return util::make_unique<BinaryPrimitive>(res_value);
756 }
757
758 // Converts the codepoint to UTF-8 and appends it to the string.
AppendCodepointToUtf8String(char32_t codepoint,std::string * output)759 static bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
760 ssize_t len = utf32_to_utf8_length(&codepoint, 1);
761 if (len < 0) {
762 return false;
763 }
764
765 const size_t start_append_pos = output->size();
766
767 // Make room for the next character.
768 output->resize(output->size() + len);
769
770 char* dst = &*(output->begin() + start_append_pos);
771 utf32_to_utf8(&codepoint, 1, dst, len + 1);
772 return true;
773 }
774
775 // Reads up to 4 UTF-8 characters that represent a Unicode escape sequence, and appends the
776 // Unicode codepoint represented by the escape sequence to the string.
AppendUnicodeEscapeSequence(Utf8Iterator * iter,std::string * output)777 static bool AppendUnicodeEscapeSequence(Utf8Iterator* iter, std::string* output) {
778 char32_t code = 0;
779 for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
780 char32_t codepoint = iter->Next();
781 char32_t a;
782 if (codepoint >= U'0' && codepoint <= U'9') {
783 a = codepoint - U'0';
784 } else if (codepoint >= U'a' && codepoint <= U'f') {
785 a = codepoint - U'a' + 10;
786 } else if (codepoint >= U'A' && codepoint <= U'F') {
787 a = codepoint - U'A' + 10;
788 } else {
789 return {};
790 }
791 code = (code << 4) | a;
792 }
793 return AppendCodepointToUtf8String(code, output);
794 }
795
StringBuilder(bool preserve_spaces)796 StringBuilder::StringBuilder(bool preserve_spaces)
797 : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
798 }
799
AppendText(const std::string & text)800 StringBuilder& StringBuilder::AppendText(const std::string& text) {
801 if (!error_.empty()) {
802 return *this;
803 }
804
805 const size_t previous_len = xml_string_.text.size();
806 Utf8Iterator iter(text);
807 while (iter.HasNext()) {
808 char32_t codepoint = iter.Next();
809 if (!quote_ && iswspace(codepoint)) {
810 if (!last_codepoint_was_space_) {
811 // Emit a space if it's the first.
812 xml_string_.text += ' ';
813 last_codepoint_was_space_ = true;
814 }
815
816 // Keep eating spaces.
817 continue;
818 }
819
820 // This is not a space.
821 last_codepoint_was_space_ = false;
822
823 if (codepoint == U'\\') {
824 if (iter.HasNext()) {
825 codepoint = iter.Next();
826 switch (codepoint) {
827 case U't':
828 xml_string_.text += '\t';
829 break;
830
831 case U'n':
832 xml_string_.text += '\n';
833 break;
834
835 case U'#':
836 case U'@':
837 case U'?':
838 case U'"':
839 case U'\'':
840 case U'\\':
841 xml_string_.text += static_cast<char>(codepoint);
842 break;
843
844 case U'u':
845 if (!AppendUnicodeEscapeSequence(&iter, &xml_string_.text)) {
846 error_ =
847 StringPrintf("invalid unicode escape sequence in string\n\"%s\"", text.c_str());
848 return *this;
849 }
850 break;
851
852 default:
853 // Ignore the escape character and just include the codepoint.
854 AppendCodepointToUtf8String(codepoint, &xml_string_.text);
855 break;
856 }
857 }
858 } else if (!preserve_spaces_ && codepoint == U'"') {
859 // Only toggle the quote state when we are not preserving spaces.
860 quote_ = !quote_;
861
862 } else if (!quote_ && codepoint == U'\'') {
863 // This should be escaped.
864 error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
865 return *this;
866
867 } else {
868 AppendCodepointToUtf8String(codepoint, &xml_string_.text);
869 }
870 }
871
872 // Accumulate the added string's UTF-16 length.
873 const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(xml_string_.text.c_str());
874 const size_t utf8_length = xml_string_.text.size();
875 ssize_t len = utf8_to_utf16_length(utf8_data + previous_len, utf8_length - previous_len);
876 if (len < 0) {
877 error_ = StringPrintf("invalid unicode code point in string\n\"%s\"", utf8_data + previous_len);
878 return *this;
879 }
880
881 utf16_len_ += static_cast<uint32_t>(len);
882 return *this;
883 }
884
StartSpan(const std::string & name)885 StringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) {
886 if (!error_.empty()) {
887 return 0u;
888 }
889
890 // When we start a span, all state associated with whitespace truncation and quotation is ended.
891 ResetTextState();
892 Span span;
893 span.name = name;
894 span.first_char = span.last_char = utf16_len_;
895 xml_string_.spans.push_back(std::move(span));
896 return xml_string_.spans.size() - 1;
897 }
898
EndSpan(SpanHandle handle)899 void StringBuilder::EndSpan(SpanHandle handle) {
900 if (!error_.empty()) {
901 return;
902 }
903
904 // When we end a span, all state associated with whitespace truncation and quotation is ended.
905 ResetTextState();
906 xml_string_.spans[handle].last_char = utf16_len_ - 1u;
907 }
908
StartUntranslatable()909 StringBuilder::UntranslatableHandle StringBuilder::StartUntranslatable() {
910 if (!error_.empty()) {
911 return 0u;
912 }
913
914 UntranslatableSection section;
915 section.start = section.end = xml_string_.text.size();
916 xml_string_.untranslatable_sections.push_back(section);
917 return xml_string_.untranslatable_sections.size() - 1;
918 }
919
EndUntranslatable(UntranslatableHandle handle)920 void StringBuilder::EndUntranslatable(UntranslatableHandle handle) {
921 if (!error_.empty()) {
922 return;
923 }
924 xml_string_.untranslatable_sections[handle].end = xml_string_.text.size();
925 }
926
GetFlattenedString() const927 FlattenedXmlString StringBuilder::GetFlattenedString() const {
928 return xml_string_;
929 }
930
to_string() const931 std::string StringBuilder::to_string() const {
932 return xml_string_.text;
933 }
934
operator bool() const935 StringBuilder::operator bool() const {
936 return error_.empty();
937 }
938
GetError() const939 std::string StringBuilder::GetError() const {
940 return error_;
941 }
942
ResetTextState()943 void StringBuilder::ResetTextState() {
944 quote_ = preserve_spaces_;
945 last_codepoint_was_space_ = false;
946 }
947
948 } // namespace ResourceUtils
949 } // namespace aapt
950