Bug Summary

File:llvm/tools/llvm-rc/ResourceFileWriter.cpp
Warning:line 879, column 5
Forming reference to null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ResourceFileWriter.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-14/lib/clang/14.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/llvm-rc -I /build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/llvm/tools/llvm-rc -I include -I /build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/llvm/include -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/= -O3 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-01-31-132209-101875-1 -x c++ /build/llvm-toolchain-snapshot-14~++20220131111436+ae68b3a45776/llvm/tools/llvm-rc/ResourceFileWriter.cpp
1//===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===---------------------------------------------------------------------===//
8//
9// This implements the visitor serializing resources to a .res stream.
10//
11//===---------------------------------------------------------------------===//
12
13#include "ResourceFileWriter.h"
14#include "llvm/Object/WindowsResource.h"
15#include "llvm/Support/ConvertUTF.h"
16#include "llvm/Support/Endian.h"
17#include "llvm/Support/EndianStream.h"
18#include "llvm/Support/FileSystem.h"
19#include "llvm/Support/MemoryBuffer.h"
20#include "llvm/Support/Path.h"
21#include "llvm/Support/Process.h"
22
23using namespace llvm::support;
24
25// Take an expression returning llvm::Error and forward the error if it exists.
26#define RETURN_IF_ERROR(Expr)if (auto Err = (Expr)) return Err; \
27 if (auto Err = (Expr)) \
28 return Err;
29
30namespace llvm {
31namespace rc {
32
33// Class that employs RAII to save the current FileWriter object state
34// and revert to it as soon as we leave the scope. This is useful if resources
35// declare their own resource-local statements.
36class ContextKeeper {
37 ResourceFileWriter *FileWriter;
38 ResourceFileWriter::ObjectInfo SavedInfo;
39
40public:
41 ContextKeeper(ResourceFileWriter *V)
42 : FileWriter(V), SavedInfo(V->ObjectData) {}
43 ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
44};
45
46static Error createError(const Twine &Message,
47 std::errc Type = std::errc::invalid_argument) {
48 return make_error<StringError>(Message, std::make_error_code(Type));
49}
50
51static Error checkNumberFits(uint32_t Number, size_t MaxBits,
52 const Twine &FieldName) {
53 assert(1 <= MaxBits && MaxBits <= 32)(static_cast <bool> (1 <= MaxBits && MaxBits
<= 32) ? void (0) : __assert_fail ("1 <= MaxBits && MaxBits <= 32"
, "llvm/tools/llvm-rc/ResourceFileWriter.cpp", 53, __extension__
__PRETTY_FUNCTION__))
;
54 if (!(Number >> MaxBits))
55 return Error::success();
56 return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
57 Twine(MaxBits) + " bits.",
58 std::errc::value_too_large);
59}
60
61template <typename FitType>
62static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
63 return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
64}
65
66// A similar function for signed integers.
67template <typename FitType>
68static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
69 bool CanBeNegative) {
70 int32_t SignedNum = Number;
71 if (SignedNum < std::numeric_limits<FitType>::min() ||
72 SignedNum > std::numeric_limits<FitType>::max())
73 return createError(FieldName + " (" + Twine(SignedNum) +
74 ") does not fit in " + Twine(sizeof(FitType) * 8) +
75 "-bit signed integer type.",
76 std::errc::value_too_large);
77
78 if (!CanBeNegative && SignedNum < 0)
79 return createError(FieldName + " (" + Twine(SignedNum) +
80 ") cannot be negative.");
81
82 return Error::success();
83}
84
85static Error checkRCInt(RCInt Number, const Twine &FieldName) {
86 if (Number.isLong())
87 return Error::success();
88 return checkNumberFits<uint16_t>(Number, FieldName);
89}
90
91static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
92 if (!Value.isInt())
93 return Error::success();
94 return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
95}
96
97static bool stripQuotes(StringRef &Str, bool &IsLongString) {
98 if (!Str.contains('"'))
99 return false;
100
101 // Just take the contents of the string, checking if it's been marked long.
102 IsLongString = Str.startswith_insensitive("L");
103 if (IsLongString)
104 Str = Str.drop_front();
105
106 bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
107 (void)StripSuccess;
108 assert(StripSuccess && "Strings should be enclosed in quotes.")(static_cast <bool> (StripSuccess && "Strings should be enclosed in quotes."
) ? void (0) : __assert_fail ("StripSuccess && \"Strings should be enclosed in quotes.\""
, "llvm/tools/llvm-rc/ResourceFileWriter.cpp", 108, __extension__
__PRETTY_FUNCTION__))
;
109 return true;
110}
111
112static UTF16 cp1252ToUnicode(unsigned char C) {
113 static const UTF16 Map80[] = {
114 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
115 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
116 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
117 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
118 };
119 if (C >= 0x80 && C <= 0x9F)
120 return Map80[C - 0x80];
121 return C;
122}
123
124// Describes a way to handle '\0' characters when processing the string.
125// rc.exe tool sometimes behaves in a weird way in postprocessing.
126// If the string to be output is equivalent to a C-string (e.g. in MENU
127// titles), string is (predictably) truncated after first 0-byte.
128// When outputting a string table, the behavior is equivalent to appending
129// '\0\0' at the end of the string, and then stripping the string
130// before the first '\0\0' occurrence.
131// Finally, when handling strings in user-defined resources, 0-bytes
132// aren't stripped, nor do they terminate the string.
133
134enum class NullHandlingMethod {
135 UserResource, // Don't terminate string on '\0'.
136 CutAtNull, // Terminate string on '\0'.
137 CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
138};
139
140// Parses an identifier or string and returns a processed version of it:
141// * Strip the string boundary quotes.
142// * Convert the input code page characters to UTF16.
143// * Squash "" to a single ".
144// * Replace the escape sequences with their processed version.
145// For identifiers, this is no-op.
146static Error processString(StringRef Str, NullHandlingMethod NullHandler,
147 bool &IsLongString, SmallVectorImpl<UTF16> &Result,
148 int CodePage) {
149 bool IsString = stripQuotes(Str, IsLongString);
150 SmallVector<UTF16, 128> Chars;
151
152 // Convert the input bytes according to the chosen codepage.
153 if (CodePage == CpUtf8) {
154 convertUTF8ToUTF16String(Str, Chars);
155 } else if (CodePage == CpWin1252) {
156 for (char C : Str)
157 Chars.push_back(cp1252ToUnicode((unsigned char)C));
158 } else {
159 // For other, unknown codepages, only allow plain ASCII input.
160 for (char C : Str) {
161 if ((unsigned char)C > 0x7F)
162 return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
163 ") can't be interpreted in the current codepage");
164 Chars.push_back((unsigned char)C);
165 }
166 }
167
168 if (!IsString) {
169 // It's an identifier if it's not a string. Make all characters uppercase.
170 for (UTF16 &Ch : Chars) {
171 assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII")(static_cast <bool> (Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII"
) ? void (0) : __assert_fail ("Ch <= 0x7F && \"We didn't allow identifiers to be non-ASCII\""
, "llvm/tools/llvm-rc/ResourceFileWriter.cpp", 171, __extension__
__PRETTY_FUNCTION__))
;
172 Ch = toupper(Ch);
173 }
174 Result.swap(Chars);
175 return Error::success();
176 }
177 Result.reserve(Chars.size());
178 size_t Pos = 0;
179
180 auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
181 if (!IsLongString) {
182 if (NullHandler == NullHandlingMethod::UserResource) {
183 // Narrow strings in user-defined resources are *not* output in
184 // UTF-16 format.
185 if (Char > 0xFF)
186 return createError("Non-8-bit codepoint (" + Twine(Char) +
187 ") can't occur in a user-defined narrow string");
188 }
189 }
190
191 Result.push_back(Char);
192 return Error::success();
193 };
194 auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
195 if (!IsLongString) {
196 // Escaped chars in narrow strings have to be interpreted according to
197 // the chosen code page.
198 if (Char > 0xFF)
199 return createError("Non-8-bit escaped char (" + Twine(Char) +
200 ") can't occur in narrow string");
201 if (CodePage == CpUtf8) {
202 if (Char >= 0x80)
203 return createError("Unable to interpret single byte (" + Twine(Char) +
204 ") as UTF-8");
205 } else if (CodePage == CpWin1252) {
206 Char = cp1252ToUnicode(Char);
207 } else {
208 // Unknown/unsupported codepage, only allow ASCII input.
209 if (Char > 0x7F)
210 return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
211 ") can't "
212 "occur in a non-Unicode string");
213 }
214 }
215
216 return AddRes(Char);
217 };
218
219 while (Pos < Chars.size()) {
220 UTF16 CurChar = Chars[Pos];
221 ++Pos;
222
223 // Strip double "".
224 if (CurChar == '"') {
225 if (Pos == Chars.size() || Chars[Pos] != '"')
226 return createError("Expected \"\"");
227 ++Pos;
228 RETURN_IF_ERROR(AddRes('"'))if (auto Err = (AddRes('"'))) return Err;;
229 continue;
230 }
231
232 if (CurChar == '\\') {
233 UTF16 TypeChar = Chars[Pos];
234 ++Pos;
235
236 if (TypeChar == 'x' || TypeChar == 'X') {
237 // Read a hex number. Max number of characters to read differs between
238 // narrow and wide strings.
239 UTF16 ReadInt = 0;
240 size_t RemainingChars = IsLongString ? 4 : 2;
241 // We don't want to read non-ASCII hex digits. std:: functions past
242 // 0xFF invoke UB.
243 //
244 // FIXME: actually, Microsoft version probably doesn't check this
245 // condition and uses their Unicode version of 'isxdigit'. However,
246 // there are some hex-digit Unicode character outside of ASCII, and
247 // some of these are actually accepted by rc.exe, the notable example
248 // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
249 // instead of ASCII digits in \x... escape sequence and get accepted.
250 // However, the resulting hexcodes seem totally unpredictable.
251 // We think it's infeasible to try to reproduce this behavior, nor to
252 // put effort in order to detect it.
253 while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
254 if (!isxdigit(Chars[Pos]))
255 break;
256 char Digit = tolower(Chars[Pos]);
257 ++Pos;
258
259 ReadInt <<= 4;
260 if (isdigit(Digit))
261 ReadInt |= Digit - '0';
262 else
263 ReadInt |= Digit - 'a' + 10;
264
265 --RemainingChars;
266 }
267
268 RETURN_IF_ERROR(AddEscapedChar(ReadInt))if (auto Err = (AddEscapedChar(ReadInt))) return Err;;
269 continue;
270 }
271
272 if (TypeChar >= '0' && TypeChar < '8') {
273 // Read an octal number. Note that we've already read the first digit.
274 UTF16 ReadInt = TypeChar - '0';
275 size_t RemainingChars = IsLongString ? 6 : 2;
276
277 while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
278 Chars[Pos] < '8') {
279 ReadInt <<= 3;
280 ReadInt |= Chars[Pos] - '0';
281 --RemainingChars;
282 ++Pos;
283 }
284
285 RETURN_IF_ERROR(AddEscapedChar(ReadInt))if (auto Err = (AddEscapedChar(ReadInt))) return Err;;
286
287 continue;
288 }
289
290 switch (TypeChar) {
291 case 'A':
292 case 'a':
293 // Windows '\a' translates into '\b' (Backspace).
294 RETURN_IF_ERROR(AddRes('\b'))if (auto Err = (AddRes('\b'))) return Err;;
295 break;
296
297 case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
298 RETURN_IF_ERROR(AddRes('\n'))if (auto Err = (AddRes('\n'))) return Err;;
299 break;
300
301 case 'r':
302 RETURN_IF_ERROR(AddRes('\r'))if (auto Err = (AddRes('\r'))) return Err;;
303 break;
304
305 case 'T':
306 case 't':
307 RETURN_IF_ERROR(AddRes('\t'))if (auto Err = (AddRes('\t'))) return Err;;
308 break;
309
310 case '\\':
311 RETURN_IF_ERROR(AddRes('\\'))if (auto Err = (AddRes('\\'))) return Err;;
312 break;
313
314 case '"':
315 // RC accepts \" only if another " comes afterwards; then, \"" means
316 // a single ".
317 if (Pos == Chars.size() || Chars[Pos] != '"')
318 return createError("Expected \\\"\"");
319 ++Pos;
320 RETURN_IF_ERROR(AddRes('"'))if (auto Err = (AddRes('"'))) return Err;;
321 break;
322
323 default:
324 // If TypeChar means nothing, \ is should be output to stdout with
325 // following char. However, rc.exe consumes these characters when
326 // dealing with wide strings.
327 if (!IsLongString) {
328 RETURN_IF_ERROR(AddRes('\\'))if (auto Err = (AddRes('\\'))) return Err;;
329 RETURN_IF_ERROR(AddRes(TypeChar))if (auto Err = (AddRes(TypeChar))) return Err;;
330 }
331 break;
332 }
333
334 continue;
335 }
336
337 // If nothing interesting happens, just output the character.
338 RETURN_IF_ERROR(AddRes(CurChar))if (auto Err = (AddRes(CurChar))) return Err;;
339 }
340
341 switch (NullHandler) {
342 case NullHandlingMethod::CutAtNull:
343 for (size_t Pos = 0; Pos < Result.size(); ++Pos)
344 if (Result[Pos] == '\0')
345 Result.resize(Pos);
346 break;
347
348 case NullHandlingMethod::CutAtDoubleNull:
349 for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
350 if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
351 Result.resize(Pos);
352 if (Result.size() > 0 && Result.back() == '\0')
353 Result.pop_back();
354 break;
355
356 case NullHandlingMethod::UserResource:
357 break;
358 }
359
360 return Error::success();
361}
362
363uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
364 uint64_t Result = tell();
365 FS->write((const char *)Data.begin(), Data.size());
366 return Result;
367}
368
369Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
370 SmallVector<UTF16, 128> ProcessedString;
371 bool IsLongString;
372 RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,if (auto Err = (processString(Str, NullHandlingMethod::CutAtNull
, IsLongString, ProcessedString, Params.CodePage))) return Err
;
373 IsLongString, ProcessedString,if (auto Err = (processString(Str, NullHandlingMethod::CutAtNull
, IsLongString, ProcessedString, Params.CodePage))) return Err
;
374 Params.CodePage))if (auto Err = (processString(Str, NullHandlingMethod::CutAtNull
, IsLongString, ProcessedString, Params.CodePage))) return Err
;
;
375 for (auto Ch : ProcessedString)
376 writeInt<uint16_t>(Ch);
377 if (WriteTerminator)
378 writeInt<uint16_t>(0);
379 return Error::success();
380}
381
382Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
383 return writeIntOrString(Ident);
384}
385
386Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
387 if (!Value.isInt())
388 return writeCString(Value.getString());
389
390 writeInt<uint16_t>(0xFFFF);
391 writeInt<uint16_t>(Value.getInt());
392 return Error::success();
393}
394
395void ResourceFileWriter::writeRCInt(RCInt Value) {
396 if (Value.isLong())
397 writeInt<uint32_t>(Value);
398 else
399 writeInt<uint16_t>(Value);
400}
401
402Error ResourceFileWriter::appendFile(StringRef Filename) {
403 bool IsLong;
404 stripQuotes(Filename, IsLong);
405
406 auto File = loadFile(Filename);
407 if (!File)
408 return File.takeError();
409
410 *FS << (*File)->getBuffer();
411 return Error::success();
412}
413
414void ResourceFileWriter::padStream(uint64_t Length) {
415 assert(Length > 0)(static_cast <bool> (Length > 0) ? void (0) : __assert_fail
("Length > 0", "llvm/tools/llvm-rc/ResourceFileWriter.cpp"
, 415, __extension__ __PRETTY_FUNCTION__))
;
416 uint64_t Location = tell();
417 Location %= Length;
418 uint64_t Pad = (Length - Location) % Length;
419 for (uint64_t i = 0; i < Pad; ++i)
420 writeInt<uint8_t>(0);
421}
422
423Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
424 if (Err)
425 return joinErrors(createError("Error in " + Res->getResourceTypeName() +
426 " statement (ID " + Twine(Res->ResName) +
427 "): "),
428 std::move(Err));
429 return Error::success();
430}
431
432Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
433 return writeResource(Res, &ResourceFileWriter::writeNullBody);
434}
435
436Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
437 return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
438}
439
440Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
441 return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
442}
443
444Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
445 return handleError(visitIconOrCursorResource(Res), Res);
446}
447
448Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
449 return writeResource(Res, &ResourceFileWriter::writeDialogBody);
450}
451
452Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
453 return handleError(visitIconOrCursorResource(Res), Res);
1
Calling 'ResourceFileWriter::visitIconOrCursorResource'
454}
455
456Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
457 ObjectData.Caption = Stmt->Value;
458 return Error::success();
459}
460
461Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
462 ObjectData.Class = Stmt->Value;
463 return Error::success();
464}
465
466Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
467 return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
468}
469
470Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
471 return writeResource(Res, &ResourceFileWriter::writeMenuBody);
472}
473
474Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
475 const auto *Res = cast<StringTableResource>(Base);
476
477 ContextKeeper RAII(this);
478 RETURN_IF_ERROR(Res->applyStmts(this))if (auto Err = (Res->applyStmts(this))) return Err;;
479
480 for (auto &String : Res->Table) {
481 RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"))if (auto Err = (checkNumberFits<uint16_t>(String.first,
"String ID"))) return Err;
;
482 uint16_t BundleID = String.first >> 4;
483 StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
484 auto &BundleData = StringTableData.BundleData;
485 auto Iter = BundleData.find(Key);
486
487 if (Iter == BundleData.end()) {
488 // Need to create a bundle.
489 StringTableData.BundleList.push_back(Key);
490 auto EmplaceResult = BundleData.emplace(
491 Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
492 assert(EmplaceResult.second && "Could not create a bundle")(static_cast <bool> (EmplaceResult.second && "Could not create a bundle"
) ? void (0) : __assert_fail ("EmplaceResult.second && \"Could not create a bundle\""
, "llvm/tools/llvm-rc/ResourceFileWriter.cpp", 492, __extension__
__PRETTY_FUNCTION__))
;
493 Iter = EmplaceResult.first;
494 }
495
496 RETURN_IF_ERROR(if (auto Err = (insertStringIntoBundle(Iter->second, String
.first, String.second))) return Err;
497 insertStringIntoBundle(Iter->second, String.first, String.second))if (auto Err = (insertStringIntoBundle(Iter->second, String
.first, String.second))) return Err;
;
498 }
499
500 return Error::success();
501}
502
503Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
504 return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
505}
506
507Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
508 return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
509}
510
511Error ResourceFileWriter::visitCharacteristicsStmt(
512 const CharacteristicsStmt *Stmt) {
513 ObjectData.Characteristics = Stmt->Value;
514 return Error::success();
515}
516
517Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) {
518 ObjectData.ExStyle = Stmt->Value;
519 return Error::success();
520}
521
522Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
523 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"))if (auto Err = (checkNumberFits<uint16_t>(Stmt->Size
, "Font size"))) return Err;
;
524 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"))if (auto Err = (checkNumberFits<uint16_t>(Stmt->Weight
, "Font weight"))) return Err;
;
525 RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"))if (auto Err = (checkNumberFits<uint8_t>(Stmt->Charset
, "Font charset"))) return Err;
;
526 ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
527 Stmt->Charset};
528 ObjectData.Font.emplace(Font);
529 return Error::success();
530}
531
532Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
533 RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"))if (auto Err = (checkNumberFits(Stmt->Lang, 10, "Primary language ID"
))) return Err;
;
534 RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"))if (auto Err = (checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"
))) return Err;
;
535 ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
536 return Error::success();
537}
538
539Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
540 ObjectData.Style = Stmt->Value;
541 return Error::success();
542}
543
544Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
545 ObjectData.VersionInfo = Stmt->Value;
546 return Error::success();
547}
548
549Error ResourceFileWriter::writeResource(
550 const RCResource *Res,
551 Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
552 // We don't know the sizes yet.
553 object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
554 uint64_t HeaderLoc = writeObject(HeaderPrefix);
555
556 auto ResType = Res->getResourceType();
557 RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"))if (auto Err = (checkIntOrString(ResType, "Resource type"))) return
Err;
;
558 RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"))if (auto Err = (checkIntOrString(Res->ResName, "Resource ID"
))) return Err;
;
559 RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res))if (auto Err = (handleError(writeIdentifier(ResType), Res))) return
Err;
;
560 RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res))if (auto Err = (handleError(writeIdentifier(Res->ResName),
Res))) return Err;
;
561
562 // Apply the resource-local optional statements.
563 ContextKeeper RAII(this);
564 RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res))if (auto Err = (handleError(Res->applyStmts(this), Res))) return
Err;
;
565
566 padStream(sizeof(uint32_t));
567 object::WinResHeaderSuffix HeaderSuffix{
568 ulittle32_t(0), // DataVersion; seems to always be 0
569 ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
570 ulittle32_t(ObjectData.VersionInfo),
571 ulittle32_t(ObjectData.Characteristics)};
572 writeObject(HeaderSuffix);
573
574 uint64_t DataLoc = tell();
575 RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res))if (auto Err = (handleError((this->*BodyWriter)(Res), Res)
)) return Err;
;
576 // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
577
578 // Update the sizes.
579 HeaderPrefix.DataSize = tell() - DataLoc;
580 HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
581 writeObjectAt(HeaderPrefix, HeaderLoc);
582 padStream(sizeof(uint32_t));
583
584 return Error::success();
585}
586
587// --- NullResource helpers. --- //
588
589Error ResourceFileWriter::writeNullBody(const RCResource *) {
590 return Error::success();
591}
592
593// --- AcceleratorsResource helpers. --- //
594
595Error ResourceFileWriter::writeSingleAccelerator(
596 const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
597 using Accelerator = AcceleratorsResource::Accelerator;
598 using Opt = Accelerator::Options;
599
600 struct AccelTableEntry {
601 ulittle16_t Flags;
602 ulittle16_t ANSICode;
603 ulittle16_t Id;
604 uint16_t Padding;
605 } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
606
607 bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
608
609 // Remove ASCII flags (which doesn't occur in .res files).
610 Entry.Flags = Obj.Flags & ~Opt::ASCII;
611
612 if (IsLastItem)
613 Entry.Flags |= 0x80;
614
615 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"))if (auto Err = (checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"
))) return Err;
;
616 Entry.Id = ulittle16_t(Obj.Id);
617
618 auto createAccError = [&Obj](const char *Msg) {
619 return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
620 };
621
622 if (IsASCII && IsVirtKey)
623 return createAccError("Accelerator can't be both ASCII and VIRTKEY");
624
625 if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
626 return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
627 " accelerators");
628
629 if (Obj.Event.isInt()) {
630 if (!IsASCII && !IsVirtKey)
631 return createAccError(
632 "Accelerator with a numeric event must be either ASCII"
633 " or VIRTKEY");
634
635 uint32_t EventVal = Obj.Event.getInt();
636 RETURN_IF_ERROR(if (auto Err = (checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"
))) return Err;
637 checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"))if (auto Err = (checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"
))) return Err;
;
638 Entry.ANSICode = ulittle16_t(EventVal);
639 writeObject(Entry);
640 return Error::success();
641 }
642
643 StringRef Str = Obj.Event.getString();
644 bool IsWide;
645 stripQuotes(Str, IsWide);
646
647 if (Str.size() == 0 || Str.size() > 2)
648 return createAccError(
649 "Accelerator string events should have length 1 or 2");
650
651 if (Str[0] == '^') {
652 if (Str.size() == 1)
653 return createAccError("No character following '^' in accelerator event");
654 if (IsVirtKey)
655 return createAccError(
656 "VIRTKEY accelerator events can't be preceded by '^'");
657
658 char Ch = Str[1];
659 if (Ch >= 'a' && Ch <= 'z')
660 Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
661 else if (Ch >= 'A' && Ch <= 'Z')
662 Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
663 else
664 return createAccError("Control character accelerator event should be"
665 " alphabetic");
666
667 writeObject(Entry);
668 return Error::success();
669 }
670
671 if (Str.size() == 2)
672 return createAccError("Event string should be one-character, possibly"
673 " preceded by '^'");
674
675 uint8_t EventCh = Str[0];
676 // The original tool just warns in this situation. We chose to fail.
677 if (IsVirtKey && !isalnum(EventCh))
678 return createAccError("Non-alphanumeric characters cannot describe virtual"
679 " keys");
680 if (EventCh > 0x7F)
681 return createAccError("Non-ASCII description of accelerator");
682
683 if (IsVirtKey)
684 EventCh = toupper(EventCh);
685 Entry.ANSICode = ulittle16_t(EventCh);
686 writeObject(Entry);
687 return Error::success();
688}
689
690Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
691 auto *Res = cast<AcceleratorsResource>(Base);
692 size_t AcceleratorId = 0;
693 for (auto &Acc : Res->Accelerators) {
694 ++AcceleratorId;
695 RETURN_IF_ERROR(if (auto Err = (writeSingleAccelerator(Acc, AcceleratorId == Res
->Accelerators.size()))) return Err;
696 writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()))if (auto Err = (writeSingleAccelerator(Acc, AcceleratorId == Res
->Accelerators.size()))) return Err;
;
697 }
698 return Error::success();
699}
700
701// --- BitmapResource helpers. --- //
702
703Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
704 StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
705 bool IsLong;
706 stripQuotes(Filename, IsLong);
707
708 auto File = loadFile(Filename);
709 if (!File)
710 return File.takeError();
711
712 StringRef Buffer = (*File)->getBuffer();
713
714 // Skip the 14 byte BITMAPFILEHEADER.
715 constexpr size_t BITMAPFILEHEADER_size = 14;
716 if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
717 Buffer[1] != 'M')
718 return createError("Incorrect bitmap file.");
719
720 *FS << Buffer.substr(BITMAPFILEHEADER_size);
721 return Error::success();
722}
723
724// --- CursorResource and IconResource helpers. --- //
725
726// ICONRESDIR structure. Describes a single icon in resource group.
727//
728// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
729struct IconResDir {
730 uint8_t Width;
731 uint8_t Height;
732 uint8_t ColorCount;
733 uint8_t Reserved;
734};
735
736// CURSORDIR structure. Describes a single cursor in resource group.
737//
738// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
739struct CursorDir {
740 ulittle16_t Width;
741 ulittle16_t Height;
742};
743
744// RESDIRENTRY structure, stripped from the last item. Stripping made
745// for compatibility with RESDIR.
746//
747// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
748struct ResourceDirEntryStart {
749 union {
750 CursorDir Cursor; // Used in CURSOR resources.
751 IconResDir Icon; // Used in .ico and .cur files, and ICON resources.
752 };
753 ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource).
754 ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
755 ulittle32_t Size;
756 // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only).
757 // ulittle16_t IconID; // Resource icon ID (RESDIR only).
758};
759
760// BITMAPINFOHEADER structure. Describes basic information about the bitmap
761// being read.
762//
763// Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
764struct BitmapInfoHeader {
765 ulittle32_t Size;
766 ulittle32_t Width;
767 ulittle32_t Height;
768 ulittle16_t Planes;
769 ulittle16_t BitCount;
770 ulittle32_t Compression;
771 ulittle32_t SizeImage;
772 ulittle32_t XPelsPerMeter;
773 ulittle32_t YPelsPerMeter;
774 ulittle32_t ClrUsed;
775 ulittle32_t ClrImportant;
776};
777
778// Group icon directory header. Called ICONDIR in .ico/.cur files and
779// NEWHEADER in .res files.
780//
781// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
782struct GroupIconDir {
783 ulittle16_t Reserved; // Always 0.
784 ulittle16_t ResType; // 1 for icons, 2 for cursors.
785 ulittle16_t ResCount; // Number of items.
786};
787
788enum class IconCursorGroupType { Icon, Cursor };
789
790class SingleIconCursorResource : public RCResource {
791public:
792 IconCursorGroupType Type;
793 const ResourceDirEntryStart &Header;
794 ArrayRef<uint8_t> Image;
795
796 SingleIconCursorResource(IconCursorGroupType ResourceType,
797 const ResourceDirEntryStart &HeaderEntry,
798 ArrayRef<uint8_t> ImageData, uint16_t Flags)
799 : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
800 Image(ImageData) {}
801
802 Twine getResourceTypeName() const override { return "Icon/cursor image"; }
803 IntOrString getResourceType() const override {
804 return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
805 }
806 ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
807 static bool classof(const RCResource *Res) {
808 return Res->getKind() == RkSingleCursorOrIconRes;
809 }
810};
811
812class IconCursorGroupResource : public RCResource {
813public:
814 IconCursorGroupType Type;
815 GroupIconDir Header;
816 std::vector<ResourceDirEntryStart> ItemEntries;
817
818 IconCursorGroupResource(IconCursorGroupType ResourceType,
819 const GroupIconDir &HeaderData,
820 std::vector<ResourceDirEntryStart> &&Entries)
821 : Type(ResourceType), Header(HeaderData),
822 ItemEntries(std::move(Entries)) {}
823
824 Twine getResourceTypeName() const override { return "Icon/cursor group"; }
825 IntOrString getResourceType() const override {
826 return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
827 }
828 ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
829 static bool classof(const RCResource *Res) {
830 return Res->getKind() == RkCursorOrIconGroupRes;
831 }
832};
833
834Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
835 auto *Res = cast<SingleIconCursorResource>(Base);
836 if (Res->Type == IconCursorGroupType::Cursor) {
837 // In case of cursors, two WORDS are appended to the beginning
838 // of the resource: HotspotX (Planes in RESDIRENTRY),
839 // and HotspotY (BitCount).
840 //
841 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
842 // (Remarks section).
843 writeObject(Res->Header.Planes);
844 writeObject(Res->Header.BitCount);
845 }
846
847 writeObject(Res->Image);
848 return Error::success();
849}
850
851Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
852 auto *Res = cast<IconCursorGroupResource>(Base);
853 writeObject(Res->Header);
854 for (auto Item : Res->ItemEntries) {
855 writeObject(Item);
856 writeInt(IconCursorID++);
857 }
858 return Error::success();
859}
860
861Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
862 return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
863}
864
865Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
866 return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
867}
868
869Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
870 IconCursorGroupType Type;
871 StringRef FileStr;
872 IntOrString ResName = Base->ResName;
873
874 if (auto *IconRes
2.1
'IconRes' is null
= dyn_cast<IconResource>(Base)) {
2
Assuming 'Base' is not a 'IconResource'
3
Taking false branch
875 FileStr = IconRes->IconLoc;
876 Type = IconCursorGroupType::Icon;
877 } else {
878 auto *CursorRes = dyn_cast<CursorResource>(Base);
4
Assuming 'Base' is not a 'CursorResource'
5
'CursorRes' initialized to a null pointer value
879 FileStr = CursorRes->CursorLoc;
6
Forming reference to null pointer
880 Type = IconCursorGroupType::Cursor;
881 }
882
883 bool IsLong;
884 stripQuotes(FileStr, IsLong);
885 auto File = loadFile(FileStr);
886
887 if (!File)
888 return File.takeError();
889
890 BinaryStreamReader Reader((*File)->getBuffer(), support::little);
891
892 // Read the file headers.
893 // - At the beginning, ICONDIR/NEWHEADER header.
894 // - Then, a number of RESDIR headers follow. These contain offsets
895 // to data.
896 const GroupIconDir *Header;
897
898 RETURN_IF_ERROR(Reader.readObject(Header))if (auto Err = (Reader.readObject(Header))) return Err;;
899 if (Header->Reserved != 0)
900 return createError("Incorrect icon/cursor Reserved field; should be 0.");
901 uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
902 if (Header->ResType != NeededType)
903 return createError("Incorrect icon/cursor ResType field; should be " +
904 Twine(NeededType) + ".");
905
906 uint16_t NumItems = Header->ResCount;
907
908 // Read single ico/cur headers.
909 std::vector<ResourceDirEntryStart> ItemEntries;
910 ItemEntries.reserve(NumItems);
911 std::vector<uint32_t> ItemOffsets(NumItems);
912 for (size_t ID = 0; ID < NumItems; ++ID) {
913 const ResourceDirEntryStart *Object;
914 RETURN_IF_ERROR(Reader.readObject(Object))if (auto Err = (Reader.readObject(Object))) return Err;;
915 ItemEntries.push_back(*Object);
916 RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]))if (auto Err = (Reader.readInteger(ItemOffsets[ID]))) return Err
;
;
917 }
918
919 // Now write each icon/cursors one by one. At first, all the contents
920 // without ICO/CUR header. This is described by SingleIconCursorResource.
921 for (size_t ID = 0; ID < NumItems; ++ID) {
922 // Load the fragment of file.
923 Reader.setOffset(ItemOffsets[ID]);
924 ArrayRef<uint8_t> Image;
925 RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size))if (auto Err = (Reader.readArray(Image, ItemEntries[ID].Size)
)) return Err;
;
926 SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
927 Base->MemoryFlags);
928 SingleRes.setName(IconCursorID + ID);
929 RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes))if (auto Err = (visitSingleIconOrCursor(&SingleRes))) return
Err;
;
930 }
931
932 // Now, write all the headers concatenated into a separate resource.
933 for (size_t ID = 0; ID < NumItems; ++ID) {
934 // We need to rewrite the cursor headers, and fetch actual values
935 // for Planes/BitCount.
936 const auto &OldHeader = ItemEntries[ID];
937 ResourceDirEntryStart NewHeader = OldHeader;
938
939 if (Type == IconCursorGroupType::Cursor) {
940 NewHeader.Cursor.Width = OldHeader.Icon.Width;
941 // Each cursor in fact stores two bitmaps, one under another.
942 // Height provided in cursor definition describes the height of the
943 // cursor, whereas the value existing in resource definition describes
944 // the height of the bitmap. Therefore, we need to double this height.
945 NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
946
947 // Two WORDs were written at the beginning of the resource (hotspot
948 // location). This is reflected in Size field.
949 NewHeader.Size += 2 * sizeof(uint16_t);
950 }
951
952 // Now, we actually need to read the bitmap header to find
953 // the number of planes and the number of bits per pixel.
954 Reader.setOffset(ItemOffsets[ID]);
955 const BitmapInfoHeader *BMPHeader;
956 RETURN_IF_ERROR(Reader.readObject(BMPHeader))if (auto Err = (Reader.readObject(BMPHeader))) return Err;;
957 if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
958 NewHeader.Planes = BMPHeader->Planes;
959 NewHeader.BitCount = BMPHeader->BitCount;
960 } else {
961 // A PNG .ico file.
962 // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
963 // "The image must be in 32bpp"
964 NewHeader.Planes = 1;
965 NewHeader.BitCount = 32;
966 }
967
968 ItemEntries[ID] = NewHeader;
969 }
970
971 IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
972 HeaderRes.setName(ResName);
973 if (Base->MemoryFlags & MfPreload) {
974 HeaderRes.MemoryFlags |= MfPreload;
975 HeaderRes.MemoryFlags &= ~MfPure;
976 }
977 RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes))if (auto Err = (visitIconOrCursorGroup(&HeaderRes))) return
Err;
;
978
979 return Error::success();
980}
981
982// --- DialogResource helpers. --- //
983
984Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
985 bool IsExtended) {
986 // Each control should be aligned to DWORD.
987 padStream(sizeof(uint32_t));
988
989 auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
990 IntWithNotMask CtlStyle(TypeInfo.Style);
991 CtlStyle |= Ctl.Style.getValueOr(RCInt(0));
992 uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
993
994 // DIALOG(EX) item header prefix.
995 if (!IsExtended) {
996 struct {
997 ulittle32_t Style;
998 ulittle32_t ExtStyle;
999 } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
1000 writeObject(Prefix);
1001 } else {
1002 struct {
1003 ulittle32_t HelpID;
1004 ulittle32_t ExtStyle;
1005 ulittle32_t Style;
1006 } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
1007 ulittle32_t(CtlStyle.getValue())};
1008 writeObject(Prefix);
1009 }
1010
1011 // Common fixed-length part.
1012 RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.X, "Dialog control x-coordinate"
, true))) return Err;
1013 Ctl.X, "Dialog control x-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.X, "Dialog control x-coordinate"
, true))) return Err;
;
1014 RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Y, "Dialog control y-coordinate"
, true))) return Err;
1015 Ctl.Y, "Dialog control y-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Y, "Dialog control y-coordinate"
, true))) return Err;
;
1016 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Ctl.Width
, "Dialog control width", false))) return Err;
1017 checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false))if (auto Err = (checkSignedNumberFits<int16_t>(Ctl.Width
, "Dialog control width", false))) return Err;
;
1018 RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Height
, "Dialog control height", false))) return Err;
1019 Ctl.Height, "Dialog control height", false))if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Height
, "Dialog control height", false))) return Err;
;
1020 struct {
1021 ulittle16_t X;
1022 ulittle16_t Y;
1023 ulittle16_t Width;
1024 ulittle16_t Height;
1025 } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
1026 ulittle16_t(Ctl.Height)};
1027 writeObject(Middle);
1028
1029 // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
1030 if (!IsExtended) {
1031 // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
1032 // want to refer to later.
1033 if (Ctl.ID != static_cast<uint32_t>(-1))
1034 RETURN_IF_ERROR(checkNumberFits<uint16_t>(if (auto Err = (checkNumberFits<uint16_t>( Ctl.ID, "Control ID in simple DIALOG resource"
))) return Err;
1035 Ctl.ID, "Control ID in simple DIALOG resource"))if (auto Err = (checkNumberFits<uint16_t>( Ctl.ID, "Control ID in simple DIALOG resource"
))) return Err;
;
1036 writeInt<uint16_t>(Ctl.ID);
1037 } else {
1038 writeInt<uint32_t>(Ctl.ID);
1039 }
1040
1041 // Window class - either 0xFFFF + 16-bit integer or a string.
1042 RETURN_IF_ERROR(writeIntOrString(Ctl.Class))if (auto Err = (writeIntOrString(Ctl.Class))) return Err;;
1043
1044 // Element caption/reference ID. ID is preceded by 0xFFFF.
1045 RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"))if (auto Err = (checkIntOrString(Ctl.Title, "Control reference ID"
))) return Err;
;
1046 RETURN_IF_ERROR(writeIntOrString(Ctl.Title))if (auto Err = (writeIntOrString(Ctl.Title))) return Err;;
1047
1048 // # bytes of extra creation data count. Don't pass any.
1049 writeInt<uint16_t>(0);
1050
1051 return Error::success();
1052}
1053
1054Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
1055 auto *Res = cast<DialogResource>(Base);
1056
1057 // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1058 const uint32_t DefaultStyle = 0x80880000;
1059 const uint32_t StyleFontFlag = 0x40;
1060 const uint32_t StyleCaptionFlag = 0x00C00000;
1061
1062 uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
1063 if (ObjectData.Font)
1064 UsedStyle |= StyleFontFlag;
1065 else
1066 UsedStyle &= ~StyleFontFlag;
1067
1068 // Actually, in case of empty (but existent) caption, the examined field
1069 // is equal to "\"\"". That's why empty captions are still noticed.
1070 if (ObjectData.Caption != "")
1071 UsedStyle |= StyleCaptionFlag;
1072
1073 const uint16_t DialogExMagic = 0xFFFF;
1074 uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0);
1075
1076 // Write DIALOG(EX) header prefix. These are pretty different.
1077 if (!Res->IsExtended) {
1078 // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1079 // In such a case, whole object (in .res file) is equivalent to a
1080 // DIALOGEX. It might lead to access violation/segmentation fault in
1081 // resource readers. For example,
1082 // 1 DIALOG 0, 0, 0, 65432
1083 // STYLE 0xFFFF0001 {}
1084 // would be compiled to a DIALOGEX with 65432 controls.
1085 if ((UsedStyle >> 16) == DialogExMagic)
1086 return createError("16 higher bits of DIALOG resource style cannot be"
1087 " equal to 0xFFFF");
1088
1089 struct {
1090 ulittle32_t Style;
1091 ulittle32_t ExtStyle;
1092 } Prefix{ulittle32_t(UsedStyle),
1093 ulittle32_t(ExStyle)};
1094
1095 writeObject(Prefix);
1096 } else {
1097 struct {
1098 ulittle16_t Version;
1099 ulittle16_t Magic;
1100 ulittle32_t HelpID;
1101 ulittle32_t ExtStyle;
1102 ulittle32_t Style;
1103 } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
1104 ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
1105
1106 writeObject(Prefix);
1107 }
1108
1109 // Now, a common part. First, fixed-length fields.
1110 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),if (auto Err = (checkNumberFits<uint16_t>(Res->Controls
.size(), "Number of dialog controls"))) return Err;
1111 "Number of dialog controls"))if (auto Err = (checkNumberFits<uint16_t>(Res->Controls
.size(), "Number of dialog controls"))) return Err;
;
1112 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
X, "Dialog x-coordinate", true))) return Err;
1113 checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
X, "Dialog x-coordinate", true))) return Err;
;
1114 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Y, "Dialog y-coordinate", true))) return Err;
1115 checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Y, "Dialog y-coordinate", true))) return Err;
;
1116 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Width, "Dialog width", false))) return Err;
1117 checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Width, "Dialog width", false))) return Err;
;
1118 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Height, "Dialog height", false))) return Err;
1119 checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Height, "Dialog height", false))) return Err;
;
1120 struct {
1121 ulittle16_t Count;
1122 ulittle16_t PosX;
1123 ulittle16_t PosY;
1124 ulittle16_t DialogWidth;
1125 ulittle16_t DialogHeight;
1126 } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
1127 ulittle16_t(Res->Y), ulittle16_t(Res->Width),
1128 ulittle16_t(Res->Height)};
1129 writeObject(Middle);
1130
1131 // MENU field. As of now, we don't keep them in the state and can peacefully
1132 // think there is no menu attached to the dialog.
1133 writeInt<uint16_t>(0);
1134
1135 // Window CLASS field.
1136 RETURN_IF_ERROR(writeIntOrString(ObjectData.Class))if (auto Err = (writeIntOrString(ObjectData.Class))) return Err
;
;
1137
1138 // Window title or a single word equal to 0.
1139 RETURN_IF_ERROR(writeCString(ObjectData.Caption))if (auto Err = (writeCString(ObjectData.Caption))) return Err
;
;
1140
1141 // If there *is* a window font declared, output its data.
1142 auto &Font = ObjectData.Font;
1143 if (Font) {
1144 writeInt<uint16_t>(Font->Size);
1145 // Additional description occurs only in DIALOGEX.
1146 if (Res->IsExtended) {
1147 writeInt<uint16_t>(Font->Weight);
1148 writeInt<uint8_t>(Font->IsItalic);
1149 writeInt<uint8_t>(Font->Charset);
1150 }
1151 RETURN_IF_ERROR(writeCString(Font->Typeface))if (auto Err = (writeCString(Font->Typeface))) return Err;;
1152 }
1153
1154 auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
1155 if (!Err)
1156 return Error::success();
1157 return joinErrors(createError("Error in " + Twine(Ctl.Type) +
1158 " control (ID " + Twine(Ctl.ID) + "):"),
1159 std::move(Err));
1160 };
1161
1162 for (auto &Ctl : Res->Controls)
1163 RETURN_IF_ERROR(if (auto Err = (handleCtlError(writeSingleDialogControl(Ctl, Res
->IsExtended), Ctl))) return Err;
1164 handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl))if (auto Err = (handleCtlError(writeSingleDialogControl(Ctl, Res
->IsExtended), Ctl))) return Err;
;
1165
1166 return Error::success();
1167}
1168
1169// --- HTMLResource helpers. --- //
1170
1171Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
1172 return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
1173}
1174
1175// --- MenuResource helpers. --- //
1176
1177Error ResourceFileWriter::writeMenuDefinition(
1178 const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1179 assert(Def)(static_cast <bool> (Def) ? void (0) : __assert_fail ("Def"
, "llvm/tools/llvm-rc/ResourceFileWriter.cpp", 1179, __extension__
__PRETTY_FUNCTION__))
;
1180 const MenuDefinition *DefPtr = Def.get();
1181
1182 if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
1183 writeInt<uint16_t>(Flags);
1184 // Some resource files use -1, i.e. UINT32_MAX, for empty menu items.
1185 if (MenuItemPtr->Id != static_cast<uint32_t>(-1))
1186 RETURN_IF_ERROR(if (auto Err = (checkNumberFits<uint16_t>(MenuItemPtr->
Id, "MENUITEM action ID"))) return Err;
1187 checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"))if (auto Err = (checkNumberFits<uint16_t>(MenuItemPtr->
Id, "MENUITEM action ID"))) return Err;
;
1188 writeInt<uint16_t>(MenuItemPtr->Id);
1189 RETURN_IF_ERROR(writeCString(MenuItemPtr->Name))if (auto Err = (writeCString(MenuItemPtr->Name))) return Err
;
;
1190 return Error::success();
1191 }
1192
1193 if (isa<MenuSeparator>(DefPtr)) {
1194 writeInt<uint16_t>(Flags);
1195 writeInt<uint32_t>(0);
1196 return Error::success();
1197 }
1198
1199 auto *PopupPtr = cast<PopupItem>(DefPtr);
1200 writeInt<uint16_t>(Flags);
1201 RETURN_IF_ERROR(writeCString(PopupPtr->Name))if (auto Err = (writeCString(PopupPtr->Name))) return Err;;
1202 return writeMenuDefinitionList(PopupPtr->SubItems);
1203}
1204
1205Error ResourceFileWriter::writeMenuDefinitionList(
1206 const MenuDefinitionList &List) {
1207 for (auto &Def : List.Definitions) {
1208 uint16_t Flags = Def->getResFlags();
1209 // Last element receives an additional 0x80 flag.
1210 const uint16_t LastElementFlag = 0x0080;
1211 if (&Def == &List.Definitions.back())
1212 Flags |= LastElementFlag;
1213
1214 RETURN_IF_ERROR(writeMenuDefinition(Def, Flags))if (auto Err = (writeMenuDefinition(Def, Flags))) return Err;;
1215 }
1216 return Error::success();
1217}
1218
1219Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
1220 // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
1221 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
1222 writeInt<uint32_t>(0);
1223
1224 return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
1225}
1226
1227// --- StringTableResource helpers. --- //
1228
1229class BundleResource : public RCResource {
1230public:
1231 using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1232 BundleType Bundle;
1233
1234 BundleResource(const BundleType &StrBundle)
1235 : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
1236 IntOrString getResourceType() const override { return 6; }
1237
1238 ResourceKind getKind() const override { return RkStringTableBundle; }
1239 static bool classof(const RCResource *Res) {
1240 return Res->getKind() == RkStringTableBundle;
1241 }
1242 Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1243};
1244
1245Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1246 return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1247}
1248
1249Error ResourceFileWriter::insertStringIntoBundle(
1250 StringTableInfo::Bundle &Bundle, uint16_t StringID,
1251 const std::vector<StringRef> &String) {
1252 uint16_t StringLoc = StringID & 15;
1253 if (Bundle.Data[StringLoc])
1254 return createError("Multiple STRINGTABLE strings located under ID " +
1255 Twine(StringID));
1256 Bundle.Data[StringLoc] = String;
1257 return Error::success();
1258}
1259
1260Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1261 auto *Res = cast<BundleResource>(Base);
1262 for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1263 // The string format is a tiny bit different here. We
1264 // first output the size of the string, and then the string itself
1265 // (which is not null-terminated).
1266 SmallVector<UTF16, 128> Data;
1267 if (Res->Bundle.Data[ID]) {
1268 bool IsLongString;
1269 for (StringRef S : *Res->Bundle.Data[ID])
1270 RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull,if (auto Err = (processString(S, NullHandlingMethod::CutAtDoubleNull
, IsLongString, Data, Params.CodePage))) return Err;
1271 IsLongString, Data, Params.CodePage))if (auto Err = (processString(S, NullHandlingMethod::CutAtDoubleNull
, IsLongString, Data, Params.CodePage))) return Err;
;
1272 if (AppendNull)
1273 Data.push_back('\0');
1274 }
1275 RETURN_IF_ERROR(if (auto Err = (checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"
))) return Err;
1276 checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"))if (auto Err = (checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"
))) return Err;
;
1277 writeInt<uint16_t>(Data.size());
1278 for (auto Char : Data)
1279 writeInt(Char);
1280 }
1281 return Error::success();
1282}
1283
1284Error ResourceFileWriter::dumpAllStringTables() {
1285 for (auto Key : StringTableData.BundleList) {
1286 auto Iter = StringTableData.BundleData.find(Key);
1287 assert(Iter != StringTableData.BundleData.end())(static_cast <bool> (Iter != StringTableData.BundleData
.end()) ? void (0) : __assert_fail ("Iter != StringTableData.BundleData.end()"
, "llvm/tools/llvm-rc/ResourceFileWriter.cpp", 1287, __extension__
__PRETTY_FUNCTION__))
;
1288
1289 // For a moment, revert the context info to moment of bundle declaration.
1290 ContextKeeper RAII(this);
1291 ObjectData = Iter->second.DeclTimeInfo;
1292
1293 BundleResource Res(Iter->second);
1294 // Bundle #(k+1) contains keys [16k, 16k + 15].
1295 Res.setName(Key.first + 1);
1296 RETURN_IF_ERROR(visitStringTableBundle(&Res))if (auto Err = (visitStringTableBundle(&Res))) return Err
;
;
1297 }
1298 return Error::success();
1299}
1300
1301// --- UserDefinedResource helpers. --- //
1302
1303Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
1304 auto *Res = cast<UserDefinedResource>(Base);
1305
1306 if (Res->IsFileResource)
1307 return appendFile(Res->FileLoc);
1308
1309 for (auto &Elem : Res->Contents) {
1310 if (Elem.isInt()) {
1311 RETURN_IF_ERROR(if (auto Err = (checkRCInt(Elem.getInt(), "Number in user-defined resource"
))) return Err;
1312 checkRCInt(Elem.getInt(), "Number in user-defined resource"))if (auto Err = (checkRCInt(Elem.getInt(), "Number in user-defined resource"
))) return Err;
;
1313 writeRCInt(Elem.getInt());
1314 continue;
1315 }
1316
1317 SmallVector<UTF16, 128> ProcessedString;
1318 bool IsLongString;
1319 RETURN_IF_ERROR(if (auto Err = (processString(Elem.getString(), NullHandlingMethod
::UserResource, IsLongString, ProcessedString, Params.CodePage
))) return Err;
1320 processString(Elem.getString(), NullHandlingMethod::UserResource,if (auto Err = (processString(Elem.getString(), NullHandlingMethod
::UserResource, IsLongString, ProcessedString, Params.CodePage
))) return Err;
1321 IsLongString, ProcessedString, Params.CodePage))if (auto Err = (processString(Elem.getString(), NullHandlingMethod
::UserResource, IsLongString, ProcessedString, Params.CodePage
))) return Err;
;
1322
1323 for (auto Ch : ProcessedString) {
1324 if (IsLongString) {
1325 writeInt(Ch);
1326 continue;
1327 }
1328
1329 RETURN_IF_ERROR(checkNumberFits<uint8_t>(if (auto Err = (checkNumberFits<uint8_t>( Ch, "Character in narrow string in user-defined resource"
))) return Err;
1330 Ch, "Character in narrow string in user-defined resource"))if (auto Err = (checkNumberFits<uint8_t>( Ch, "Character in narrow string in user-defined resource"
))) return Err;
;
1331 writeInt<uint8_t>(Ch);
1332 }
1333 }
1334
1335 return Error::success();
1336}
1337
1338// --- VersionInfoResourceResource helpers. --- //
1339
1340Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
1341 // Output the header if the block has name.
1342 bool OutputHeader = Blk.Name != "";
1343 uint64_t LengthLoc;
1344
1345 padStream(sizeof(uint32_t));
1346 if (OutputHeader) {
1347 LengthLoc = writeInt<uint16_t>(0);
1348 writeInt<uint16_t>(0);
1349 writeInt<uint16_t>(1); // true
1350 RETURN_IF_ERROR(writeCString(Blk.Name))if (auto Err = (writeCString(Blk.Name))) return Err;;
1351 padStream(sizeof(uint32_t));
1352 }
1353
1354 for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
1355 VersionInfoStmt *ItemPtr = Item.get();
1356
1357 if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
1358 RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr))if (auto Err = (writeVersionInfoBlock(*BlockPtr))) return Err
;
;
1359 continue;
1360 }
1361
1362 auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
1363 RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr))if (auto Err = (writeVersionInfoValue(*ValuePtr))) return Err
;
;
1364 }
1365
1366 if (OutputHeader) {
1367 uint64_t CurLoc = tell();
1368 writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1369 }
1370
1371 return Error::success();
1372}
1373
1374Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
1375 // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
1376 // is a mapping from the key (string) to the value (a sequence of ints or
1377 // a sequence of strings).
1378 //
1379 // If integers are to be written: width of each integer written depends on
1380 // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
1381 // ValueLength defined in structure referenced below is then the total
1382 // number of bytes taken by these integers.
1383 //
1384 // If strings are to be written: characters are always WORDs.
1385 // Moreover, '\0' character is written after the last string, and between
1386 // every two strings separated by comma (if strings are not comma-separated,
1387 // they're simply concatenated). ValueLength is equal to the number of WORDs
1388 // written (that is, half of the bytes written).
1389 //
1390 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
1391 bool HasStrings = false, HasInts = false;
1392 for (auto &Item : Val.Values)
1393 (Item.isInt() ? HasInts : HasStrings) = true;
1394
1395 assert((HasStrings || HasInts) && "VALUE must have at least one argument")(static_cast <bool> ((HasStrings || HasInts) &&
"VALUE must have at least one argument") ? void (0) : __assert_fail
("(HasStrings || HasInts) && \"VALUE must have at least one argument\""
, "llvm/tools/llvm-rc/ResourceFileWriter.cpp", 1395, __extension__
__PRETTY_FUNCTION__))
;
1396 if (HasStrings && HasInts)
1397 return createError(Twine("VALUE ") + Val.Key +
1398 " cannot contain both strings and integers");
1399
1400 padStream(sizeof(uint32_t));
1401 auto LengthLoc = writeInt<uint16_t>(0);
1402 auto ValLengthLoc = writeInt<uint16_t>(0);
1403 writeInt<uint16_t>(HasStrings);
1404 RETURN_IF_ERROR(writeCString(Val.Key))if (auto Err = (writeCString(Val.Key))) return Err;;
1405 padStream(sizeof(uint32_t));
1406
1407 auto DataLoc = tell();
1408 for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
1409 auto &Item = Val.Values[Id];
1410 if (Item.isInt()) {
1411 auto Value = Item.getInt();
1412 RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"))if (auto Err = (checkRCInt(Value, "VERSIONINFO integer value"
))) return Err;
;
1413 writeRCInt(Value);
1414 continue;
1415 }
1416
1417 bool WriteTerminator =
1418 Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
1419 RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator))if (auto Err = (writeCString(Item.getString(), WriteTerminator
))) return Err;
;
1420 }
1421
1422 auto CurLoc = tell();
1423 auto ValueLength = CurLoc - DataLoc;
1424 if (HasStrings) {
1425 assert(ValueLength % 2 == 0)(static_cast <bool> (ValueLength % 2 == 0) ? void (0) :
__assert_fail ("ValueLength % 2 == 0", "llvm/tools/llvm-rc/ResourceFileWriter.cpp"
, 1425, __extension__ __PRETTY_FUNCTION__))
;
1426 ValueLength /= 2;
1427 }
1428 writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1429 writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
1430 return Error::success();
1431}
1432
1433template <typename Ty>
1434static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
1435 const Ty &Default) {
1436 auto Iter = Map.find(Key);
1437 if (Iter != Map.end())
1438 return Iter->getValue();
1439 return Default;
1440}
1441
1442Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
1443 auto *Res = cast<VersionInfoResource>(Base);
1444
1445 const auto &FixedData = Res->FixedData;
1446
1447 struct /* VS_FIXEDFILEINFO */ {
1448 ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
1449 ulittle32_t StructVersion = ulittle32_t(0x10000);
1450 // It's weird to have most-significant DWORD first on the little-endian
1451 // machines, but let it be this way.
1452 ulittle32_t FileVersionMS;
1453 ulittle32_t FileVersionLS;
1454 ulittle32_t ProductVersionMS;
1455 ulittle32_t ProductVersionLS;
1456 ulittle32_t FileFlagsMask;
1457 ulittle32_t FileFlags;
1458 ulittle32_t FileOS;
1459 ulittle32_t FileType;
1460 ulittle32_t FileSubtype;
1461 // MS implementation seems to always set these fields to 0.
1462 ulittle32_t FileDateMS = ulittle32_t(0);
1463 ulittle32_t FileDateLS = ulittle32_t(0);
1464 } FixedInfo;
1465
1466 // First, VS_VERSIONINFO.
1467 auto LengthLoc = writeInt<uint16_t>(0);
1468 writeInt<uint16_t>(sizeof(FixedInfo));
1469 writeInt<uint16_t>(0);
1470 cantFail(writeCString("VS_VERSION_INFO"));
1471 padStream(sizeof(uint32_t));
1472
1473 using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
1474 auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
1475 static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
1476 if (!FixedData.IsTypePresent[(int)Type])
1477 return DefaultOut;
1478 return FixedData.FixedInfo[(int)Type];
1479 };
1480
1481 auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
1482 RETURN_IF_ERROR(checkNumberFits<uint16_t>(if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(FileVer.begin(), FileVer.end()), "FILEVERSION fields"))) return
Err;
1483 *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"))if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(FileVer.begin(), FileVer.end()), "FILEVERSION fields"))) return
Err;
;
1484 FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
1485 FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
1486
1487 auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
1488 RETURN_IF_ERROR(checkNumberFits<uint16_t>(if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(ProdVer.begin(), ProdVer.end()), "PRODUCTVERSION fields"))) return
Err;
1489 *std::max_element(ProdVer.begin(), ProdVer.end()),if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(ProdVer.begin(), ProdVer.end()), "PRODUCTVERSION fields"))) return
Err;
1490 "PRODUCTVERSION fields"))if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(ProdVer.begin(), ProdVer.end()), "PRODUCTVERSION fields"))) return
Err;
;
1491 FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
1492 FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
1493
1494 FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
1495 FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
1496 FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
1497 FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
1498 FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
1499
1500 writeObject(FixedInfo);
1501 padStream(sizeof(uint32_t));
1502
1503 RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock))if (auto Err = (writeVersionInfoBlock(Res->MainBlock))) return
Err;
;
1504
1505 // FIXME: check overflow?
1506 writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
1507
1508 return Error::success();
1509}
1510
1511Expected<std::unique_ptr<MemoryBuffer>>
1512ResourceFileWriter::loadFile(StringRef File) const {
1513 SmallString<128> Path;
1514 SmallString<128> Cwd;
1515 std::unique_ptr<MemoryBuffer> Result;
1516
1517 // 0. The file path is absolute or has a root directory, so we shouldn't
1518 // try to append it on top of other base directories. (An absolute path
1519 // must have a root directory, but e.g. the path "\dir\file" on windows
1520 // isn't considered absolute, but it does have a root directory. As long as
1521 // sys::path::append doesn't handle appending an absolute path or a path
1522 // starting with a root directory on top of a base, we must handle this
1523 // case separately at the top. C++17's path::append handles that case
1524 // properly though, so if using that to append paths below, this early
1525 // exception case could be removed.)
1526 if (sys::path::has_root_directory(File))
1527 return errorOrToExpected(MemoryBuffer::getFile(
1528 File, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1529
1530 // 1. The current working directory.
1531 sys::fs::current_path(Cwd);
1532 Path.assign(Cwd.begin(), Cwd.end());
1533 sys::path::append(Path, File);
1534 if (sys::fs::exists(Path))
1535 return errorOrToExpected(MemoryBuffer::getFile(
1536 Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1537
1538 // 2. The directory of the input resource file, if it is different from the
1539 // current working directory.
1540 StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1541 Path.assign(InputFileDir.begin(), InputFileDir.end());
1542 sys::path::append(Path, File);
1543 if (sys::fs::exists(Path))
1544 return errorOrToExpected(MemoryBuffer::getFile(
1545 Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1546
1547 // 3. All of the include directories specified on the command line.
1548 for (StringRef ForceInclude : Params.Include) {
1549 Path.assign(ForceInclude.begin(), ForceInclude.end());
1550 sys::path::append(Path, File);
1551 if (sys::fs::exists(Path))
1552 return errorOrToExpected(MemoryBuffer::getFile(
1553 Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1554 }
1555
1556 if (!Params.NoInclude) {
1557 if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File))
1558 return errorOrToExpected(MemoryBuffer::getFile(
1559 *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1560 }
1561
1562 return make_error<StringError>("error : file not found : " + Twine(File),
1563 inconvertibleErrorCode());
1564}
1565
1566} // namespace rc
1567} // namespace llvm