xref: /llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.cpp (revision b35bdb1d7b31c2267af14ee7e097c78d89c3ddb0)
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 
23 using namespace llvm::support;
24 
25 // Take an expression returning llvm::Error and forward the error if it exists.
26 #define RETURN_IF_ERROR(Expr)                                                  \
27   if (auto Err = (Expr))                                                       \
28     return Err;
29 
30 namespace llvm {
31 namespace 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.
36 class ContextKeeper {
37   ResourceFileWriter *FileWriter;
38   ResourceFileWriter::ObjectInfo SavedInfo;
39 
40 public:
ContextKeeper(ResourceFileWriter * V)41   ContextKeeper(ResourceFileWriter *V)
42       : FileWriter(V), SavedInfo(V->ObjectData) {}
~ContextKeeper()43   ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
44 };
45 
createError(const Twine & Message,std::errc Type=std::errc::invalid_argument)46 static 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 
checkNumberFits(uint32_t Number,size_t MaxBits,const Twine & FieldName)51 static Error checkNumberFits(uint32_t Number, size_t MaxBits,
52                              const Twine &FieldName) {
53   assert(1 <= MaxBits && MaxBits <= 32);
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 
61 template <typename FitType>
checkNumberFits(uint32_t Number,const Twine & FieldName)62 static 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.
67 template <typename FitType>
checkSignedNumberFits(uint32_t Number,const Twine & FieldName,bool CanBeNegative)68 static 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 
checkRCInt(RCInt Number,const Twine & FieldName)85 static Error checkRCInt(RCInt Number, const Twine &FieldName) {
86   if (Number.isLong())
87     return Error::success();
88   return checkNumberFits<uint16_t>(Number, FieldName);
89 }
90 
checkIntOrString(IntOrString Value,const Twine & FieldName)91 static 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 
stripQuotes(StringRef & Str,bool & IsLongString)97 static 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.starts_with_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.");
109   return true;
110 }
111 
cp1252ToUnicode(unsigned char C)112 static 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 
134 enum 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.
processString(StringRef Str,NullHandlingMethod NullHandler,bool & IsLongString,SmallVectorImpl<UTF16> & Result,int CodePage)146 static 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");
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('"'));
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));
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));
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'));
295         break;
296 
297       case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
298         RETURN_IF_ERROR(AddRes('\n'));
299         break;
300 
301       case 'r':
302         RETURN_IF_ERROR(AddRes('\r'));
303         break;
304 
305       case 'T':
306       case 't':
307         RETURN_IF_ERROR(AddRes('\t'));
308         break;
309 
310       case '\\':
311         RETURN_IF_ERROR(AddRes('\\'));
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('"'));
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('\\'));
329           RETURN_IF_ERROR(AddRes(TypeChar));
330         }
331         break;
332       }
333 
334       continue;
335     }
336 
337     // If nothing interesting happens, just output the character.
338     RETURN_IF_ERROR(AddRes(CurChar));
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 
writeObject(const ArrayRef<uint8_t> Data)363 uint64_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 
writeCString(StringRef Str,bool WriteTerminator)369 Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
370   SmallVector<UTF16, 128> ProcessedString;
371   bool IsLongString;
372   RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
373                                 IsLongString, ProcessedString,
374                                 Params.CodePage));
375   for (auto Ch : ProcessedString)
376     writeInt<uint16_t>(Ch);
377   if (WriteTerminator)
378     writeInt<uint16_t>(0);
379   return Error::success();
380 }
381 
writeIdentifier(const IntOrString & Ident)382 Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
383   return writeIntOrString(Ident);
384 }
385 
writeIntOrString(const IntOrString & Value)386 Error 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 
writeRCInt(RCInt Value)395 void ResourceFileWriter::writeRCInt(RCInt Value) {
396   if (Value.isLong())
397     writeInt<uint32_t>(Value);
398   else
399     writeInt<uint16_t>(Value);
400 }
401 
appendFile(StringRef Filename)402 Error 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 
padStream(uint64_t Length)414 void ResourceFileWriter::padStream(uint64_t Length) {
415   assert(Length > 0);
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 
handleError(Error Err,const RCResource * Res)423 Error 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 
visitNullResource(const RCResource * Res)432 Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
433   return writeResource(Res, &ResourceFileWriter::writeNullBody);
434 }
435 
visitAcceleratorsResource(const RCResource * Res)436 Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
437   return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
438 }
439 
visitBitmapResource(const RCResource * Res)440 Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
441   return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
442 }
443 
visitCursorResource(const RCResource * Res)444 Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
445   return handleError(visitIconOrCursorResource(Res), Res);
446 }
447 
visitDialogResource(const RCResource * Res)448 Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
449   return writeResource(Res, &ResourceFileWriter::writeDialogBody);
450 }
451 
visitIconResource(const RCResource * Res)452 Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
453   return handleError(visitIconOrCursorResource(Res), Res);
454 }
455 
visitCaptionStmt(const CaptionStmt * Stmt)456 Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
457   ObjectData.Caption = Stmt->Value;
458   return Error::success();
459 }
460 
visitClassStmt(const ClassStmt * Stmt)461 Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
462   ObjectData.Class = Stmt->Value;
463   return Error::success();
464 }
465 
visitHTMLResource(const RCResource * Res)466 Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
467   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
468 }
469 
visitMenuResource(const RCResource * Res)470 Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
471   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
472 }
473 
visitMenuExResource(const RCResource * Res)474 Error ResourceFileWriter::visitMenuExResource(const RCResource *Res) {
475   return writeResource(Res, &ResourceFileWriter::writeMenuExBody);
476 }
477 
visitStringTableResource(const RCResource * Base)478 Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
479   const auto *Res = cast<StringTableResource>(Base);
480 
481   ContextKeeper RAII(this);
482   RETURN_IF_ERROR(Res->applyStmts(this));
483 
484   for (auto &String : Res->Table) {
485     RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
486     uint16_t BundleID = String.first >> 4;
487     StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
488     auto &BundleData = StringTableData.BundleData;
489     auto Iter = BundleData.find(Key);
490 
491     if (Iter == BundleData.end()) {
492       // Need to create a bundle.
493       StringTableData.BundleList.push_back(Key);
494       auto EmplaceResult = BundleData.emplace(
495           Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
496       assert(EmplaceResult.second && "Could not create a bundle");
497       Iter = EmplaceResult.first;
498     }
499 
500     RETURN_IF_ERROR(
501         insertStringIntoBundle(Iter->second, String.first, String.second));
502   }
503 
504   return Error::success();
505 }
506 
visitUserDefinedResource(const RCResource * Res)507 Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
508   return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
509 }
510 
visitVersionInfoResource(const RCResource * Res)511 Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
512   return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
513 }
514 
visitCharacteristicsStmt(const CharacteristicsStmt * Stmt)515 Error ResourceFileWriter::visitCharacteristicsStmt(
516     const CharacteristicsStmt *Stmt) {
517   ObjectData.Characteristics = Stmt->Value;
518   return Error::success();
519 }
520 
visitExStyleStmt(const ExStyleStmt * Stmt)521 Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) {
522   ObjectData.ExStyle = Stmt->Value;
523   return Error::success();
524 }
525 
visitFontStmt(const FontStmt * Stmt)526 Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
527   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
528   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
529   RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
530   ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
531                             Stmt->Charset};
532   ObjectData.Font.emplace(Font);
533   return Error::success();
534 }
535 
visitLanguageStmt(const LanguageResource * Stmt)536 Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
537   RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
538   RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
539   ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
540   return Error::success();
541 }
542 
visitStyleStmt(const StyleStmt * Stmt)543 Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
544   ObjectData.Style = Stmt->Value;
545   return Error::success();
546 }
547 
visitVersionStmt(const VersionStmt * Stmt)548 Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
549   ObjectData.VersionInfo = Stmt->Value;
550   return Error::success();
551 }
552 
visitMenuStmt(const MenuStmt * Stmt)553 Error ResourceFileWriter::visitMenuStmt(const MenuStmt *Stmt) {
554   ObjectData.Menu = Stmt->Value;
555   return Error::success();
556 }
557 
writeResource(const RCResource * Res,Error (ResourceFileWriter::* BodyWriter)(const RCResource *))558 Error ResourceFileWriter::writeResource(
559     const RCResource *Res,
560     Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
561   // We don't know the sizes yet.
562   object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
563   uint64_t HeaderLoc = writeObject(HeaderPrefix);
564 
565   auto ResType = Res->getResourceType();
566   RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
567   RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
568   RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
569   RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
570 
571   // Apply the resource-local optional statements.
572   ContextKeeper RAII(this);
573   RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
574 
575   padStream(sizeof(uint32_t));
576   object::WinResHeaderSuffix HeaderSuffix{
577       ulittle32_t(0), // DataVersion; seems to always be 0
578       ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
579       ulittle32_t(ObjectData.VersionInfo),
580       ulittle32_t(ObjectData.Characteristics)};
581   writeObject(HeaderSuffix);
582 
583   uint64_t DataLoc = tell();
584   RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
585   // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
586 
587   // Update the sizes.
588   HeaderPrefix.DataSize = tell() - DataLoc;
589   HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
590   writeObjectAt(HeaderPrefix, HeaderLoc);
591   padStream(sizeof(uint32_t));
592 
593   return Error::success();
594 }
595 
596 // --- NullResource helpers. --- //
597 
writeNullBody(const RCResource *)598 Error ResourceFileWriter::writeNullBody(const RCResource *) {
599   return Error::success();
600 }
601 
602 // --- AcceleratorsResource helpers. --- //
603 
writeSingleAccelerator(const AcceleratorsResource::Accelerator & Obj,bool IsLastItem)604 Error ResourceFileWriter::writeSingleAccelerator(
605     const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
606   using Accelerator = AcceleratorsResource::Accelerator;
607   using Opt = Accelerator::Options;
608 
609   struct AccelTableEntry {
610     ulittle16_t Flags;
611     ulittle16_t ANSICode;
612     ulittle16_t Id;
613     uint16_t Padding;
614   } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
615 
616   bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
617 
618   // Remove ASCII flags (which doesn't occur in .res files).
619   Entry.Flags = Obj.Flags & ~Opt::ASCII;
620 
621   if (IsLastItem)
622     Entry.Flags |= 0x80;
623 
624   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
625   Entry.Id = ulittle16_t(Obj.Id);
626 
627   auto createAccError = [&Obj](const char *Msg) {
628     return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
629   };
630 
631   if (IsASCII && IsVirtKey)
632     return createAccError("Accelerator can't be both ASCII and VIRTKEY");
633 
634   if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
635     return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
636                           " accelerators");
637 
638   if (Obj.Event.isInt()) {
639     if (!IsASCII && !IsVirtKey)
640       return createAccError(
641           "Accelerator with a numeric event must be either ASCII"
642           " or VIRTKEY");
643 
644     uint32_t EventVal = Obj.Event.getInt();
645     RETURN_IF_ERROR(
646         checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
647     Entry.ANSICode = ulittle16_t(EventVal);
648     writeObject(Entry);
649     return Error::success();
650   }
651 
652   StringRef Str = Obj.Event.getString();
653   bool IsWide;
654   stripQuotes(Str, IsWide);
655 
656   if (Str.size() == 0 || Str.size() > 2)
657     return createAccError(
658         "Accelerator string events should have length 1 or 2");
659 
660   if (Str[0] == '^') {
661     if (Str.size() == 1)
662       return createAccError("No character following '^' in accelerator event");
663     if (IsVirtKey)
664       return createAccError(
665           "VIRTKEY accelerator events can't be preceded by '^'");
666 
667     char Ch = Str[1];
668     if (Ch >= 'a' && Ch <= 'z')
669       Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
670     else if (Ch >= 'A' && Ch <= 'Z')
671       Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
672     else
673       return createAccError("Control character accelerator event should be"
674                             " alphabetic");
675 
676     writeObject(Entry);
677     return Error::success();
678   }
679 
680   if (Str.size() == 2)
681     return createAccError("Event string should be one-character, possibly"
682                           " preceded by '^'");
683 
684   uint8_t EventCh = Str[0];
685   // The original tool just warns in this situation. We chose to fail.
686   if (IsVirtKey && !isalnum(EventCh))
687     return createAccError("Non-alphanumeric characters cannot describe virtual"
688                           " keys");
689   if (EventCh > 0x7F)
690     return createAccError("Non-ASCII description of accelerator");
691 
692   if (IsVirtKey)
693     EventCh = toupper(EventCh);
694   Entry.ANSICode = ulittle16_t(EventCh);
695   writeObject(Entry);
696   return Error::success();
697 }
698 
writeAcceleratorsBody(const RCResource * Base)699 Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
700   auto *Res = cast<AcceleratorsResource>(Base);
701   size_t AcceleratorId = 0;
702   for (auto &Acc : Res->Accelerators) {
703     ++AcceleratorId;
704     RETURN_IF_ERROR(
705         writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
706   }
707   return Error::success();
708 }
709 
710 // --- BitmapResource helpers. --- //
711 
writeBitmapBody(const RCResource * Base)712 Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
713   StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
714   bool IsLong;
715   stripQuotes(Filename, IsLong);
716 
717   auto File = loadFile(Filename);
718   if (!File)
719     return File.takeError();
720 
721   StringRef Buffer = (*File)->getBuffer();
722 
723   // Skip the 14 byte BITMAPFILEHEADER.
724   constexpr size_t BITMAPFILEHEADER_size = 14;
725   if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
726       Buffer[1] != 'M')
727     return createError("Incorrect bitmap file.");
728 
729   *FS << Buffer.substr(BITMAPFILEHEADER_size);
730   return Error::success();
731 }
732 
733 // --- CursorResource and IconResource helpers. --- //
734 
735 // ICONRESDIR structure. Describes a single icon in resource group.
736 //
737 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
738 struct IconResDir {
739   uint8_t Width;
740   uint8_t Height;
741   uint8_t ColorCount;
742   uint8_t Reserved;
743 };
744 
745 // CURSORDIR structure. Describes a single cursor in resource group.
746 //
747 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
748 struct CursorDir {
749   ulittle16_t Width;
750   ulittle16_t Height;
751 };
752 
753 // RESDIRENTRY structure, stripped from the last item. Stripping made
754 // for compatibility with RESDIR.
755 //
756 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
757 struct ResourceDirEntryStart {
758   union {
759     CursorDir Cursor; // Used in CURSOR resources.
760     IconResDir Icon;  // Used in .ico and .cur files, and ICON resources.
761   };
762   ulittle16_t Planes;   // HotspotX (.cur files but not CURSOR resource).
763   ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
764   ulittle32_t Size;
765   // ulittle32_t ImageOffset;  // Offset to image data (ICONDIRENTRY only).
766   // ulittle16_t IconID;       // Resource icon ID (RESDIR only).
767 };
768 
769 // BITMAPINFOHEADER structure. Describes basic information about the bitmap
770 // being read.
771 //
772 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
773 struct BitmapInfoHeader {
774   ulittle32_t Size;
775   ulittle32_t Width;
776   ulittle32_t Height;
777   ulittle16_t Planes;
778   ulittle16_t BitCount;
779   ulittle32_t Compression;
780   ulittle32_t SizeImage;
781   ulittle32_t XPelsPerMeter;
782   ulittle32_t YPelsPerMeter;
783   ulittle32_t ClrUsed;
784   ulittle32_t ClrImportant;
785 };
786 
787 // Group icon directory header. Called ICONDIR in .ico/.cur files and
788 // NEWHEADER in .res files.
789 //
790 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
791 struct GroupIconDir {
792   ulittle16_t Reserved; // Always 0.
793   ulittle16_t ResType;  // 1 for icons, 2 for cursors.
794   ulittle16_t ResCount; // Number of items.
795 };
796 
797 enum class IconCursorGroupType { Icon, Cursor };
798 
799 class SingleIconCursorResource : public RCResource {
800 public:
801   IconCursorGroupType Type;
802   const ResourceDirEntryStart &Header;
803   ArrayRef<uint8_t> Image;
804 
SingleIconCursorResource(IconCursorGroupType ResourceType,const ResourceDirEntryStart & HeaderEntry,ArrayRef<uint8_t> ImageData,uint16_t Flags)805   SingleIconCursorResource(IconCursorGroupType ResourceType,
806                            const ResourceDirEntryStart &HeaderEntry,
807                            ArrayRef<uint8_t> ImageData, uint16_t Flags)
808       : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
809         Image(ImageData) {}
810 
getResourceTypeName() const811   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
getResourceType() const812   IntOrString getResourceType() const override {
813     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
814   }
getKind() const815   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
classof(const RCResource * Res)816   static bool classof(const RCResource *Res) {
817     return Res->getKind() == RkSingleCursorOrIconRes;
818   }
819 };
820 
821 class IconCursorGroupResource : public RCResource {
822 public:
823   IconCursorGroupType Type;
824   GroupIconDir Header;
825   std::vector<ResourceDirEntryStart> ItemEntries;
826 
IconCursorGroupResource(IconCursorGroupType ResourceType,const GroupIconDir & HeaderData,std::vector<ResourceDirEntryStart> && Entries)827   IconCursorGroupResource(IconCursorGroupType ResourceType,
828                           const GroupIconDir &HeaderData,
829                           std::vector<ResourceDirEntryStart> &&Entries)
830       : Type(ResourceType), Header(HeaderData),
831         ItemEntries(std::move(Entries)) {}
832 
getResourceTypeName() const833   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
getResourceType() const834   IntOrString getResourceType() const override {
835     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
836   }
getKind() const837   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
classof(const RCResource * Res)838   static bool classof(const RCResource *Res) {
839     return Res->getKind() == RkCursorOrIconGroupRes;
840   }
841 };
842 
writeSingleIconOrCursorBody(const RCResource * Base)843 Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
844   auto *Res = cast<SingleIconCursorResource>(Base);
845   if (Res->Type == IconCursorGroupType::Cursor) {
846     // In case of cursors, two WORDS are appended to the beginning
847     // of the resource: HotspotX (Planes in RESDIRENTRY),
848     // and HotspotY (BitCount).
849     //
850     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
851     //  (Remarks section).
852     writeObject(Res->Header.Planes);
853     writeObject(Res->Header.BitCount);
854   }
855 
856   writeObject(Res->Image);
857   return Error::success();
858 }
859 
writeIconOrCursorGroupBody(const RCResource * Base)860 Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
861   auto *Res = cast<IconCursorGroupResource>(Base);
862   writeObject(Res->Header);
863   for (auto Item : Res->ItemEntries) {
864     writeObject(Item);
865     writeInt(IconCursorID++);
866   }
867   return Error::success();
868 }
869 
visitSingleIconOrCursor(const RCResource * Res)870 Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
871   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
872 }
873 
visitIconOrCursorGroup(const RCResource * Res)874 Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
875   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
876 }
877 
visitIconOrCursorResource(const RCResource * Base)878 Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
879   IconCursorGroupType Type;
880   StringRef FileStr;
881   IntOrString ResName = Base->ResName;
882 
883   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
884     FileStr = IconRes->IconLoc;
885     Type = IconCursorGroupType::Icon;
886   } else {
887     auto *CursorRes = cast<CursorResource>(Base);
888     FileStr = CursorRes->CursorLoc;
889     Type = IconCursorGroupType::Cursor;
890   }
891 
892   bool IsLong;
893   stripQuotes(FileStr, IsLong);
894   auto File = loadFile(FileStr);
895 
896   if (!File)
897     return File.takeError();
898 
899   BinaryStreamReader Reader((*File)->getBuffer(), llvm::endianness::little);
900 
901   // Read the file headers.
902   //   - At the beginning, ICONDIR/NEWHEADER header.
903   //   - Then, a number of RESDIR headers follow. These contain offsets
904   //       to data.
905   const GroupIconDir *Header;
906 
907   RETURN_IF_ERROR(Reader.readObject(Header));
908   if (Header->Reserved != 0)
909     return createError("Incorrect icon/cursor Reserved field; should be 0.");
910   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
911   if (Header->ResType != NeededType)
912     return createError("Incorrect icon/cursor ResType field; should be " +
913                        Twine(NeededType) + ".");
914 
915   uint16_t NumItems = Header->ResCount;
916 
917   // Read single ico/cur headers.
918   std::vector<ResourceDirEntryStart> ItemEntries;
919   ItemEntries.reserve(NumItems);
920   std::vector<uint32_t> ItemOffsets(NumItems);
921   for (size_t ID = 0; ID < NumItems; ++ID) {
922     const ResourceDirEntryStart *Object;
923     RETURN_IF_ERROR(Reader.readObject(Object));
924     ItemEntries.push_back(*Object);
925     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
926   }
927 
928   // Now write each icon/cursors one by one. At first, all the contents
929   // without ICO/CUR header. This is described by SingleIconCursorResource.
930   for (size_t ID = 0; ID < NumItems; ++ID) {
931     // Load the fragment of file.
932     Reader.setOffset(ItemOffsets[ID]);
933     ArrayRef<uint8_t> Image;
934     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
935     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
936                                        Base->MemoryFlags);
937     SingleRes.setName(IconCursorID + ID);
938     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
939   }
940 
941   // Now, write all the headers concatenated into a separate resource.
942   for (size_t ID = 0; ID < NumItems; ++ID) {
943     // We need to rewrite the cursor headers, and fetch actual values
944     // for Planes/BitCount.
945     const auto &OldHeader = ItemEntries[ID];
946     ResourceDirEntryStart NewHeader = OldHeader;
947 
948     if (Type == IconCursorGroupType::Cursor) {
949       NewHeader.Cursor.Width = OldHeader.Icon.Width;
950       // Each cursor in fact stores two bitmaps, one under another.
951       // Height provided in cursor definition describes the height of the
952       // cursor, whereas the value existing in resource definition describes
953       // the height of the bitmap. Therefore, we need to double this height.
954       NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
955 
956       // Two WORDs were written at the beginning of the resource (hotspot
957       // location). This is reflected in Size field.
958       NewHeader.Size += 2 * sizeof(uint16_t);
959     }
960 
961     // Now, we actually need to read the bitmap header to find
962     // the number of planes and the number of bits per pixel.
963     Reader.setOffset(ItemOffsets[ID]);
964     const BitmapInfoHeader *BMPHeader;
965     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
966     if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
967       NewHeader.Planes = BMPHeader->Planes;
968       NewHeader.BitCount = BMPHeader->BitCount;
969     } else {
970       // A PNG .ico file.
971       // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
972       // "The image must be in 32bpp"
973       NewHeader.Planes = 1;
974       NewHeader.BitCount = 32;
975     }
976 
977     ItemEntries[ID] = NewHeader;
978   }
979 
980   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
981   HeaderRes.setName(ResName);
982   if (Base->MemoryFlags & MfPreload) {
983     HeaderRes.MemoryFlags |= MfPreload;
984     HeaderRes.MemoryFlags &= ~MfPure;
985   }
986   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
987 
988   return Error::success();
989 }
990 
991 // --- DialogResource helpers. --- //
992 
writeSingleDialogControl(const Control & Ctl,bool IsExtended)993 Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
994                                                    bool IsExtended) {
995   // Each control should be aligned to DWORD.
996   padStream(sizeof(uint32_t));
997 
998   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
999   IntWithNotMask CtlStyle(TypeInfo.Style);
1000   CtlStyle |= Ctl.Style.value_or(RCInt(0));
1001   uint32_t CtlExtStyle = Ctl.ExtStyle.value_or(0);
1002 
1003   // DIALOG(EX) item header prefix.
1004   if (!IsExtended) {
1005     struct {
1006       ulittle32_t Style;
1007       ulittle32_t ExtStyle;
1008     } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
1009     writeObject(Prefix);
1010   } else {
1011     struct {
1012       ulittle32_t HelpID;
1013       ulittle32_t ExtStyle;
1014       ulittle32_t Style;
1015     } Prefix{ulittle32_t(Ctl.HelpID.value_or(0)), ulittle32_t(CtlExtStyle),
1016              ulittle32_t(CtlStyle.getValue())};
1017     writeObject(Prefix);
1018   }
1019 
1020   // Common fixed-length part.
1021   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
1022       Ctl.X, "Dialog control x-coordinate", true));
1023   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
1024       Ctl.Y, "Dialog control y-coordinate", true));
1025   RETURN_IF_ERROR(
1026       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
1027   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
1028       Ctl.Height, "Dialog control height", false));
1029   struct {
1030     ulittle16_t X;
1031     ulittle16_t Y;
1032     ulittle16_t Width;
1033     ulittle16_t Height;
1034   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
1035            ulittle16_t(Ctl.Height)};
1036   writeObject(Middle);
1037 
1038   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
1039   if (!IsExtended) {
1040     // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
1041     // want to refer to later.
1042     if (Ctl.ID != static_cast<uint32_t>(-1))
1043       RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1044           Ctl.ID, "Control ID in simple DIALOG resource"));
1045     writeInt<uint16_t>(Ctl.ID);
1046   } else {
1047     writeInt<uint32_t>(Ctl.ID);
1048   }
1049 
1050   // Window class - either 0xFFFF + 16-bit integer or a string.
1051   RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
1052 
1053   // Element caption/reference ID. ID is preceded by 0xFFFF.
1054   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
1055   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
1056 
1057   // # bytes of extra creation data count. Don't pass any.
1058   writeInt<uint16_t>(0);
1059 
1060   return Error::success();
1061 }
1062 
writeDialogBody(const RCResource * Base)1063 Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
1064   auto *Res = cast<DialogResource>(Base);
1065 
1066   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1067   const uint32_t DefaultStyle = 0x80880000;
1068   const uint32_t StyleFontFlag = 0x40;
1069   const uint32_t StyleCaptionFlag = 0x00C00000;
1070 
1071   uint32_t UsedStyle = ObjectData.Style.value_or(DefaultStyle);
1072   if (ObjectData.Font)
1073     UsedStyle |= StyleFontFlag;
1074   else
1075     UsedStyle &= ~StyleFontFlag;
1076 
1077   // Actually, in case of empty (but existent) caption, the examined field
1078   // is equal to "\"\"". That's why empty captions are still noticed.
1079   if (ObjectData.Caption != "")
1080     UsedStyle |= StyleCaptionFlag;
1081 
1082   const uint16_t DialogExMagic = 0xFFFF;
1083   uint32_t ExStyle = ObjectData.ExStyle.value_or(0);
1084 
1085   // Write DIALOG(EX) header prefix. These are pretty different.
1086   if (!Res->IsExtended) {
1087     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1088     // In such a case, whole object (in .res file) is equivalent to a
1089     // DIALOGEX. It might lead to access violation/segmentation fault in
1090     // resource readers. For example,
1091     //   1 DIALOG 0, 0, 0, 65432
1092     //   STYLE 0xFFFF0001 {}
1093     // would be compiled to a DIALOGEX with 65432 controls.
1094     if ((UsedStyle >> 16) == DialogExMagic)
1095       return createError("16 higher bits of DIALOG resource style cannot be"
1096                          " equal to 0xFFFF");
1097 
1098     struct {
1099       ulittle32_t Style;
1100       ulittle32_t ExtStyle;
1101     } Prefix{ulittle32_t(UsedStyle),
1102              ulittle32_t(ExStyle)};
1103 
1104     writeObject(Prefix);
1105   } else {
1106     struct {
1107       ulittle16_t Version;
1108       ulittle16_t Magic;
1109       ulittle32_t HelpID;
1110       ulittle32_t ExtStyle;
1111       ulittle32_t Style;
1112     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
1113              ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
1114 
1115     writeObject(Prefix);
1116   }
1117 
1118   // Now, a common part. First, fixed-length fields.
1119   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
1120                                             "Number of dialog controls"));
1121   RETURN_IF_ERROR(
1122       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
1123   RETURN_IF_ERROR(
1124       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
1125   RETURN_IF_ERROR(
1126       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
1127   RETURN_IF_ERROR(
1128       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
1129   struct {
1130     ulittle16_t Count;
1131     ulittle16_t PosX;
1132     ulittle16_t PosY;
1133     ulittle16_t DialogWidth;
1134     ulittle16_t DialogHeight;
1135   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
1136            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
1137            ulittle16_t(Res->Height)};
1138   writeObject(Middle);
1139 
1140   // MENU field.
1141   RETURN_IF_ERROR(writeIntOrString(ObjectData.Menu));
1142 
1143   // Window CLASS field.
1144   RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
1145 
1146   // Window title or a single word equal to 0.
1147   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
1148 
1149   // If there *is* a window font declared, output its data.
1150   auto &Font = ObjectData.Font;
1151   if (Font) {
1152     writeInt<uint16_t>(Font->Size);
1153     // Additional description occurs only in DIALOGEX.
1154     if (Res->IsExtended) {
1155       writeInt<uint16_t>(Font->Weight);
1156       writeInt<uint8_t>(Font->IsItalic);
1157       writeInt<uint8_t>(Font->Charset);
1158     }
1159     RETURN_IF_ERROR(writeCString(Font->Typeface));
1160   }
1161 
1162   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
1163     if (!Err)
1164       return Error::success();
1165     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
1166                                   " control  (ID " + Twine(Ctl.ID) + "):"),
1167                       std::move(Err));
1168   };
1169 
1170   for (auto &Ctl : Res->Controls)
1171     RETURN_IF_ERROR(
1172         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
1173 
1174   return Error::success();
1175 }
1176 
1177 // --- HTMLResource helpers. --- //
1178 
writeHTMLBody(const RCResource * Base)1179 Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
1180   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
1181 }
1182 
1183 // --- MenuResource helpers. --- //
1184 
writeMenuDefinition(const std::unique_ptr<MenuDefinition> & Def,uint16_t Flags)1185 Error ResourceFileWriter::writeMenuDefinition(
1186     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1187   // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-menuitemtemplate
1188   assert(Def);
1189   const MenuDefinition *DefPtr = Def.get();
1190 
1191   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
1192     writeInt<uint16_t>(Flags);
1193     // Some resource files use -1, i.e. UINT32_MAX, for empty menu items.
1194     if (MenuItemPtr->Id != static_cast<uint32_t>(-1))
1195       RETURN_IF_ERROR(
1196           checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
1197     writeInt<uint16_t>(MenuItemPtr->Id);
1198     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
1199     return Error::success();
1200   }
1201 
1202   if (isa<MenuSeparator>(DefPtr)) {
1203     writeInt<uint16_t>(Flags);
1204     writeInt<uint32_t>(0);
1205     return Error::success();
1206   }
1207 
1208   auto *PopupPtr = cast<PopupItem>(DefPtr);
1209   writeInt<uint16_t>(Flags);
1210   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
1211   return writeMenuDefinitionList(PopupPtr->SubItems);
1212 }
1213 
writeMenuExDefinition(const std::unique_ptr<MenuDefinition> & Def,uint16_t Flags)1214 Error ResourceFileWriter::writeMenuExDefinition(
1215     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1216   // https://learn.microsoft.com/en-us/windows/win32/menurc/menuex-template-item
1217   assert(Def);
1218   const MenuDefinition *DefPtr = Def.get();
1219 
1220   padStream(sizeof(uint32_t));
1221   if (auto *MenuItemPtr = dyn_cast<MenuExItem>(DefPtr)) {
1222     writeInt<uint32_t>(MenuItemPtr->Type);
1223     writeInt<uint32_t>(MenuItemPtr->State);
1224     writeInt<uint32_t>(MenuItemPtr->Id);
1225     writeInt<uint16_t>(Flags);
1226     padStream(sizeof(uint16_t));
1227     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
1228     return Error::success();
1229   }
1230 
1231   auto *PopupPtr = cast<PopupExItem>(DefPtr);
1232   writeInt<uint32_t>(PopupPtr->Type);
1233   writeInt<uint32_t>(PopupPtr->State);
1234   writeInt<uint32_t>(PopupPtr->Id);
1235   writeInt<uint16_t>(Flags);
1236   padStream(sizeof(uint16_t));
1237   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
1238   writeInt<uint32_t>(PopupPtr->HelpId);
1239   return writeMenuExDefinitionList(PopupPtr->SubItems);
1240 }
1241 
writeMenuDefinitionList(const MenuDefinitionList & List)1242 Error ResourceFileWriter::writeMenuDefinitionList(
1243     const MenuDefinitionList &List) {
1244   for (auto &Def : List.Definitions) {
1245     uint16_t Flags = Def->getResFlags();
1246     // Last element receives an additional 0x80 flag.
1247     const uint16_t LastElementFlag = 0x0080;
1248     if (&Def == &List.Definitions.back())
1249       Flags |= LastElementFlag;
1250 
1251     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
1252   }
1253   return Error::success();
1254 }
1255 
writeMenuExDefinitionList(const MenuDefinitionList & List)1256 Error ResourceFileWriter::writeMenuExDefinitionList(
1257     const MenuDefinitionList &List) {
1258   for (auto &Def : List.Definitions) {
1259     uint16_t Flags = Def->getResFlags();
1260     // Last element receives an additional 0x80 flag.
1261     const uint16_t LastElementFlag = 0x0080;
1262     if (&Def == &List.Definitions.back())
1263       Flags |= LastElementFlag;
1264 
1265     RETURN_IF_ERROR(writeMenuExDefinition(Def, Flags));
1266   }
1267   return Error::success();
1268 }
1269 
writeMenuBody(const RCResource * Base)1270 Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
1271   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
1272   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
1273   writeInt<uint32_t>(0);
1274 
1275   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
1276 }
1277 
writeMenuExBody(const RCResource * Base)1278 Error ResourceFileWriter::writeMenuExBody(const RCResource *Base) {
1279   // At first, MENUEX_TEMPLATE_HEADER structure.
1280   // Ref:
1281   // https://learn.microsoft.com/en-us/windows/win32/menurc/menuex-template-header
1282   writeInt<uint16_t>(1);
1283   writeInt<uint16_t>(4);
1284   writeInt<uint32_t>(0);
1285 
1286   return writeMenuExDefinitionList(cast<MenuExResource>(Base)->Elements);
1287 }
1288 
1289 // --- StringTableResource helpers. --- //
1290 
1291 class BundleResource : public RCResource {
1292 public:
1293   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1294   BundleType Bundle;
1295 
BundleResource(const BundleType & StrBundle)1296   BundleResource(const BundleType &StrBundle)
1297       : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
getResourceType() const1298   IntOrString getResourceType() const override { return 6; }
1299 
getKind() const1300   ResourceKind getKind() const override { return RkStringTableBundle; }
classof(const RCResource * Res)1301   static bool classof(const RCResource *Res) {
1302     return Res->getKind() == RkStringTableBundle;
1303   }
getResourceTypeName() const1304   Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1305 };
1306 
visitStringTableBundle(const RCResource * Res)1307 Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1308   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1309 }
1310 
insertStringIntoBundle(StringTableInfo::Bundle & Bundle,uint16_t StringID,const std::vector<StringRef> & String)1311 Error ResourceFileWriter::insertStringIntoBundle(
1312     StringTableInfo::Bundle &Bundle, uint16_t StringID,
1313     const std::vector<StringRef> &String) {
1314   uint16_t StringLoc = StringID & 15;
1315   if (Bundle.Data[StringLoc])
1316     return createError("Multiple STRINGTABLE strings located under ID " +
1317                        Twine(StringID));
1318   Bundle.Data[StringLoc] = String;
1319   return Error::success();
1320 }
1321 
writeStringTableBundleBody(const RCResource * Base)1322 Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1323   auto *Res = cast<BundleResource>(Base);
1324   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1325     // The string format is a tiny bit different here. We
1326     // first output the size of the string, and then the string itself
1327     // (which is not null-terminated).
1328     SmallVector<UTF16, 128> Data;
1329     if (Res->Bundle.Data[ID]) {
1330       bool IsLongString;
1331       for (StringRef S : *Res->Bundle.Data[ID])
1332         RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull,
1333                                       IsLongString, Data, Params.CodePage));
1334       if (AppendNull)
1335         Data.push_back('\0');
1336     }
1337     RETURN_IF_ERROR(
1338         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
1339     writeInt<uint16_t>(Data.size());
1340     for (auto Char : Data)
1341       writeInt(Char);
1342   }
1343   return Error::success();
1344 }
1345 
dumpAllStringTables()1346 Error ResourceFileWriter::dumpAllStringTables() {
1347   for (auto Key : StringTableData.BundleList) {
1348     auto Iter = StringTableData.BundleData.find(Key);
1349     assert(Iter != StringTableData.BundleData.end());
1350 
1351     // For a moment, revert the context info to moment of bundle declaration.
1352     ContextKeeper RAII(this);
1353     ObjectData = Iter->second.DeclTimeInfo;
1354 
1355     BundleResource Res(Iter->second);
1356     // Bundle #(k+1) contains keys [16k, 16k + 15].
1357     Res.setName(Key.first + 1);
1358     RETURN_IF_ERROR(visitStringTableBundle(&Res));
1359   }
1360   return Error::success();
1361 }
1362 
1363 // --- UserDefinedResource helpers. --- //
1364 
writeUserDefinedBody(const RCResource * Base)1365 Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
1366   auto *Res = cast<UserDefinedResource>(Base);
1367 
1368   if (Res->IsFileResource)
1369     return appendFile(Res->FileLoc);
1370 
1371   for (auto &Elem : Res->Contents) {
1372     if (Elem.isInt()) {
1373       RETURN_IF_ERROR(
1374           checkRCInt(Elem.getInt(), "Number in user-defined resource"));
1375       writeRCInt(Elem.getInt());
1376       continue;
1377     }
1378 
1379     SmallVector<UTF16, 128> ProcessedString;
1380     bool IsLongString;
1381     RETURN_IF_ERROR(
1382         processString(Elem.getString(), NullHandlingMethod::UserResource,
1383                       IsLongString, ProcessedString, Params.CodePage));
1384 
1385     for (auto Ch : ProcessedString) {
1386       if (IsLongString) {
1387         writeInt(Ch);
1388         continue;
1389       }
1390 
1391       RETURN_IF_ERROR(checkNumberFits<uint8_t>(
1392           Ch, "Character in narrow string in user-defined resource"));
1393       writeInt<uint8_t>(Ch);
1394     }
1395   }
1396 
1397   return Error::success();
1398 }
1399 
1400 // --- VersionInfoResourceResource helpers. --- //
1401 
writeVersionInfoBlock(const VersionInfoBlock & Blk)1402 Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
1403   // Output the header if the block has name.
1404   bool OutputHeader = Blk.Name != "";
1405   uint64_t LengthLoc;
1406 
1407   padStream(sizeof(uint32_t));
1408   if (OutputHeader) {
1409     LengthLoc = writeInt<uint16_t>(0);
1410     writeInt<uint16_t>(0);
1411     writeInt<uint16_t>(1); // true
1412     RETURN_IF_ERROR(writeCString(Blk.Name));
1413     padStream(sizeof(uint32_t));
1414   }
1415 
1416   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
1417     VersionInfoStmt *ItemPtr = Item.get();
1418 
1419     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
1420       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
1421       continue;
1422     }
1423 
1424     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
1425     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
1426   }
1427 
1428   if (OutputHeader) {
1429     uint64_t CurLoc = tell();
1430     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1431   }
1432 
1433   return Error::success();
1434 }
1435 
writeVersionInfoValue(const VersionInfoValue & Val)1436 Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
1437   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
1438   // is a mapping from the key (string) to the value (a sequence of ints or
1439   // a sequence of strings).
1440   //
1441   // If integers are to be written: width of each integer written depends on
1442   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
1443   // ValueLength defined in structure referenced below is then the total
1444   // number of bytes taken by these integers.
1445   //
1446   // If strings are to be written: characters are always WORDs.
1447   // Moreover, '\0' character is written after the last string, and between
1448   // every two strings separated by comma (if strings are not comma-separated,
1449   // they're simply concatenated). ValueLength is equal to the number of WORDs
1450   // written (that is, half of the bytes written).
1451   //
1452   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
1453   bool HasStrings = false, HasInts = false;
1454   for (auto &Item : Val.Values)
1455     (Item.isInt() ? HasInts : HasStrings) = true;
1456 
1457   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
1458   if (HasStrings && HasInts)
1459     return createError(Twine("VALUE ") + Val.Key +
1460                        " cannot contain both strings and integers");
1461 
1462   padStream(sizeof(uint32_t));
1463   auto LengthLoc = writeInt<uint16_t>(0);
1464   auto ValLengthLoc = writeInt<uint16_t>(0);
1465   writeInt<uint16_t>(HasStrings);
1466   RETURN_IF_ERROR(writeCString(Val.Key));
1467   padStream(sizeof(uint32_t));
1468 
1469   auto DataLoc = tell();
1470   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
1471     auto &Item = Val.Values[Id];
1472     if (Item.isInt()) {
1473       auto Value = Item.getInt();
1474       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
1475       writeRCInt(Value);
1476       continue;
1477     }
1478 
1479     bool WriteTerminator =
1480         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
1481     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
1482   }
1483 
1484   auto CurLoc = tell();
1485   auto ValueLength = CurLoc - DataLoc;
1486   if (HasStrings) {
1487     assert(ValueLength % 2 == 0);
1488     ValueLength /= 2;
1489   }
1490   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1491   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
1492   return Error::success();
1493 }
1494 
1495 template <typename Ty>
getWithDefault(const StringMap<Ty> & Map,StringRef Key,const Ty & Default)1496 static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
1497                          const Ty &Default) {
1498   auto Iter = Map.find(Key);
1499   if (Iter != Map.end())
1500     return Iter->getValue();
1501   return Default;
1502 }
1503 
writeVersionInfoBody(const RCResource * Base)1504 Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
1505   auto *Res = cast<VersionInfoResource>(Base);
1506 
1507   const auto &FixedData = Res->FixedData;
1508 
1509   struct /* VS_FIXEDFILEINFO */ {
1510     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
1511     ulittle32_t StructVersion = ulittle32_t(0x10000);
1512     // It's weird to have most-significant DWORD first on the little-endian
1513     // machines, but let it be this way.
1514     ulittle32_t FileVersionMS;
1515     ulittle32_t FileVersionLS;
1516     ulittle32_t ProductVersionMS;
1517     ulittle32_t ProductVersionLS;
1518     ulittle32_t FileFlagsMask;
1519     ulittle32_t FileFlags;
1520     ulittle32_t FileOS;
1521     ulittle32_t FileType;
1522     ulittle32_t FileSubtype;
1523     // MS implementation seems to always set these fields to 0.
1524     ulittle32_t FileDateMS = ulittle32_t(0);
1525     ulittle32_t FileDateLS = ulittle32_t(0);
1526   } FixedInfo;
1527 
1528   // First, VS_VERSIONINFO.
1529   auto LengthLoc = writeInt<uint16_t>(0);
1530   writeInt<uint16_t>(sizeof(FixedInfo));
1531   writeInt<uint16_t>(0);
1532   cantFail(writeCString("VS_VERSION_INFO"));
1533   padStream(sizeof(uint32_t));
1534 
1535   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
1536   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
1537     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
1538     if (!FixedData.IsTypePresent[(int)Type])
1539       return DefaultOut;
1540     return FixedData.FixedInfo[(int)Type];
1541   };
1542 
1543   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
1544   RETURN_IF_ERROR(checkNumberFits<uint16_t>(*llvm::max_element(FileVer),
1545                                             "FILEVERSION fields"));
1546   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
1547   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
1548 
1549   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
1550   RETURN_IF_ERROR(checkNumberFits<uint16_t>(*llvm::max_element(ProdVer),
1551                                             "PRODUCTVERSION fields"));
1552   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
1553   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
1554 
1555   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
1556   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
1557   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
1558   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
1559   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
1560 
1561   writeObject(FixedInfo);
1562   padStream(sizeof(uint32_t));
1563 
1564   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
1565 
1566   // FIXME: check overflow?
1567   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
1568 
1569   return Error::success();
1570 }
1571 
1572 Expected<std::unique_ptr<MemoryBuffer>>
loadFile(StringRef File) const1573 ResourceFileWriter::loadFile(StringRef File) const {
1574   SmallString<128> Path;
1575   SmallString<128> Cwd;
1576   std::unique_ptr<MemoryBuffer> Result;
1577 
1578   // 0. The file path is absolute or has a root directory, so we shouldn't
1579   // try to append it on top of other base directories. (An absolute path
1580   // must have a root directory, but e.g. the path "\dir\file" on windows
1581   // isn't considered absolute, but it does have a root directory. As long as
1582   // sys::path::append doesn't handle appending an absolute path or a path
1583   // starting with a root directory on top of a base, we must handle this
1584   // case separately at the top. C++17's path::append handles that case
1585   // properly though, so if using that to append paths below, this early
1586   // exception case could be removed.)
1587   if (sys::path::has_root_directory(File))
1588     return errorOrToExpected(MemoryBuffer::getFile(
1589         File, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1590 
1591   // 1. The current working directory.
1592   sys::fs::current_path(Cwd);
1593   Path.assign(Cwd.begin(), Cwd.end());
1594   sys::path::append(Path, File);
1595   if (sys::fs::exists(Path))
1596     return errorOrToExpected(MemoryBuffer::getFile(
1597         Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1598 
1599   // 2. The directory of the input resource file, if it is different from the
1600   // current working directory.
1601   StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1602   Path.assign(InputFileDir.begin(), InputFileDir.end());
1603   sys::path::append(Path, File);
1604   if (sys::fs::exists(Path))
1605     return errorOrToExpected(MemoryBuffer::getFile(
1606         Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1607 
1608   // 3. All of the include directories specified on the command line.
1609   for (StringRef ForceInclude : Params.Include) {
1610     Path.assign(ForceInclude.begin(), ForceInclude.end());
1611     sys::path::append(Path, File);
1612     if (sys::fs::exists(Path))
1613       return errorOrToExpected(MemoryBuffer::getFile(
1614           Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1615   }
1616 
1617   if (!Params.NoInclude) {
1618     if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File))
1619       return errorOrToExpected(MemoryBuffer::getFile(
1620           *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false));
1621   }
1622 
1623   return make_error<StringError>("error : file not found : " + Twine(File),
1624                                  inconvertibleErrorCode());
1625 }
1626 
1627 } // namespace rc
1628 } // namespace llvm
1629