xref: /aosp_15_r20/frameworks/base/tools/aapt2/util/Util.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "util/Util.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <algorithm>
20*d57664e9SAndroid Build Coastguard Worker #include <ostream>
21*d57664e9SAndroid Build Coastguard Worker #include <string>
22*d57664e9SAndroid Build Coastguard Worker #include <vector>
23*d57664e9SAndroid Build Coastguard Worker 
24*d57664e9SAndroid Build Coastguard Worker #include "android-base/parseint.h"
25*d57664e9SAndroid Build Coastguard Worker #include "android-base/stringprintf.h"
26*d57664e9SAndroid Build Coastguard Worker #include "android-base/strings.h"
27*d57664e9SAndroid Build Coastguard Worker #include "androidfw/BigBuffer.h"
28*d57664e9SAndroid Build Coastguard Worker #include "androidfw/StringPiece.h"
29*d57664e9SAndroid Build Coastguard Worker #include "androidfw/Util.h"
30*d57664e9SAndroid Build Coastguard Worker #include "build/version.h"
31*d57664e9SAndroid Build Coastguard Worker #include "text/Unicode.h"
32*d57664e9SAndroid Build Coastguard Worker #include "text/Utf8Iterator.h"
33*d57664e9SAndroid Build Coastguard Worker #include "utils/Unicode.h"
34*d57664e9SAndroid Build Coastguard Worker 
35*d57664e9SAndroid Build Coastguard Worker using ::aapt::text::Utf8Iterator;
36*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece;
37*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece16;
38*d57664e9SAndroid Build Coastguard Worker 
39*d57664e9SAndroid Build Coastguard Worker namespace aapt {
40*d57664e9SAndroid Build Coastguard Worker namespace util {
41*d57664e9SAndroid Build Coastguard Worker 
42*d57664e9SAndroid Build Coastguard Worker // Package name and shared user id would be used as a part of the file name.
43*d57664e9SAndroid Build Coastguard Worker // Limits size to 223 and reserves 32 for the OS.
44*d57664e9SAndroid Build Coastguard Worker // See frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java
45*d57664e9SAndroid Build Coastguard Worker constexpr static const size_t kMaxPackageNameSize = 223;
46*d57664e9SAndroid Build Coastguard Worker 
SplitAndTransform(StringPiece str,char sep,char (* f)(char))47*d57664e9SAndroid Build Coastguard Worker static std::vector<std::string> SplitAndTransform(StringPiece str, char sep, char (*f)(char)) {
48*d57664e9SAndroid Build Coastguard Worker   std::vector<std::string> parts;
49*d57664e9SAndroid Build Coastguard Worker   const StringPiece::const_iterator end = std::end(str);
50*d57664e9SAndroid Build Coastguard Worker   StringPiece::const_iterator start = std::begin(str);
51*d57664e9SAndroid Build Coastguard Worker   StringPiece::const_iterator current;
52*d57664e9SAndroid Build Coastguard Worker   do {
53*d57664e9SAndroid Build Coastguard Worker     current = std::find(start, end, sep);
54*d57664e9SAndroid Build Coastguard Worker     parts.emplace_back(start, current);
55*d57664e9SAndroid Build Coastguard Worker     if (f) {
56*d57664e9SAndroid Build Coastguard Worker       std::string& part = parts.back();
57*d57664e9SAndroid Build Coastguard Worker       std::transform(part.begin(), part.end(), part.begin(), f);
58*d57664e9SAndroid Build Coastguard Worker     }
59*d57664e9SAndroid Build Coastguard Worker     start = current + 1;
60*d57664e9SAndroid Build Coastguard Worker   } while (current != end);
61*d57664e9SAndroid Build Coastguard Worker   return parts;
62*d57664e9SAndroid Build Coastguard Worker }
63*d57664e9SAndroid Build Coastguard Worker 
Split(StringPiece str,char sep)64*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> Split(StringPiece str, char sep) {
65*d57664e9SAndroid Build Coastguard Worker   return SplitAndTransform(str, sep, nullptr);
66*d57664e9SAndroid Build Coastguard Worker }
67*d57664e9SAndroid Build Coastguard Worker 
SplitAndLowercase(StringPiece str,char sep)68*d57664e9SAndroid Build Coastguard Worker std::vector<std::string> SplitAndLowercase(StringPiece str, char sep) {
69*d57664e9SAndroid Build Coastguard Worker   return SplitAndTransform(str, sep, [](char c) -> char { return ::tolower(c); });
70*d57664e9SAndroid Build Coastguard Worker }
71*d57664e9SAndroid Build Coastguard Worker 
StartsWith(StringPiece str,StringPiece prefix)72*d57664e9SAndroid Build Coastguard Worker bool StartsWith(StringPiece str, StringPiece prefix) {
73*d57664e9SAndroid Build Coastguard Worker   if (str.size() < prefix.size()) {
74*d57664e9SAndroid Build Coastguard Worker     return false;
75*d57664e9SAndroid Build Coastguard Worker   }
76*d57664e9SAndroid Build Coastguard Worker   return str.substr(0, prefix.size()) == prefix;
77*d57664e9SAndroid Build Coastguard Worker }
78*d57664e9SAndroid Build Coastguard Worker 
EndsWith(StringPiece str,StringPiece suffix)79*d57664e9SAndroid Build Coastguard Worker bool EndsWith(StringPiece str, StringPiece suffix) {
80*d57664e9SAndroid Build Coastguard Worker   if (str.size() < suffix.size()) {
81*d57664e9SAndroid Build Coastguard Worker     return false;
82*d57664e9SAndroid Build Coastguard Worker   }
83*d57664e9SAndroid Build Coastguard Worker   return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
84*d57664e9SAndroid Build Coastguard Worker }
85*d57664e9SAndroid Build Coastguard Worker 
TrimLeadingWhitespace(StringPiece str)86*d57664e9SAndroid Build Coastguard Worker StringPiece TrimLeadingWhitespace(StringPiece str) {
87*d57664e9SAndroid Build Coastguard Worker   if (str.size() == 0 || str.data() == nullptr) {
88*d57664e9SAndroid Build Coastguard Worker     return str;
89*d57664e9SAndroid Build Coastguard Worker   }
90*d57664e9SAndroid Build Coastguard Worker 
91*d57664e9SAndroid Build Coastguard Worker   const char* start = str.data();
92*d57664e9SAndroid Build Coastguard Worker   const char* end = start + str.length();
93*d57664e9SAndroid Build Coastguard Worker 
94*d57664e9SAndroid Build Coastguard Worker   while (start != end && isspace(*start)) {
95*d57664e9SAndroid Build Coastguard Worker     start++;
96*d57664e9SAndroid Build Coastguard Worker   }
97*d57664e9SAndroid Build Coastguard Worker   return StringPiece(start, end - start);
98*d57664e9SAndroid Build Coastguard Worker }
99*d57664e9SAndroid Build Coastguard Worker 
TrimTrailingWhitespace(StringPiece str)100*d57664e9SAndroid Build Coastguard Worker StringPiece TrimTrailingWhitespace(StringPiece str) {
101*d57664e9SAndroid Build Coastguard Worker   if (str.size() == 0 || str.data() == nullptr) {
102*d57664e9SAndroid Build Coastguard Worker     return str;
103*d57664e9SAndroid Build Coastguard Worker   }
104*d57664e9SAndroid Build Coastguard Worker 
105*d57664e9SAndroid Build Coastguard Worker   const char* start = str.data();
106*d57664e9SAndroid Build Coastguard Worker   const char* end = start + str.length();
107*d57664e9SAndroid Build Coastguard Worker 
108*d57664e9SAndroid Build Coastguard Worker   while (end != start && isspace(*(end - 1))) {
109*d57664e9SAndroid Build Coastguard Worker     end--;
110*d57664e9SAndroid Build Coastguard Worker   }
111*d57664e9SAndroid Build Coastguard Worker   return StringPiece(start, end - start);
112*d57664e9SAndroid Build Coastguard Worker }
113*d57664e9SAndroid Build Coastguard Worker 
TrimWhitespace(StringPiece str)114*d57664e9SAndroid Build Coastguard Worker StringPiece TrimWhitespace(StringPiece str) {
115*d57664e9SAndroid Build Coastguard Worker   if (str.size() == 0 || str.data() == nullptr) {
116*d57664e9SAndroid Build Coastguard Worker     return str;
117*d57664e9SAndroid Build Coastguard Worker   }
118*d57664e9SAndroid Build Coastguard Worker 
119*d57664e9SAndroid Build Coastguard Worker   const char* start = str.data();
120*d57664e9SAndroid Build Coastguard Worker   const char* end = str.data() + str.length();
121*d57664e9SAndroid Build Coastguard Worker 
122*d57664e9SAndroid Build Coastguard Worker   while (start != end && isspace(*start)) {
123*d57664e9SAndroid Build Coastguard Worker     start++;
124*d57664e9SAndroid Build Coastguard Worker   }
125*d57664e9SAndroid Build Coastguard Worker 
126*d57664e9SAndroid Build Coastguard Worker   while (end != start && isspace(*(end - 1))) {
127*d57664e9SAndroid Build Coastguard Worker     end--;
128*d57664e9SAndroid Build Coastguard Worker   }
129*d57664e9SAndroid Build Coastguard Worker 
130*d57664e9SAndroid Build Coastguard Worker   return StringPiece(start, end - start);
131*d57664e9SAndroid Build Coastguard Worker }
132*d57664e9SAndroid Build Coastguard Worker 
IsJavaNameImpl(StringPiece str)133*d57664e9SAndroid Build Coastguard Worker static int IsJavaNameImpl(StringPiece str) {
134*d57664e9SAndroid Build Coastguard Worker   int pieces = 0;
135*d57664e9SAndroid Build Coastguard Worker   for (StringPiece piece : Tokenize(str, '.')) {
136*d57664e9SAndroid Build Coastguard Worker     pieces++;
137*d57664e9SAndroid Build Coastguard Worker     if (!text::IsJavaIdentifier(piece)) {
138*d57664e9SAndroid Build Coastguard Worker       return -1;
139*d57664e9SAndroid Build Coastguard Worker     }
140*d57664e9SAndroid Build Coastguard Worker   }
141*d57664e9SAndroid Build Coastguard Worker   return pieces;
142*d57664e9SAndroid Build Coastguard Worker }
143*d57664e9SAndroid Build Coastguard Worker 
IsJavaClassName(StringPiece str)144*d57664e9SAndroid Build Coastguard Worker bool IsJavaClassName(StringPiece str) {
145*d57664e9SAndroid Build Coastguard Worker   return IsJavaNameImpl(str) >= 2;
146*d57664e9SAndroid Build Coastguard Worker }
147*d57664e9SAndroid Build Coastguard Worker 
IsJavaPackageName(StringPiece str)148*d57664e9SAndroid Build Coastguard Worker bool IsJavaPackageName(StringPiece str) {
149*d57664e9SAndroid Build Coastguard Worker   return IsJavaNameImpl(str) >= 1;
150*d57664e9SAndroid Build Coastguard Worker }
151*d57664e9SAndroid Build Coastguard Worker 
IsAndroidNameImpl(StringPiece str)152*d57664e9SAndroid Build Coastguard Worker static int IsAndroidNameImpl(StringPiece str) {
153*d57664e9SAndroid Build Coastguard Worker   int pieces = 0;
154*d57664e9SAndroid Build Coastguard Worker   for (StringPiece piece : Tokenize(str, '.')) {
155*d57664e9SAndroid Build Coastguard Worker     if (piece.empty()) {
156*d57664e9SAndroid Build Coastguard Worker       return -1;
157*d57664e9SAndroid Build Coastguard Worker     }
158*d57664e9SAndroid Build Coastguard Worker 
159*d57664e9SAndroid Build Coastguard Worker     const char first_character = piece.data()[0];
160*d57664e9SAndroid Build Coastguard Worker     if (!::isalpha(first_character)) {
161*d57664e9SAndroid Build Coastguard Worker       return -1;
162*d57664e9SAndroid Build Coastguard Worker     }
163*d57664e9SAndroid Build Coastguard Worker 
164*d57664e9SAndroid Build Coastguard Worker     bool valid = std::all_of(piece.begin() + 1, piece.end(), [](const char c) -> bool {
165*d57664e9SAndroid Build Coastguard Worker       return ::isalnum(c) || c == '_';
166*d57664e9SAndroid Build Coastguard Worker     });
167*d57664e9SAndroid Build Coastguard Worker 
168*d57664e9SAndroid Build Coastguard Worker     if (!valid) {
169*d57664e9SAndroid Build Coastguard Worker       return -1;
170*d57664e9SAndroid Build Coastguard Worker     }
171*d57664e9SAndroid Build Coastguard Worker     pieces++;
172*d57664e9SAndroid Build Coastguard Worker   }
173*d57664e9SAndroid Build Coastguard Worker   return pieces;
174*d57664e9SAndroid Build Coastguard Worker }
175*d57664e9SAndroid Build Coastguard Worker 
IsAndroidPackageName(StringPiece str)176*d57664e9SAndroid Build Coastguard Worker bool IsAndroidPackageName(StringPiece str) {
177*d57664e9SAndroid Build Coastguard Worker   if (str.size() > kMaxPackageNameSize) {
178*d57664e9SAndroid Build Coastguard Worker     return false;
179*d57664e9SAndroid Build Coastguard Worker   }
180*d57664e9SAndroid Build Coastguard Worker   return IsAndroidNameImpl(str) > 1 || str == "android";
181*d57664e9SAndroid Build Coastguard Worker }
182*d57664e9SAndroid Build Coastguard Worker 
IsAndroidSharedUserId(android::StringPiece package_name,android::StringPiece shared_user_id)183*d57664e9SAndroid Build Coastguard Worker bool IsAndroidSharedUserId(android::StringPiece package_name, android::StringPiece shared_user_id) {
184*d57664e9SAndroid Build Coastguard Worker   if (shared_user_id.size() > kMaxPackageNameSize) {
185*d57664e9SAndroid Build Coastguard Worker     return false;
186*d57664e9SAndroid Build Coastguard Worker   }
187*d57664e9SAndroid Build Coastguard Worker   return shared_user_id.empty() || IsAndroidNameImpl(shared_user_id) > 1 ||
188*d57664e9SAndroid Build Coastguard Worker          package_name == "android";
189*d57664e9SAndroid Build Coastguard Worker }
190*d57664e9SAndroid Build Coastguard Worker 
IsAndroidSplitName(StringPiece str)191*d57664e9SAndroid Build Coastguard Worker bool IsAndroidSplitName(StringPiece str) {
192*d57664e9SAndroid Build Coastguard Worker   return IsAndroidNameImpl(str) > 0;
193*d57664e9SAndroid Build Coastguard Worker }
194*d57664e9SAndroid Build Coastguard Worker 
GetFullyQualifiedClassName(StringPiece package,StringPiece classname)195*d57664e9SAndroid Build Coastguard Worker std::optional<std::string> GetFullyQualifiedClassName(StringPiece package, StringPiece classname) {
196*d57664e9SAndroid Build Coastguard Worker   if (classname.empty()) {
197*d57664e9SAndroid Build Coastguard Worker     return {};
198*d57664e9SAndroid Build Coastguard Worker   }
199*d57664e9SAndroid Build Coastguard Worker 
200*d57664e9SAndroid Build Coastguard Worker   if (util::IsJavaClassName(classname)) {
201*d57664e9SAndroid Build Coastguard Worker     return std::string(classname);
202*d57664e9SAndroid Build Coastguard Worker   }
203*d57664e9SAndroid Build Coastguard Worker 
204*d57664e9SAndroid Build Coastguard Worker   if (package.empty()) {
205*d57664e9SAndroid Build Coastguard Worker     return {};
206*d57664e9SAndroid Build Coastguard Worker   }
207*d57664e9SAndroid Build Coastguard Worker 
208*d57664e9SAndroid Build Coastguard Worker   std::string result{package};
209*d57664e9SAndroid Build Coastguard Worker   if (classname.data()[0] != '.') {
210*d57664e9SAndroid Build Coastguard Worker     result += '.';
211*d57664e9SAndroid Build Coastguard Worker   }
212*d57664e9SAndroid Build Coastguard Worker 
213*d57664e9SAndroid Build Coastguard Worker   result.append(classname.data(), classname.size());
214*d57664e9SAndroid Build Coastguard Worker   if (!IsJavaClassName(result)) {
215*d57664e9SAndroid Build Coastguard Worker     return {};
216*d57664e9SAndroid Build Coastguard Worker   }
217*d57664e9SAndroid Build Coastguard Worker   return result;
218*d57664e9SAndroid Build Coastguard Worker }
219*d57664e9SAndroid Build Coastguard Worker 
GetToolName()220*d57664e9SAndroid Build Coastguard Worker const char* GetToolName() {
221*d57664e9SAndroid Build Coastguard Worker   static const char* const sToolName = "Android Asset Packaging Tool (aapt)";
222*d57664e9SAndroid Build Coastguard Worker   return sToolName;
223*d57664e9SAndroid Build Coastguard Worker }
224*d57664e9SAndroid Build Coastguard Worker 
GetToolFingerprint()225*d57664e9SAndroid Build Coastguard Worker std::string GetToolFingerprint() {
226*d57664e9SAndroid Build Coastguard Worker   // DO NOT UPDATE, this is more of a marketing version.
227*d57664e9SAndroid Build Coastguard Worker   static const char* const sMajorVersion = "2";
228*d57664e9SAndroid Build Coastguard Worker 
229*d57664e9SAndroid Build Coastguard Worker   // Update minor version whenever a feature or flag is added.
230*d57664e9SAndroid Build Coastguard Worker   static const char* const sMinorVersion = "19";
231*d57664e9SAndroid Build Coastguard Worker 
232*d57664e9SAndroid Build Coastguard Worker   // The build id of aapt2 binary.
233*d57664e9SAndroid Build Coastguard Worker   static const std::string sBuildId = [] {
234*d57664e9SAndroid Build Coastguard Worker     std::string buildNumber = android::build::GetBuildNumber();
235*d57664e9SAndroid Build Coastguard Worker 
236*d57664e9SAndroid Build Coastguard Worker     if (android::base::StartsWith(buildNumber, "eng.")) {
237*d57664e9SAndroid Build Coastguard Worker       // android::build::GetBuildNumber() returns something like "eng.user.20230725.214219" where
238*d57664e9SAndroid Build Coastguard Worker       // the latter two parts are "yyyyMMdd.HHmmss" at build time. Use "yyyyMM" in the fingerprint.
239*d57664e9SAndroid Build Coastguard Worker       std::vector<std::string> parts = util::Split(buildNumber, '.');
240*d57664e9SAndroid Build Coastguard Worker       int buildYear;
241*d57664e9SAndroid Build Coastguard Worker       int buildMonth;
242*d57664e9SAndroid Build Coastguard Worker       if (parts.size() < 3 || parts[2].length() < 6 ||
243*d57664e9SAndroid Build Coastguard Worker           !android::base::ParseInt(parts[2].substr(0, 4), &buildYear) ||
244*d57664e9SAndroid Build Coastguard Worker           !android::base::ParseInt(parts[2].substr(4, 2), &buildMonth)) {
245*d57664e9SAndroid Build Coastguard Worker         // Fallback to localtime() if GetBuildNumber() returns an unexpected output.
246*d57664e9SAndroid Build Coastguard Worker         time_t now = time(0);
247*d57664e9SAndroid Build Coastguard Worker         tm* ltm = localtime(&now);
248*d57664e9SAndroid Build Coastguard Worker         buildYear = 1900 + ltm->tm_year;
249*d57664e9SAndroid Build Coastguard Worker         buildMonth = 1 + ltm->tm_mon;
250*d57664e9SAndroid Build Coastguard Worker       }
251*d57664e9SAndroid Build Coastguard Worker 
252*d57664e9SAndroid Build Coastguard Worker       buildNumber = android::base::StringPrintf("eng.%04d%02d", buildYear, buildMonth);
253*d57664e9SAndroid Build Coastguard Worker     }
254*d57664e9SAndroid Build Coastguard Worker     return buildNumber;
255*d57664e9SAndroid Build Coastguard Worker   }();
256*d57664e9SAndroid Build Coastguard Worker 
257*d57664e9SAndroid Build Coastguard Worker   return android::base::StringPrintf("%s.%s-%s", sMajorVersion, sMinorVersion, sBuildId.c_str());
258*d57664e9SAndroid Build Coastguard Worker }
259*d57664e9SAndroid Build Coastguard Worker 
ConsumeDigits(const char * start,const char * end)260*d57664e9SAndroid Build Coastguard Worker static size_t ConsumeDigits(const char* start, const char* end) {
261*d57664e9SAndroid Build Coastguard Worker   const char* c = start;
262*d57664e9SAndroid Build Coastguard Worker   for (; c != end && *c >= '0' && *c <= '9'; c++) {
263*d57664e9SAndroid Build Coastguard Worker   }
264*d57664e9SAndroid Build Coastguard Worker   return static_cast<size_t>(c - start);
265*d57664e9SAndroid Build Coastguard Worker }
266*d57664e9SAndroid Build Coastguard Worker 
VerifyJavaStringFormat(StringPiece str)267*d57664e9SAndroid Build Coastguard Worker bool VerifyJavaStringFormat(StringPiece str) {
268*d57664e9SAndroid Build Coastguard Worker   const char* c = str.begin();
269*d57664e9SAndroid Build Coastguard Worker   const char* const end = str.end();
270*d57664e9SAndroid Build Coastguard Worker 
271*d57664e9SAndroid Build Coastguard Worker   size_t arg_count = 0;
272*d57664e9SAndroid Build Coastguard Worker   bool nonpositional = false;
273*d57664e9SAndroid Build Coastguard Worker   while (c != end) {
274*d57664e9SAndroid Build Coastguard Worker     if (*c == '%' && c + 1 < end) {
275*d57664e9SAndroid Build Coastguard Worker       c++;
276*d57664e9SAndroid Build Coastguard Worker 
277*d57664e9SAndroid Build Coastguard Worker       if (*c == '%' || *c == 'n') {
278*d57664e9SAndroid Build Coastguard Worker         c++;
279*d57664e9SAndroid Build Coastguard Worker         continue;
280*d57664e9SAndroid Build Coastguard Worker       }
281*d57664e9SAndroid Build Coastguard Worker 
282*d57664e9SAndroid Build Coastguard Worker       arg_count++;
283*d57664e9SAndroid Build Coastguard Worker 
284*d57664e9SAndroid Build Coastguard Worker       size_t num_digits = ConsumeDigits(c, end);
285*d57664e9SAndroid Build Coastguard Worker       if (num_digits > 0) {
286*d57664e9SAndroid Build Coastguard Worker         c += num_digits;
287*d57664e9SAndroid Build Coastguard Worker         if (c != end && *c != '$') {
288*d57664e9SAndroid Build Coastguard Worker           // The digits were a size, but not a positional argument.
289*d57664e9SAndroid Build Coastguard Worker           nonpositional = true;
290*d57664e9SAndroid Build Coastguard Worker         }
291*d57664e9SAndroid Build Coastguard Worker       } else if (*c == '<') {
292*d57664e9SAndroid Build Coastguard Worker         // Reusing last argument, bad idea since positions can be moved around
293*d57664e9SAndroid Build Coastguard Worker         // during translation.
294*d57664e9SAndroid Build Coastguard Worker         nonpositional = true;
295*d57664e9SAndroid Build Coastguard Worker 
296*d57664e9SAndroid Build Coastguard Worker         c++;
297*d57664e9SAndroid Build Coastguard Worker 
298*d57664e9SAndroid Build Coastguard Worker         // Optionally we can have a $ after
299*d57664e9SAndroid Build Coastguard Worker         if (c != end && *c == '$') {
300*d57664e9SAndroid Build Coastguard Worker           c++;
301*d57664e9SAndroid Build Coastguard Worker         }
302*d57664e9SAndroid Build Coastguard Worker       } else {
303*d57664e9SAndroid Build Coastguard Worker         nonpositional = true;
304*d57664e9SAndroid Build Coastguard Worker       }
305*d57664e9SAndroid Build Coastguard Worker 
306*d57664e9SAndroid Build Coastguard Worker       // Ignore size, width, flags, etc.
307*d57664e9SAndroid Build Coastguard Worker       while (c != end && (*c == '-' || *c == '#' || *c == '+' || *c == ' ' ||
308*d57664e9SAndroid Build Coastguard Worker                           *c == ',' || *c == '(' || (*c >= '0' && *c <= '9'))) {
309*d57664e9SAndroid Build Coastguard Worker         c++;
310*d57664e9SAndroid Build Coastguard Worker       }
311*d57664e9SAndroid Build Coastguard Worker 
312*d57664e9SAndroid Build Coastguard Worker       /*
313*d57664e9SAndroid Build Coastguard Worker        * This is a shortcut to detect strings that are going to Time.format()
314*d57664e9SAndroid Build Coastguard Worker        * instead of String.format()
315*d57664e9SAndroid Build Coastguard Worker        *
316*d57664e9SAndroid Build Coastguard Worker        * Comparison of String.format() and Time.format() args:
317*d57664e9SAndroid Build Coastguard Worker        *
318*d57664e9SAndroid Build Coastguard Worker        * String: ABC E GH  ST X abcdefgh  nost x
319*d57664e9SAndroid Build Coastguard Worker        *   Time:    DEFGHKMS W Za  d   hkm  s w yz
320*d57664e9SAndroid Build Coastguard Worker        *
321*d57664e9SAndroid Build Coastguard Worker        * Therefore we know it's definitely Time if we have:
322*d57664e9SAndroid Build Coastguard Worker        *     DFKMWZkmwyz
323*d57664e9SAndroid Build Coastguard Worker        */
324*d57664e9SAndroid Build Coastguard Worker       if (c != end) {
325*d57664e9SAndroid Build Coastguard Worker         switch (*c) {
326*d57664e9SAndroid Build Coastguard Worker           case 'D':
327*d57664e9SAndroid Build Coastguard Worker           case 'F':
328*d57664e9SAndroid Build Coastguard Worker           case 'K':
329*d57664e9SAndroid Build Coastguard Worker           case 'M':
330*d57664e9SAndroid Build Coastguard Worker           case 'W':
331*d57664e9SAndroid Build Coastguard Worker           case 'Z':
332*d57664e9SAndroid Build Coastguard Worker           case 'k':
333*d57664e9SAndroid Build Coastguard Worker           case 'm':
334*d57664e9SAndroid Build Coastguard Worker           case 'w':
335*d57664e9SAndroid Build Coastguard Worker           case 'y':
336*d57664e9SAndroid Build Coastguard Worker           case 'z':
337*d57664e9SAndroid Build Coastguard Worker             return true;
338*d57664e9SAndroid Build Coastguard Worker         }
339*d57664e9SAndroid Build Coastguard Worker       }
340*d57664e9SAndroid Build Coastguard Worker     }
341*d57664e9SAndroid Build Coastguard Worker 
342*d57664e9SAndroid Build Coastguard Worker     if (c != end) {
343*d57664e9SAndroid Build Coastguard Worker       c++;
344*d57664e9SAndroid Build Coastguard Worker     }
345*d57664e9SAndroid Build Coastguard Worker   }
346*d57664e9SAndroid Build Coastguard Worker 
347*d57664e9SAndroid Build Coastguard Worker   if (arg_count > 1 && nonpositional) {
348*d57664e9SAndroid Build Coastguard Worker     // Multiple arguments were specified, but some or all were non positional.
349*d57664e9SAndroid Build Coastguard Worker     // Translated
350*d57664e9SAndroid Build Coastguard Worker     // strings may rearrange the order of the arguments, which will break the
351*d57664e9SAndroid Build Coastguard Worker     // string.
352*d57664e9SAndroid Build Coastguard Worker     return false;
353*d57664e9SAndroid Build Coastguard Worker   }
354*d57664e9SAndroid Build Coastguard Worker   return true;
355*d57664e9SAndroid Build Coastguard Worker }
356*d57664e9SAndroid Build Coastguard Worker 
Utf8ToUtf16(StringPiece utf8)357*d57664e9SAndroid Build Coastguard Worker std::u16string Utf8ToUtf16(StringPiece utf8) {
358*d57664e9SAndroid Build Coastguard Worker   ssize_t utf16_length = utf8_to_utf16_length(
359*d57664e9SAndroid Build Coastguard Worker       reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
360*d57664e9SAndroid Build Coastguard Worker   if (utf16_length <= 0) {
361*d57664e9SAndroid Build Coastguard Worker     return {};
362*d57664e9SAndroid Build Coastguard Worker   }
363*d57664e9SAndroid Build Coastguard Worker 
364*d57664e9SAndroid Build Coastguard Worker   std::u16string utf16;
365*d57664e9SAndroid Build Coastguard Worker   utf16.resize(utf16_length);
366*d57664e9SAndroid Build Coastguard Worker   utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(),
367*d57664e9SAndroid Build Coastguard Worker                 &*utf16.begin(), utf16_length + 1);
368*d57664e9SAndroid Build Coastguard Worker   return utf16;
369*d57664e9SAndroid Build Coastguard Worker }
370*d57664e9SAndroid Build Coastguard Worker 
Utf16ToUtf8(const StringPiece16 & utf16)371*d57664e9SAndroid Build Coastguard Worker std::string Utf16ToUtf8(const StringPiece16& utf16) {
372*d57664e9SAndroid Build Coastguard Worker   ssize_t utf8_length = utf16_to_utf8_length(utf16.data(), utf16.length());
373*d57664e9SAndroid Build Coastguard Worker   if (utf8_length <= 0) {
374*d57664e9SAndroid Build Coastguard Worker     return {};
375*d57664e9SAndroid Build Coastguard Worker   }
376*d57664e9SAndroid Build Coastguard Worker 
377*d57664e9SAndroid Build Coastguard Worker   std::string utf8;
378*d57664e9SAndroid Build Coastguard Worker   utf8.resize(utf8_length);
379*d57664e9SAndroid Build Coastguard Worker   utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1);
380*d57664e9SAndroid Build Coastguard Worker   return utf8;
381*d57664e9SAndroid Build Coastguard Worker }
382*d57664e9SAndroid Build Coastguard Worker 
WriteAll(std::ostream & out,const android::BigBuffer & buffer)383*d57664e9SAndroid Build Coastguard Worker bool WriteAll(std::ostream& out, const android::BigBuffer& buffer) {
384*d57664e9SAndroid Build Coastguard Worker   for (const auto& b : buffer) {
385*d57664e9SAndroid Build Coastguard Worker     if (!out.write(reinterpret_cast<const char*>(b.buffer.get()), b.size)) {
386*d57664e9SAndroid Build Coastguard Worker       return false;
387*d57664e9SAndroid Build Coastguard Worker     }
388*d57664e9SAndroid Build Coastguard Worker   }
389*d57664e9SAndroid Build Coastguard Worker   return true;
390*d57664e9SAndroid Build Coastguard Worker }
391*d57664e9SAndroid Build Coastguard Worker 
operator ++()392*d57664e9SAndroid Build Coastguard Worker typename Tokenizer::iterator& Tokenizer::iterator::operator++() {
393*d57664e9SAndroid Build Coastguard Worker   const char* start = token_.end();
394*d57664e9SAndroid Build Coastguard Worker   const char* end = str_.end();
395*d57664e9SAndroid Build Coastguard Worker   if (start == end) {
396*d57664e9SAndroid Build Coastguard Worker     end_ = true;
397*d57664e9SAndroid Build Coastguard Worker     token_ = StringPiece(token_.end(), 0);
398*d57664e9SAndroid Build Coastguard Worker     return *this;
399*d57664e9SAndroid Build Coastguard Worker   }
400*d57664e9SAndroid Build Coastguard Worker 
401*d57664e9SAndroid Build Coastguard Worker   start += 1;
402*d57664e9SAndroid Build Coastguard Worker   const char* current = start;
403*d57664e9SAndroid Build Coastguard Worker   while (current != end) {
404*d57664e9SAndroid Build Coastguard Worker     if (*current == separator_) {
405*d57664e9SAndroid Build Coastguard Worker       token_ = StringPiece(start, current - start);
406*d57664e9SAndroid Build Coastguard Worker       return *this;
407*d57664e9SAndroid Build Coastguard Worker     }
408*d57664e9SAndroid Build Coastguard Worker     ++current;
409*d57664e9SAndroid Build Coastguard Worker   }
410*d57664e9SAndroid Build Coastguard Worker   token_ = StringPiece(start, end - start);
411*d57664e9SAndroid Build Coastguard Worker   return *this;
412*d57664e9SAndroid Build Coastguard Worker }
413*d57664e9SAndroid Build Coastguard Worker 
operator ==(const iterator & rhs) const414*d57664e9SAndroid Build Coastguard Worker bool Tokenizer::iterator::operator==(const iterator& rhs) const {
415*d57664e9SAndroid Build Coastguard Worker   // We check equality here a bit differently.
416*d57664e9SAndroid Build Coastguard Worker   // We need to know that the addresses are the same.
417*d57664e9SAndroid Build Coastguard Worker   return token_.begin() == rhs.token_.begin() &&
418*d57664e9SAndroid Build Coastguard Worker          token_.end() == rhs.token_.end() && end_ == rhs.end_;
419*d57664e9SAndroid Build Coastguard Worker }
420*d57664e9SAndroid Build Coastguard Worker 
operator !=(const iterator & rhs) const421*d57664e9SAndroid Build Coastguard Worker bool Tokenizer::iterator::operator!=(const iterator& rhs) const {
422*d57664e9SAndroid Build Coastguard Worker   return !(*this == rhs);
423*d57664e9SAndroid Build Coastguard Worker }
424*d57664e9SAndroid Build Coastguard Worker 
iterator(StringPiece s,char sep,StringPiece tok,bool end)425*d57664e9SAndroid Build Coastguard Worker Tokenizer::iterator::iterator(StringPiece s, char sep, StringPiece tok, bool end)
426*d57664e9SAndroid Build Coastguard Worker     : str_(s), separator_(sep), token_(tok), end_(end) {
427*d57664e9SAndroid Build Coastguard Worker }
428*d57664e9SAndroid Build Coastguard Worker 
Tokenizer(StringPiece str,char sep)429*d57664e9SAndroid Build Coastguard Worker Tokenizer::Tokenizer(StringPiece str, char sep)
430*d57664e9SAndroid Build Coastguard Worker     : begin_(++iterator(str, sep, StringPiece(str.begin() - 1, 0), false)),
431*d57664e9SAndroid Build Coastguard Worker       end_(str, sep, StringPiece(str.end(), 0), true) {
432*d57664e9SAndroid Build Coastguard Worker }
433*d57664e9SAndroid Build Coastguard Worker 
ExtractResFilePathParts(StringPiece path,StringPiece * out_prefix,StringPiece * out_entry,StringPiece * out_suffix)434*d57664e9SAndroid Build Coastguard Worker bool ExtractResFilePathParts(StringPiece path, StringPiece* out_prefix, StringPiece* out_entry,
435*d57664e9SAndroid Build Coastguard Worker                              StringPiece* out_suffix) {
436*d57664e9SAndroid Build Coastguard Worker   const StringPiece res_prefix("res/");
437*d57664e9SAndroid Build Coastguard Worker   if (!StartsWith(path, res_prefix)) {
438*d57664e9SAndroid Build Coastguard Worker     return false;
439*d57664e9SAndroid Build Coastguard Worker   }
440*d57664e9SAndroid Build Coastguard Worker 
441*d57664e9SAndroid Build Coastguard Worker   StringPiece::const_iterator last_occurence = path.end();
442*d57664e9SAndroid Build Coastguard Worker   for (auto iter = path.begin() + res_prefix.size(); iter != path.end();
443*d57664e9SAndroid Build Coastguard Worker        ++iter) {
444*d57664e9SAndroid Build Coastguard Worker     if (*iter == '/') {
445*d57664e9SAndroid Build Coastguard Worker       last_occurence = iter;
446*d57664e9SAndroid Build Coastguard Worker     }
447*d57664e9SAndroid Build Coastguard Worker   }
448*d57664e9SAndroid Build Coastguard Worker 
449*d57664e9SAndroid Build Coastguard Worker   if (last_occurence == path.end()) {
450*d57664e9SAndroid Build Coastguard Worker     return false;
451*d57664e9SAndroid Build Coastguard Worker   }
452*d57664e9SAndroid Build Coastguard Worker 
453*d57664e9SAndroid Build Coastguard Worker   auto iter = std::find(last_occurence, path.end(), '.');
454*d57664e9SAndroid Build Coastguard Worker   *out_suffix = StringPiece(iter, path.end() - iter);
455*d57664e9SAndroid Build Coastguard Worker   *out_entry = StringPiece(last_occurence + 1, iter - last_occurence - 1);
456*d57664e9SAndroid Build Coastguard Worker   *out_prefix = StringPiece(path.begin(), last_occurence - path.begin() + 1);
457*d57664e9SAndroid Build Coastguard Worker   return true;
458*d57664e9SAndroid Build Coastguard Worker }
459*d57664e9SAndroid Build Coastguard Worker 
460*d57664e9SAndroid Build Coastguard Worker }  // namespace util
461*d57664e9SAndroid Build Coastguard Worker }  // namespace aapt
462