aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm14/tools/llvm-rc
diff options
context:
space:
mode:
authorvitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
committervitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
commit6ffe9e53658409f212834330e13564e4952558f6 (patch)
tree85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/llvm14/tools/llvm-rc
parent726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff)
downloadydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/llvm14/tools/llvm-rc')
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/Opts.td65
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp1567
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h217
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp111
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h34
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp856
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h189
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp294
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h954
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp367
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h81
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def39
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h61
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td62
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp755
-rw-r--r--contrib/libs/llvm14/tools/llvm-rc/ya.make44
16 files changed, 5696 insertions, 0 deletions
diff --git a/contrib/libs/llvm14/tools/llvm-rc/Opts.td b/contrib/libs/llvm14/tools/llvm-rc/Opts.td
new file mode 100644
index 0000000000..6d9c0e2601
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/Opts.td
@@ -0,0 +1,65 @@
+include "llvm/Option/OptParser.td"
+
+// All the switches can be preceded by either '/' or '-'.
+// These options seem to be important for the tool
+// and should be implemented.
+
+class S<string name, string help> :
+ Separate<["/", "-"], name>, HelpText<help>;
+
+class JS<string name, string help> :
+ JoinedOrSeparate<["/", "-"], name>, HelpText<help>;
+
+class F<string name, string help> : Flag<["/", "-"], name>, HelpText<help>;
+
+class F_nodoc<string name> : Flag<["/", "-"], name>;
+class S_nodoc<string name> : Separate<["/", "-"], name>;
+
+def fileout : JS<"FO", "Change the output file location.">;
+
+def define : JS<"D", "Define a symbol for the C preprocessor.">;
+def undef : JS<"U", "Undefine a symbol for the C preprocessor.">;
+
+def lang_id : JS<"L", "Set the default language identifier.">;
+def lang_name : S<"LN", "Set the default language name.">;
+
+def includepath : JS<"I", "Add an include path.">;
+def noinclude : F<"X", "Ignore 'include' variable.">;
+
+def add_null : F<"N", "Null-terminate all strings in the string table.">;
+
+def dupid_nowarn : F<"Y", "Suppress warnings on duplicate resource IDs.">;
+
+def verbose : F<"V", "Be verbose.">;
+def help : F<"?", "Display this help and exit.">;
+def h : F<"H", "Display this help and exit.">, Alias<help>;
+
+def codepage : JS<"C", "Set the codepage used for input strings.">;
+
+// llvm-rc specific options:
+
+def dry_run : F<"dry-run", "Don't compile the input; only try to parse it.">;
+
+def no_preprocess : F<"no-preprocess", "Don't try to preprocess the input file.">;
+
+// Print (but do not run) the commands to run for preprocessing
+def _HASH_HASH_HASH : F_nodoc<"###">;
+
+// Unused switches (at least for now). These will stay unimplemented
+// in an early stage of development and can be ignored. However, we need to
+// parse them in order to preserve the compatibility with the original tool.
+
+def nologo : F_nodoc<"NOLOGO">;
+def r : F_nodoc<"R">;
+def sl : F_nodoc<"SL">;
+
+// (Codepages support.)
+def w : F_nodoc<"W">;
+
+// (Support of MUI and similar.)
+def fm : S_nodoc<"FM">;
+def q : S_nodoc<"Q">;
+def g : F_nodoc<"G">;
+def gn : F_nodoc<"GN">;
+def g1 : F_nodoc<"G1">;
+def g2 : F_nodoc<"G2">;
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp
new file mode 100644
index 0000000000..60287a37f0
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.cpp
@@ -0,0 +1,1567 @@
+//===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements the visitor serializing resources to a .res stream.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceFileWriter.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+using namespace llvm::support;
+
+// Take an expression returning llvm::Error and forward the error if it exists.
+#define RETURN_IF_ERROR(Expr) \
+ if (auto Err = (Expr)) \
+ return Err;
+
+namespace llvm {
+namespace rc {
+
+// Class that employs RAII to save the current FileWriter object state
+// and revert to it as soon as we leave the scope. This is useful if resources
+// declare their own resource-local statements.
+class ContextKeeper {
+ ResourceFileWriter *FileWriter;
+ ResourceFileWriter::ObjectInfo SavedInfo;
+
+public:
+ ContextKeeper(ResourceFileWriter *V)
+ : FileWriter(V), SavedInfo(V->ObjectData) {}
+ ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
+};
+
+static Error createError(const Twine &Message,
+ std::errc Type = std::errc::invalid_argument) {
+ return make_error<StringError>(Message, std::make_error_code(Type));
+}
+
+static Error checkNumberFits(uint32_t Number, size_t MaxBits,
+ const Twine &FieldName) {
+ assert(1 <= MaxBits && MaxBits <= 32);
+ if (!(Number >> MaxBits))
+ return Error::success();
+ return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
+ Twine(MaxBits) + " bits.",
+ std::errc::value_too_large);
+}
+
+template <typename FitType>
+static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
+ return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
+}
+
+// A similar function for signed integers.
+template <typename FitType>
+static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
+ bool CanBeNegative) {
+ int32_t SignedNum = Number;
+ if (SignedNum < std::numeric_limits<FitType>::min() ||
+ SignedNum > std::numeric_limits<FitType>::max())
+ return createError(FieldName + " (" + Twine(SignedNum) +
+ ") does not fit in " + Twine(sizeof(FitType) * 8) +
+ "-bit signed integer type.",
+ std::errc::value_too_large);
+
+ if (!CanBeNegative && SignedNum < 0)
+ return createError(FieldName + " (" + Twine(SignedNum) +
+ ") cannot be negative.");
+
+ return Error::success();
+}
+
+static Error checkRCInt(RCInt Number, const Twine &FieldName) {
+ if (Number.isLong())
+ return Error::success();
+ return checkNumberFits<uint16_t>(Number, FieldName);
+}
+
+static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
+ if (!Value.isInt())
+ return Error::success();
+ return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
+}
+
+static bool stripQuotes(StringRef &Str, bool &IsLongString) {
+ if (!Str.contains('"'))
+ return false;
+
+ // Just take the contents of the string, checking if it's been marked long.
+ IsLongString = Str.startswith_insensitive("L");
+ if (IsLongString)
+ Str = Str.drop_front();
+
+ bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
+ (void)StripSuccess;
+ assert(StripSuccess && "Strings should be enclosed in quotes.");
+ return true;
+}
+
+static UTF16 cp1252ToUnicode(unsigned char C) {
+ static const UTF16 Map80[] = {
+ 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
+ 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
+ };
+ if (C >= 0x80 && C <= 0x9F)
+ return Map80[C - 0x80];
+ return C;
+}
+
+// Describes a way to handle '\0' characters when processing the string.
+// rc.exe tool sometimes behaves in a weird way in postprocessing.
+// If the string to be output is equivalent to a C-string (e.g. in MENU
+// titles), string is (predictably) truncated after first 0-byte.
+// When outputting a string table, the behavior is equivalent to appending
+// '\0\0' at the end of the string, and then stripping the string
+// before the first '\0\0' occurrence.
+// Finally, when handling strings in user-defined resources, 0-bytes
+// aren't stripped, nor do they terminate the string.
+
+enum class NullHandlingMethod {
+ UserResource, // Don't terminate string on '\0'.
+ CutAtNull, // Terminate string on '\0'.
+ CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
+};
+
+// Parses an identifier or string and returns a processed version of it:
+// * Strip the string boundary quotes.
+// * Convert the input code page characters to UTF16.
+// * Squash "" to a single ".
+// * Replace the escape sequences with their processed version.
+// For identifiers, this is no-op.
+static Error processString(StringRef Str, NullHandlingMethod NullHandler,
+ bool &IsLongString, SmallVectorImpl<UTF16> &Result,
+ int CodePage) {
+ bool IsString = stripQuotes(Str, IsLongString);
+ SmallVector<UTF16, 128> Chars;
+
+ // Convert the input bytes according to the chosen codepage.
+ if (CodePage == CpUtf8) {
+ convertUTF8ToUTF16String(Str, Chars);
+ } else if (CodePage == CpWin1252) {
+ for (char C : Str)
+ Chars.push_back(cp1252ToUnicode((unsigned char)C));
+ } else {
+ // For other, unknown codepages, only allow plain ASCII input.
+ for (char C : Str) {
+ if ((unsigned char)C > 0x7F)
+ return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
+ ") can't be interpreted in the current codepage");
+ Chars.push_back((unsigned char)C);
+ }
+ }
+
+ if (!IsString) {
+ // It's an identifier if it's not a string. Make all characters uppercase.
+ for (UTF16 &Ch : Chars) {
+ assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
+ Ch = toupper(Ch);
+ }
+ Result.swap(Chars);
+ return Error::success();
+ }
+ Result.reserve(Chars.size());
+ size_t Pos = 0;
+
+ auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
+ if (!IsLongString) {
+ if (NullHandler == NullHandlingMethod::UserResource) {
+ // Narrow strings in user-defined resources are *not* output in
+ // UTF-16 format.
+ if (Char > 0xFF)
+ return createError("Non-8-bit codepoint (" + Twine(Char) +
+ ") can't occur in a user-defined narrow string");
+ }
+ }
+
+ Result.push_back(Char);
+ return Error::success();
+ };
+ auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
+ if (!IsLongString) {
+ // Escaped chars in narrow strings have to be interpreted according to
+ // the chosen code page.
+ if (Char > 0xFF)
+ return createError("Non-8-bit escaped char (" + Twine(Char) +
+ ") can't occur in narrow string");
+ if (CodePage == CpUtf8) {
+ if (Char >= 0x80)
+ return createError("Unable to interpret single byte (" + Twine(Char) +
+ ") as UTF-8");
+ } else if (CodePage == CpWin1252) {
+ Char = cp1252ToUnicode(Char);
+ } else {
+ // Unknown/unsupported codepage, only allow ASCII input.
+ if (Char > 0x7F)
+ return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
+ ") can't "
+ "occur in a non-Unicode string");
+ }
+ }
+
+ return AddRes(Char);
+ };
+
+ while (Pos < Chars.size()) {
+ UTF16 CurChar = Chars[Pos];
+ ++Pos;
+
+ // Strip double "".
+ if (CurChar == '"') {
+ if (Pos == Chars.size() || Chars[Pos] != '"')
+ return createError("Expected \"\"");
+ ++Pos;
+ RETURN_IF_ERROR(AddRes('"'));
+ continue;
+ }
+
+ if (CurChar == '\\') {
+ UTF16 TypeChar = Chars[Pos];
+ ++Pos;
+
+ if (TypeChar == 'x' || TypeChar == 'X') {
+ // Read a hex number. Max number of characters to read differs between
+ // narrow and wide strings.
+ UTF16 ReadInt = 0;
+ size_t RemainingChars = IsLongString ? 4 : 2;
+ // We don't want to read non-ASCII hex digits. std:: functions past
+ // 0xFF invoke UB.
+ //
+ // FIXME: actually, Microsoft version probably doesn't check this
+ // condition and uses their Unicode version of 'isxdigit'. However,
+ // there are some hex-digit Unicode character outside of ASCII, and
+ // some of these are actually accepted by rc.exe, the notable example
+ // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
+ // instead of ASCII digits in \x... escape sequence and get accepted.
+ // However, the resulting hexcodes seem totally unpredictable.
+ // We think it's infeasible to try to reproduce this behavior, nor to
+ // put effort in order to detect it.
+ while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
+ if (!isxdigit(Chars[Pos]))
+ break;
+ char Digit = tolower(Chars[Pos]);
+ ++Pos;
+
+ ReadInt <<= 4;
+ if (isdigit(Digit))
+ ReadInt |= Digit - '0';
+ else
+ ReadInt |= Digit - 'a' + 10;
+
+ --RemainingChars;
+ }
+
+ RETURN_IF_ERROR(AddEscapedChar(ReadInt));
+ continue;
+ }
+
+ if (TypeChar >= '0' && TypeChar < '8') {
+ // Read an octal number. Note that we've already read the first digit.
+ UTF16 ReadInt = TypeChar - '0';
+ size_t RemainingChars = IsLongString ? 6 : 2;
+
+ while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
+ Chars[Pos] < '8') {
+ ReadInt <<= 3;
+ ReadInt |= Chars[Pos] - '0';
+ --RemainingChars;
+ ++Pos;
+ }
+
+ RETURN_IF_ERROR(AddEscapedChar(ReadInt));
+
+ continue;
+ }
+
+ switch (TypeChar) {
+ case 'A':
+ case 'a':
+ // Windows '\a' translates into '\b' (Backspace).
+ RETURN_IF_ERROR(AddRes('\b'));
+ break;
+
+ case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
+ RETURN_IF_ERROR(AddRes('\n'));
+ break;
+
+ case 'r':
+ RETURN_IF_ERROR(AddRes('\r'));
+ break;
+
+ case 'T':
+ case 't':
+ RETURN_IF_ERROR(AddRes('\t'));
+ break;
+
+ case '\\':
+ RETURN_IF_ERROR(AddRes('\\'));
+ break;
+
+ case '"':
+ // RC accepts \" only if another " comes afterwards; then, \"" means
+ // a single ".
+ if (Pos == Chars.size() || Chars[Pos] != '"')
+ return createError("Expected \\\"\"");
+ ++Pos;
+ RETURN_IF_ERROR(AddRes('"'));
+ break;
+
+ default:
+ // If TypeChar means nothing, \ is should be output to stdout with
+ // following char. However, rc.exe consumes these characters when
+ // dealing with wide strings.
+ if (!IsLongString) {
+ RETURN_IF_ERROR(AddRes('\\'));
+ RETURN_IF_ERROR(AddRes(TypeChar));
+ }
+ break;
+ }
+
+ continue;
+ }
+
+ // If nothing interesting happens, just output the character.
+ RETURN_IF_ERROR(AddRes(CurChar));
+ }
+
+ switch (NullHandler) {
+ case NullHandlingMethod::CutAtNull:
+ for (size_t Pos = 0; Pos < Result.size(); ++Pos)
+ if (Result[Pos] == '\0')
+ Result.resize(Pos);
+ break;
+
+ case NullHandlingMethod::CutAtDoubleNull:
+ for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
+ if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
+ Result.resize(Pos);
+ if (Result.size() > 0 && Result.back() == '\0')
+ Result.pop_back();
+ break;
+
+ case NullHandlingMethod::UserResource:
+ break;
+ }
+
+ return Error::success();
+}
+
+uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
+ uint64_t Result = tell();
+ FS->write((const char *)Data.begin(), Data.size());
+ return Result;
+}
+
+Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
+ SmallVector<UTF16, 128> ProcessedString;
+ bool IsLongString;
+ RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
+ IsLongString, ProcessedString,
+ Params.CodePage));
+ for (auto Ch : ProcessedString)
+ writeInt<uint16_t>(Ch);
+ if (WriteTerminator)
+ writeInt<uint16_t>(0);
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
+ return writeIntOrString(Ident);
+}
+
+Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
+ if (!Value.isInt())
+ return writeCString(Value.getString());
+
+ writeInt<uint16_t>(0xFFFF);
+ writeInt<uint16_t>(Value.getInt());
+ return Error::success();
+}
+
+void ResourceFileWriter::writeRCInt(RCInt Value) {
+ if (Value.isLong())
+ writeInt<uint32_t>(Value);
+ else
+ writeInt<uint16_t>(Value);
+}
+
+Error ResourceFileWriter::appendFile(StringRef Filename) {
+ bool IsLong;
+ stripQuotes(Filename, IsLong);
+
+ auto File = loadFile(Filename);
+ if (!File)
+ return File.takeError();
+
+ *FS << (*File)->getBuffer();
+ return Error::success();
+}
+
+void ResourceFileWriter::padStream(uint64_t Length) {
+ assert(Length > 0);
+ uint64_t Location = tell();
+ Location %= Length;
+ uint64_t Pad = (Length - Location) % Length;
+ for (uint64_t i = 0; i < Pad; ++i)
+ writeInt<uint8_t>(0);
+}
+
+Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
+ if (Err)
+ return joinErrors(createError("Error in " + Res->getResourceTypeName() +
+ " statement (ID " + Twine(Res->ResName) +
+ "): "),
+ std::move(Err));
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeNullBody);
+}
+
+Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
+}
+
+Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
+}
+
+Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
+ return handleError(visitIconOrCursorResource(Res), Res);
+}
+
+Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeDialogBody);
+}
+
+Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
+ return handleError(visitIconOrCursorResource(Res), Res);
+}
+
+Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
+ ObjectData.Caption = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
+ ObjectData.Class = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
+}
+
+Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeMenuBody);
+}
+
+Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
+ const auto *Res = cast<StringTableResource>(Base);
+
+ ContextKeeper RAII(this);
+ RETURN_IF_ERROR(Res->applyStmts(this));
+
+ for (auto &String : Res->Table) {
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
+ uint16_t BundleID = String.first >> 4;
+ StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
+ auto &BundleData = StringTableData.BundleData;
+ auto Iter = BundleData.find(Key);
+
+ if (Iter == BundleData.end()) {
+ // Need to create a bundle.
+ StringTableData.BundleList.push_back(Key);
+ auto EmplaceResult = BundleData.emplace(
+ Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
+ assert(EmplaceResult.second && "Could not create a bundle");
+ Iter = EmplaceResult.first;
+ }
+
+ RETURN_IF_ERROR(
+ insertStringIntoBundle(Iter->second, String.first, String.second));
+ }
+
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
+}
+
+Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
+}
+
+Error ResourceFileWriter::visitCharacteristicsStmt(
+ const CharacteristicsStmt *Stmt) {
+ ObjectData.Characteristics = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) {
+ ObjectData.ExStyle = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
+ RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
+ ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
+ Stmt->Charset};
+ ObjectData.Font.emplace(Font);
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
+ RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
+ RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
+ ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
+ ObjectData.Style = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
+ ObjectData.VersionInfo = Stmt->Value;
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeResource(
+ const RCResource *Res,
+ Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
+ // We don't know the sizes yet.
+ object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
+ uint64_t HeaderLoc = writeObject(HeaderPrefix);
+
+ auto ResType = Res->getResourceType();
+ RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
+ RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
+ RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
+ RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
+
+ // Apply the resource-local optional statements.
+ ContextKeeper RAII(this);
+ RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
+
+ padStream(sizeof(uint32_t));
+ object::WinResHeaderSuffix HeaderSuffix{
+ ulittle32_t(0), // DataVersion; seems to always be 0
+ ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
+ ulittle32_t(ObjectData.VersionInfo),
+ ulittle32_t(ObjectData.Characteristics)};
+ writeObject(HeaderSuffix);
+
+ uint64_t DataLoc = tell();
+ RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
+ // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
+
+ // Update the sizes.
+ HeaderPrefix.DataSize = tell() - DataLoc;
+ HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
+ writeObjectAt(HeaderPrefix, HeaderLoc);
+ padStream(sizeof(uint32_t));
+
+ return Error::success();
+}
+
+// --- NullResource helpers. --- //
+
+Error ResourceFileWriter::writeNullBody(const RCResource *) {
+ return Error::success();
+}
+
+// --- AcceleratorsResource helpers. --- //
+
+Error ResourceFileWriter::writeSingleAccelerator(
+ const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
+ using Accelerator = AcceleratorsResource::Accelerator;
+ using Opt = Accelerator::Options;
+
+ struct AccelTableEntry {
+ ulittle16_t Flags;
+ ulittle16_t ANSICode;
+ ulittle16_t Id;
+ uint16_t Padding;
+ } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
+
+ bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
+
+ // Remove ASCII flags (which doesn't occur in .res files).
+ Entry.Flags = Obj.Flags & ~Opt::ASCII;
+
+ if (IsLastItem)
+ Entry.Flags |= 0x80;
+
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
+ Entry.Id = ulittle16_t(Obj.Id);
+
+ auto createAccError = [&Obj](const char *Msg) {
+ return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
+ };
+
+ if (IsASCII && IsVirtKey)
+ return createAccError("Accelerator can't be both ASCII and VIRTKEY");
+
+ if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
+ return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
+ " accelerators");
+
+ if (Obj.Event.isInt()) {
+ if (!IsASCII && !IsVirtKey)
+ return createAccError(
+ "Accelerator with a numeric event must be either ASCII"
+ " or VIRTKEY");
+
+ uint32_t EventVal = Obj.Event.getInt();
+ RETURN_IF_ERROR(
+ checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
+ Entry.ANSICode = ulittle16_t(EventVal);
+ writeObject(Entry);
+ return Error::success();
+ }
+
+ StringRef Str = Obj.Event.getString();
+ bool IsWide;
+ stripQuotes(Str, IsWide);
+
+ if (Str.size() == 0 || Str.size() > 2)
+ return createAccError(
+ "Accelerator string events should have length 1 or 2");
+
+ if (Str[0] == '^') {
+ if (Str.size() == 1)
+ return createAccError("No character following '^' in accelerator event");
+ if (IsVirtKey)
+ return createAccError(
+ "VIRTKEY accelerator events can't be preceded by '^'");
+
+ char Ch = Str[1];
+ if (Ch >= 'a' && Ch <= 'z')
+ Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
+ else if (Ch >= 'A' && Ch <= 'Z')
+ Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
+ else
+ return createAccError("Control character accelerator event should be"
+ " alphabetic");
+
+ writeObject(Entry);
+ return Error::success();
+ }
+
+ if (Str.size() == 2)
+ return createAccError("Event string should be one-character, possibly"
+ " preceded by '^'");
+
+ uint8_t EventCh = Str[0];
+ // The original tool just warns in this situation. We chose to fail.
+ if (IsVirtKey && !isalnum(EventCh))
+ return createAccError("Non-alphanumeric characters cannot describe virtual"
+ " keys");
+ if (EventCh > 0x7F)
+ return createAccError("Non-ASCII description of accelerator");
+
+ if (IsVirtKey)
+ EventCh = toupper(EventCh);
+ Entry.ANSICode = ulittle16_t(EventCh);
+ writeObject(Entry);
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
+ auto *Res = cast<AcceleratorsResource>(Base);
+ size_t AcceleratorId = 0;
+ for (auto &Acc : Res->Accelerators) {
+ ++AcceleratorId;
+ RETURN_IF_ERROR(
+ writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
+ }
+ return Error::success();
+}
+
+// --- BitmapResource helpers. --- //
+
+Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
+ StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
+ bool IsLong;
+ stripQuotes(Filename, IsLong);
+
+ auto File = loadFile(Filename);
+ if (!File)
+ return File.takeError();
+
+ StringRef Buffer = (*File)->getBuffer();
+
+ // Skip the 14 byte BITMAPFILEHEADER.
+ constexpr size_t BITMAPFILEHEADER_size = 14;
+ if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
+ Buffer[1] != 'M')
+ return createError("Incorrect bitmap file.");
+
+ *FS << Buffer.substr(BITMAPFILEHEADER_size);
+ return Error::success();
+}
+
+// --- CursorResource and IconResource helpers. --- //
+
+// ICONRESDIR structure. Describes a single icon in resource group.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
+struct IconResDir {
+ uint8_t Width;
+ uint8_t Height;
+ uint8_t ColorCount;
+ uint8_t Reserved;
+};
+
+// CURSORDIR structure. Describes a single cursor in resource group.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
+struct CursorDir {
+ ulittle16_t Width;
+ ulittle16_t Height;
+};
+
+// RESDIRENTRY structure, stripped from the last item. Stripping made
+// for compatibility with RESDIR.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
+struct ResourceDirEntryStart {
+ union {
+ CursorDir Cursor; // Used in CURSOR resources.
+ IconResDir Icon; // Used in .ico and .cur files, and ICON resources.
+ };
+ ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource).
+ ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
+ ulittle32_t Size;
+ // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only).
+ // ulittle16_t IconID; // Resource icon ID (RESDIR only).
+};
+
+// BITMAPINFOHEADER structure. Describes basic information about the bitmap
+// being read.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
+struct BitmapInfoHeader {
+ ulittle32_t Size;
+ ulittle32_t Width;
+ ulittle32_t Height;
+ ulittle16_t Planes;
+ ulittle16_t BitCount;
+ ulittle32_t Compression;
+ ulittle32_t SizeImage;
+ ulittle32_t XPelsPerMeter;
+ ulittle32_t YPelsPerMeter;
+ ulittle32_t ClrUsed;
+ ulittle32_t ClrImportant;
+};
+
+// Group icon directory header. Called ICONDIR in .ico/.cur files and
+// NEWHEADER in .res files.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
+struct GroupIconDir {
+ ulittle16_t Reserved; // Always 0.
+ ulittle16_t ResType; // 1 for icons, 2 for cursors.
+ ulittle16_t ResCount; // Number of items.
+};
+
+enum class IconCursorGroupType { Icon, Cursor };
+
+class SingleIconCursorResource : public RCResource {
+public:
+ IconCursorGroupType Type;
+ const ResourceDirEntryStart &Header;
+ ArrayRef<uint8_t> Image;
+
+ SingleIconCursorResource(IconCursorGroupType ResourceType,
+ const ResourceDirEntryStart &HeaderEntry,
+ ArrayRef<uint8_t> ImageData, uint16_t Flags)
+ : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
+ Image(ImageData) {}
+
+ Twine getResourceTypeName() const override { return "Icon/cursor image"; }
+ IntOrString getResourceType() const override {
+ return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
+ }
+ ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkSingleCursorOrIconRes;
+ }
+};
+
+class IconCursorGroupResource : public RCResource {
+public:
+ IconCursorGroupType Type;
+ GroupIconDir Header;
+ std::vector<ResourceDirEntryStart> ItemEntries;
+
+ IconCursorGroupResource(IconCursorGroupType ResourceType,
+ const GroupIconDir &HeaderData,
+ std::vector<ResourceDirEntryStart> &&Entries)
+ : Type(ResourceType), Header(HeaderData),
+ ItemEntries(std::move(Entries)) {}
+
+ Twine getResourceTypeName() const override { return "Icon/cursor group"; }
+ IntOrString getResourceType() const override {
+ return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
+ }
+ ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkCursorOrIconGroupRes;
+ }
+};
+
+Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
+ auto *Res = cast<SingleIconCursorResource>(Base);
+ if (Res->Type == IconCursorGroupType::Cursor) {
+ // In case of cursors, two WORDS are appended to the beginning
+ // of the resource: HotspotX (Planes in RESDIRENTRY),
+ // and HotspotY (BitCount).
+ //
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
+ // (Remarks section).
+ writeObject(Res->Header.Planes);
+ writeObject(Res->Header.BitCount);
+ }
+
+ writeObject(Res->Image);
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
+ auto *Res = cast<IconCursorGroupResource>(Base);
+ writeObject(Res->Header);
+ for (auto Item : Res->ItemEntries) {
+ writeObject(Item);
+ writeInt(IconCursorID++);
+ }
+ return Error::success();
+}
+
+Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
+}
+
+Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
+}
+
+Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
+ IconCursorGroupType Type;
+ StringRef FileStr;
+ IntOrString ResName = Base->ResName;
+
+ if (auto *IconRes = dyn_cast<IconResource>(Base)) {
+ FileStr = IconRes->IconLoc;
+ Type = IconCursorGroupType::Icon;
+ } else {
+ auto *CursorRes = dyn_cast<CursorResource>(Base);
+ FileStr = CursorRes->CursorLoc;
+ Type = IconCursorGroupType::Cursor;
+ }
+
+ bool IsLong;
+ stripQuotes(FileStr, IsLong);
+ auto File = loadFile(FileStr);
+
+ if (!File)
+ return File.takeError();
+
+ BinaryStreamReader Reader((*File)->getBuffer(), support::little);
+
+ // Read the file headers.
+ // - At the beginning, ICONDIR/NEWHEADER header.
+ // - Then, a number of RESDIR headers follow. These contain offsets
+ // to data.
+ const GroupIconDir *Header;
+
+ RETURN_IF_ERROR(Reader.readObject(Header));
+ if (Header->Reserved != 0)
+ return createError("Incorrect icon/cursor Reserved field; should be 0.");
+ uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
+ if (Header->ResType != NeededType)
+ return createError("Incorrect icon/cursor ResType field; should be " +
+ Twine(NeededType) + ".");
+
+ uint16_t NumItems = Header->ResCount;
+
+ // Read single ico/cur headers.
+ std::vector<ResourceDirEntryStart> ItemEntries;
+ ItemEntries.reserve(NumItems);
+ std::vector<uint32_t> ItemOffsets(NumItems);
+ for (size_t ID = 0; ID < NumItems; ++ID) {
+ const ResourceDirEntryStart *Object;
+ RETURN_IF_ERROR(Reader.readObject(Object));
+ ItemEntries.push_back(*Object);
+ RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
+ }
+
+ // Now write each icon/cursors one by one. At first, all the contents
+ // without ICO/CUR header. This is described by SingleIconCursorResource.
+ for (size_t ID = 0; ID < NumItems; ++ID) {
+ // Load the fragment of file.
+ Reader.setOffset(ItemOffsets[ID]);
+ ArrayRef<uint8_t> Image;
+ RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
+ SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
+ Base->MemoryFlags);
+ SingleRes.setName(IconCursorID + ID);
+ RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
+ }
+
+ // Now, write all the headers concatenated into a separate resource.
+ for (size_t ID = 0; ID < NumItems; ++ID) {
+ // We need to rewrite the cursor headers, and fetch actual values
+ // for Planes/BitCount.
+ const auto &OldHeader = ItemEntries[ID];
+ ResourceDirEntryStart NewHeader = OldHeader;
+
+ if (Type == IconCursorGroupType::Cursor) {
+ NewHeader.Cursor.Width = OldHeader.Icon.Width;
+ // Each cursor in fact stores two bitmaps, one under another.
+ // Height provided in cursor definition describes the height of the
+ // cursor, whereas the value existing in resource definition describes
+ // the height of the bitmap. Therefore, we need to double this height.
+ NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
+
+ // Two WORDs were written at the beginning of the resource (hotspot
+ // location). This is reflected in Size field.
+ NewHeader.Size += 2 * sizeof(uint16_t);
+ }
+
+ // Now, we actually need to read the bitmap header to find
+ // the number of planes and the number of bits per pixel.
+ Reader.setOffset(ItemOffsets[ID]);
+ const BitmapInfoHeader *BMPHeader;
+ RETURN_IF_ERROR(Reader.readObject(BMPHeader));
+ if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
+ NewHeader.Planes = BMPHeader->Planes;
+ NewHeader.BitCount = BMPHeader->BitCount;
+ } else {
+ // A PNG .ico file.
+ // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
+ // "The image must be in 32bpp"
+ NewHeader.Planes = 1;
+ NewHeader.BitCount = 32;
+ }
+
+ ItemEntries[ID] = NewHeader;
+ }
+
+ IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
+ HeaderRes.setName(ResName);
+ if (Base->MemoryFlags & MfPreload) {
+ HeaderRes.MemoryFlags |= MfPreload;
+ HeaderRes.MemoryFlags &= ~MfPure;
+ }
+ RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
+
+ return Error::success();
+}
+
+// --- DialogResource helpers. --- //
+
+Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
+ bool IsExtended) {
+ // Each control should be aligned to DWORD.
+ padStream(sizeof(uint32_t));
+
+ auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
+ IntWithNotMask CtlStyle(TypeInfo.Style);
+ CtlStyle |= Ctl.Style.getValueOr(RCInt(0));
+ uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
+
+ // DIALOG(EX) item header prefix.
+ if (!IsExtended) {
+ struct {
+ ulittle32_t Style;
+ ulittle32_t ExtStyle;
+ } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
+ writeObject(Prefix);
+ } else {
+ struct {
+ ulittle32_t HelpID;
+ ulittle32_t ExtStyle;
+ ulittle32_t Style;
+ } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
+ ulittle32_t(CtlStyle.getValue())};
+ writeObject(Prefix);
+ }
+
+ // Common fixed-length part.
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.X, "Dialog control x-coordinate", true));
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.Y, "Dialog control y-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.Height, "Dialog control height", false));
+ struct {
+ ulittle16_t X;
+ ulittle16_t Y;
+ ulittle16_t Width;
+ ulittle16_t Height;
+ } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
+ ulittle16_t(Ctl.Height)};
+ writeObject(Middle);
+
+ // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
+ if (!IsExtended) {
+ // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
+ // want to refer to later.
+ if (Ctl.ID != static_cast<uint32_t>(-1))
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ Ctl.ID, "Control ID in simple DIALOG resource"));
+ writeInt<uint16_t>(Ctl.ID);
+ } else {
+ writeInt<uint32_t>(Ctl.ID);
+ }
+
+ // Window class - either 0xFFFF + 16-bit integer or a string.
+ RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
+
+ // Element caption/reference ID. ID is preceded by 0xFFFF.
+ RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
+ RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
+
+ // # bytes of extra creation data count. Don't pass any.
+ writeInt<uint16_t>(0);
+
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
+ auto *Res = cast<DialogResource>(Base);
+
+ // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
+ const uint32_t DefaultStyle = 0x80880000;
+ const uint32_t StyleFontFlag = 0x40;
+ const uint32_t StyleCaptionFlag = 0x00C00000;
+
+ uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
+ if (ObjectData.Font)
+ UsedStyle |= StyleFontFlag;
+ else
+ UsedStyle &= ~StyleFontFlag;
+
+ // Actually, in case of empty (but existent) caption, the examined field
+ // is equal to "\"\"". That's why empty captions are still noticed.
+ if (ObjectData.Caption != "")
+ UsedStyle |= StyleCaptionFlag;
+
+ const uint16_t DialogExMagic = 0xFFFF;
+ uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0);
+
+ // Write DIALOG(EX) header prefix. These are pretty different.
+ if (!Res->IsExtended) {
+ // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
+ // In such a case, whole object (in .res file) is equivalent to a
+ // DIALOGEX. It might lead to access violation/segmentation fault in
+ // resource readers. For example,
+ // 1 DIALOG 0, 0, 0, 65432
+ // STYLE 0xFFFF0001 {}
+ // would be compiled to a DIALOGEX with 65432 controls.
+ if ((UsedStyle >> 16) == DialogExMagic)
+ return createError("16 higher bits of DIALOG resource style cannot be"
+ " equal to 0xFFFF");
+
+ struct {
+ ulittle32_t Style;
+ ulittle32_t ExtStyle;
+ } Prefix{ulittle32_t(UsedStyle),
+ ulittle32_t(ExStyle)};
+
+ writeObject(Prefix);
+ } else {
+ struct {
+ ulittle16_t Version;
+ ulittle16_t Magic;
+ ulittle32_t HelpID;
+ ulittle32_t ExtStyle;
+ ulittle32_t Style;
+ } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
+ ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
+
+ writeObject(Prefix);
+ }
+
+ // Now, a common part. First, fixed-length fields.
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
+ "Number of dialog controls"));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
+ struct {
+ ulittle16_t Count;
+ ulittle16_t PosX;
+ ulittle16_t PosY;
+ ulittle16_t DialogWidth;
+ ulittle16_t DialogHeight;
+ } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
+ ulittle16_t(Res->Y), ulittle16_t(Res->Width),
+ ulittle16_t(Res->Height)};
+ writeObject(Middle);
+
+ // MENU field. As of now, we don't keep them in the state and can peacefully
+ // think there is no menu attached to the dialog.
+ writeInt<uint16_t>(0);
+
+ // Window CLASS field.
+ RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
+
+ // Window title or a single word equal to 0.
+ RETURN_IF_ERROR(writeCString(ObjectData.Caption));
+
+ // If there *is* a window font declared, output its data.
+ auto &Font = ObjectData.Font;
+ if (Font) {
+ writeInt<uint16_t>(Font->Size);
+ // Additional description occurs only in DIALOGEX.
+ if (Res->IsExtended) {
+ writeInt<uint16_t>(Font->Weight);
+ writeInt<uint8_t>(Font->IsItalic);
+ writeInt<uint8_t>(Font->Charset);
+ }
+ RETURN_IF_ERROR(writeCString(Font->Typeface));
+ }
+
+ auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
+ if (!Err)
+ return Error::success();
+ return joinErrors(createError("Error in " + Twine(Ctl.Type) +
+ " control (ID " + Twine(Ctl.ID) + "):"),
+ std::move(Err));
+ };
+
+ for (auto &Ctl : Res->Controls)
+ RETURN_IF_ERROR(
+ handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
+
+ return Error::success();
+}
+
+// --- HTMLResource helpers. --- //
+
+Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
+ return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
+}
+
+// --- MenuResource helpers. --- //
+
+Error ResourceFileWriter::writeMenuDefinition(
+ const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
+ assert(Def);
+ const MenuDefinition *DefPtr = Def.get();
+
+ if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
+ writeInt<uint16_t>(Flags);
+ // Some resource files use -1, i.e. UINT32_MAX, for empty menu items.
+ if (MenuItemPtr->Id != static_cast<uint32_t>(-1))
+ RETURN_IF_ERROR(
+ checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
+ writeInt<uint16_t>(MenuItemPtr->Id);
+ RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
+ return Error::success();
+ }
+
+ if (isa<MenuSeparator>(DefPtr)) {
+ writeInt<uint16_t>(Flags);
+ writeInt<uint32_t>(0);
+ return Error::success();
+ }
+
+ auto *PopupPtr = cast<PopupItem>(DefPtr);
+ writeInt<uint16_t>(Flags);
+ RETURN_IF_ERROR(writeCString(PopupPtr->Name));
+ return writeMenuDefinitionList(PopupPtr->SubItems);
+}
+
+Error ResourceFileWriter::writeMenuDefinitionList(
+ const MenuDefinitionList &List) {
+ for (auto &Def : List.Definitions) {
+ uint16_t Flags = Def->getResFlags();
+ // Last element receives an additional 0x80 flag.
+ const uint16_t LastElementFlag = 0x0080;
+ if (&Def == &List.Definitions.back())
+ Flags |= LastElementFlag;
+
+ RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
+ }
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
+ // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
+ writeInt<uint32_t>(0);
+
+ return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
+}
+
+// --- StringTableResource helpers. --- //
+
+class BundleResource : public RCResource {
+public:
+ using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
+ BundleType Bundle;
+
+ BundleResource(const BundleType &StrBundle)
+ : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
+ IntOrString getResourceType() const override { return 6; }
+
+ ResourceKind getKind() const override { return RkStringTableBundle; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkStringTableBundle;
+ }
+ Twine getResourceTypeName() const override { return "STRINGTABLE"; }
+};
+
+Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
+}
+
+Error ResourceFileWriter::insertStringIntoBundle(
+ StringTableInfo::Bundle &Bundle, uint16_t StringID,
+ const std::vector<StringRef> &String) {
+ uint16_t StringLoc = StringID & 15;
+ if (Bundle.Data[StringLoc])
+ return createError("Multiple STRINGTABLE strings located under ID " +
+ Twine(StringID));
+ Bundle.Data[StringLoc] = String;
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
+ auto *Res = cast<BundleResource>(Base);
+ for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
+ // The string format is a tiny bit different here. We
+ // first output the size of the string, and then the string itself
+ // (which is not null-terminated).
+ SmallVector<UTF16, 128> Data;
+ if (Res->Bundle.Data[ID]) {
+ bool IsLongString;
+ for (StringRef S : *Res->Bundle.Data[ID])
+ RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull,
+ IsLongString, Data, Params.CodePage));
+ if (AppendNull)
+ Data.push_back('\0');
+ }
+ RETURN_IF_ERROR(
+ checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
+ writeInt<uint16_t>(Data.size());
+ for (auto Char : Data)
+ writeInt(Char);
+ }
+ return Error::success();
+}
+
+Error ResourceFileWriter::dumpAllStringTables() {
+ for (auto Key : StringTableData.BundleList) {
+ auto Iter = StringTableData.BundleData.find(Key);
+ assert(Iter != StringTableData.BundleData.end());
+
+ // For a moment, revert the context info to moment of bundle declaration.
+ ContextKeeper RAII(this);
+ ObjectData = Iter->second.DeclTimeInfo;
+
+ BundleResource Res(Iter->second);
+ // Bundle #(k+1) contains keys [16k, 16k + 15].
+ Res.setName(Key.first + 1);
+ RETURN_IF_ERROR(visitStringTableBundle(&Res));
+ }
+ return Error::success();
+}
+
+// --- UserDefinedResource helpers. --- //
+
+Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
+ auto *Res = cast<UserDefinedResource>(Base);
+
+ if (Res->IsFileResource)
+ return appendFile(Res->FileLoc);
+
+ for (auto &Elem : Res->Contents) {
+ if (Elem.isInt()) {
+ RETURN_IF_ERROR(
+ checkRCInt(Elem.getInt(), "Number in user-defined resource"));
+ writeRCInt(Elem.getInt());
+ continue;
+ }
+
+ SmallVector<UTF16, 128> ProcessedString;
+ bool IsLongString;
+ RETURN_IF_ERROR(
+ processString(Elem.getString(), NullHandlingMethod::UserResource,
+ IsLongString, ProcessedString, Params.CodePage));
+
+ for (auto Ch : ProcessedString) {
+ if (IsLongString) {
+ writeInt(Ch);
+ continue;
+ }
+
+ RETURN_IF_ERROR(checkNumberFits<uint8_t>(
+ Ch, "Character in narrow string in user-defined resource"));
+ writeInt<uint8_t>(Ch);
+ }
+ }
+
+ return Error::success();
+}
+
+// --- VersionInfoResourceResource helpers. --- //
+
+Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
+ // Output the header if the block has name.
+ bool OutputHeader = Blk.Name != "";
+ uint64_t LengthLoc;
+
+ padStream(sizeof(uint32_t));
+ if (OutputHeader) {
+ LengthLoc = writeInt<uint16_t>(0);
+ writeInt<uint16_t>(0);
+ writeInt<uint16_t>(1); // true
+ RETURN_IF_ERROR(writeCString(Blk.Name));
+ padStream(sizeof(uint32_t));
+ }
+
+ for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
+ VersionInfoStmt *ItemPtr = Item.get();
+
+ if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
+ RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
+ continue;
+ }
+
+ auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
+ RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
+ }
+
+ if (OutputHeader) {
+ uint64_t CurLoc = tell();
+ writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
+ }
+
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
+ // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
+ // is a mapping from the key (string) to the value (a sequence of ints or
+ // a sequence of strings).
+ //
+ // If integers are to be written: width of each integer written depends on
+ // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
+ // ValueLength defined in structure referenced below is then the total
+ // number of bytes taken by these integers.
+ //
+ // If strings are to be written: characters are always WORDs.
+ // Moreover, '\0' character is written after the last string, and between
+ // every two strings separated by comma (if strings are not comma-separated,
+ // they're simply concatenated). ValueLength is equal to the number of WORDs
+ // written (that is, half of the bytes written).
+ //
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
+ bool HasStrings = false, HasInts = false;
+ for (auto &Item : Val.Values)
+ (Item.isInt() ? HasInts : HasStrings) = true;
+
+ assert((HasStrings || HasInts) && "VALUE must have at least one argument");
+ if (HasStrings && HasInts)
+ return createError(Twine("VALUE ") + Val.Key +
+ " cannot contain both strings and integers");
+
+ padStream(sizeof(uint32_t));
+ auto LengthLoc = writeInt<uint16_t>(0);
+ auto ValLengthLoc = writeInt<uint16_t>(0);
+ writeInt<uint16_t>(HasStrings);
+ RETURN_IF_ERROR(writeCString(Val.Key));
+ padStream(sizeof(uint32_t));
+
+ auto DataLoc = tell();
+ for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
+ auto &Item = Val.Values[Id];
+ if (Item.isInt()) {
+ auto Value = Item.getInt();
+ RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
+ writeRCInt(Value);
+ continue;
+ }
+
+ bool WriteTerminator =
+ Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
+ RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
+ }
+
+ auto CurLoc = tell();
+ auto ValueLength = CurLoc - DataLoc;
+ if (HasStrings) {
+ assert(ValueLength % 2 == 0);
+ ValueLength /= 2;
+ }
+ writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
+ writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
+ return Error::success();
+}
+
+template <typename Ty>
+static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
+ const Ty &Default) {
+ auto Iter = Map.find(Key);
+ if (Iter != Map.end())
+ return Iter->getValue();
+ return Default;
+}
+
+Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
+ auto *Res = cast<VersionInfoResource>(Base);
+
+ const auto &FixedData = Res->FixedData;
+
+ struct /* VS_FIXEDFILEINFO */ {
+ ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
+ ulittle32_t StructVersion = ulittle32_t(0x10000);
+ // It's weird to have most-significant DWORD first on the little-endian
+ // machines, but let it be this way.
+ ulittle32_t FileVersionMS;
+ ulittle32_t FileVersionLS;
+ ulittle32_t ProductVersionMS;
+ ulittle32_t ProductVersionLS;
+ ulittle32_t FileFlagsMask;
+ ulittle32_t FileFlags;
+ ulittle32_t FileOS;
+ ulittle32_t FileType;
+ ulittle32_t FileSubtype;
+ // MS implementation seems to always set these fields to 0.
+ ulittle32_t FileDateMS = ulittle32_t(0);
+ ulittle32_t FileDateLS = ulittle32_t(0);
+ } FixedInfo;
+
+ // First, VS_VERSIONINFO.
+ auto LengthLoc = writeInt<uint16_t>(0);
+ writeInt<uint16_t>(sizeof(FixedInfo));
+ writeInt<uint16_t>(0);
+ cantFail(writeCString("VS_VERSION_INFO"));
+ padStream(sizeof(uint32_t));
+
+ using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
+ auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
+ static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
+ if (!FixedData.IsTypePresent[(int)Type])
+ return DefaultOut;
+ return FixedData.FixedInfo[(int)Type];
+ };
+
+ auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
+ FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
+ FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
+
+ auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ *std::max_element(ProdVer.begin(), ProdVer.end()),
+ "PRODUCTVERSION fields"));
+ FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
+ FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
+
+ FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
+ FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
+ FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
+ FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
+ FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
+
+ writeObject(FixedInfo);
+ padStream(sizeof(uint32_t));
+
+ RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
+
+ // FIXME: check overflow?
+ writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
+
+ return Error::success();
+}
+
+Expected<std::unique_ptr<MemoryBuffer>>
+ResourceFileWriter::loadFile(StringRef File) const {
+ SmallString<128> Path;
+ SmallString<128> Cwd;
+ std::unique_ptr<MemoryBuffer> Result;
+
+ // 0. The file path is absolute or has a root directory, so we shouldn't
+ // try to append it on top of other base directories. (An absolute path
+ // must have a root directory, but e.g. the path "\dir\file" on windows
+ // isn't considered absolute, but it does have a root directory. As long as
+ // sys::path::append doesn't handle appending an absolute path or a path
+ // starting with a root directory on top of a base, we must handle this
+ // case separately at the top. C++17's path::append handles that case
+ // properly though, so if using that to append paths below, this early
+ // exception case could be removed.)
+ if (sys::path::has_root_directory(File))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ File, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+
+ // 1. The current working directory.
+ sys::fs::current_path(Cwd);
+ Path.assign(Cwd.begin(), Cwd.end());
+ sys::path::append(Path, File);
+ if (sys::fs::exists(Path))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+
+ // 2. The directory of the input resource file, if it is different from the
+ // current working directory.
+ StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
+ Path.assign(InputFileDir.begin(), InputFileDir.end());
+ sys::path::append(Path, File);
+ if (sys::fs::exists(Path))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+
+ // 3. All of the include directories specified on the command line.
+ for (StringRef ForceInclude : Params.Include) {
+ Path.assign(ForceInclude.begin(), ForceInclude.end());
+ sys::path::append(Path, File);
+ if (sys::fs::exists(Path))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ }
+
+ if (!Params.NoInclude) {
+ if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File))
+ return errorOrToExpected(MemoryBuffer::getFile(
+ *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ }
+
+ return make_error<StringError>("error : file not found : " + Twine(File),
+ inconvertibleErrorCode());
+}
+
+} // namespace rc
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h
new file mode 100644
index 0000000000..0f3d593725
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceFileWriter.h
@@ -0,0 +1,217 @@
+//===-- ResourceSerializator.h ----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This defines a visitor serializing resources to a .res stream.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
+
+#include "ResourceScriptStmt.h"
+#include "ResourceVisitor.h"
+
+#include "llvm/Support/Endian.h"
+
+namespace llvm {
+
+class MemoryBuffer;
+
+namespace rc {
+
+enum CodePage {
+ CpAcp = 0, // The current used codepage. Since there's no such
+ // notion in LLVM what codepage it actually means,
+ // this only allows ASCII.
+ CpWin1252 = 1252, // A codepage where most 8 bit values correspond to
+ // unicode code points with the same value.
+ CpUtf8 = 65001, // UTF-8.
+};
+
+struct WriterParams {
+ std::vector<std::string> Include; // Additional folders to search for files.
+ bool NoInclude; // Ignore the INCLUDE variable.
+ StringRef InputFilePath; // The full path of the input file.
+ int CodePage = CpAcp; // The codepage for interpreting characters.
+};
+
+class ResourceFileWriter : public Visitor {
+public:
+ ResourceFileWriter(const WriterParams &Params,
+ std::unique_ptr<raw_fd_ostream> Stream)
+ : Params(Params), FS(std::move(Stream)), IconCursorID(1) {
+ assert(FS && "Output stream needs to be provided to the serializator");
+ }
+
+ Error visitNullResource(const RCResource *) override;
+ Error visitAcceleratorsResource(const RCResource *) override;
+ Error visitCursorResource(const RCResource *) override;
+ Error visitDialogResource(const RCResource *) override;
+ Error visitHTMLResource(const RCResource *) override;
+ Error visitIconResource(const RCResource *) override;
+ Error visitMenuResource(const RCResource *) override;
+ Error visitVersionInfoResource(const RCResource *) override;
+ Error visitStringTableResource(const RCResource *) override;
+ Error visitUserDefinedResource(const RCResource *) override;
+
+ Error visitCaptionStmt(const CaptionStmt *) override;
+ Error visitCharacteristicsStmt(const CharacteristicsStmt *) override;
+ Error visitClassStmt(const ClassStmt *) override;
+ Error visitExStyleStmt(const ExStyleStmt *) override;
+ Error visitFontStmt(const FontStmt *) override;
+ Error visitLanguageStmt(const LanguageResource *) override;
+ Error visitStyleStmt(const StyleStmt *) override;
+ Error visitVersionStmt(const VersionStmt *) override;
+
+ // Stringtables are output at the end of .res file. We need a separate
+ // function to do it.
+ Error dumpAllStringTables();
+
+ bool AppendNull = false; // Append '\0' to each existing STRINGTABLE element?
+
+ struct ObjectInfo {
+ uint16_t LanguageInfo;
+ uint32_t Characteristics;
+ uint32_t VersionInfo;
+
+ Optional<uint32_t> Style;
+ Optional<uint32_t> ExStyle;
+ StringRef Caption;
+ struct FontInfo {
+ uint32_t Size;
+ StringRef Typeface;
+ uint32_t Weight;
+ bool IsItalic;
+ uint32_t Charset;
+ };
+ Optional<FontInfo> Font;
+ IntOrString Class;
+
+ ObjectInfo()
+ : LanguageInfo(0), Characteristics(0), VersionInfo(0),
+ Class(StringRef()) {}
+ } ObjectData;
+
+ struct StringTableInfo {
+ // Each STRINGTABLE bundle depends on ID of the bundle and language
+ // description.
+ using BundleKey = std::pair<uint16_t, uint16_t>;
+ // Each bundle is in fact an array of 16 strings.
+ struct Bundle {
+ std::array<Optional<std::vector<StringRef>>, 16> Data;
+ ObjectInfo DeclTimeInfo;
+ uint16_t MemoryFlags;
+ Bundle(const ObjectInfo &Info, uint16_t Flags)
+ : DeclTimeInfo(Info), MemoryFlags(Flags) {}
+ };
+ std::map<BundleKey, Bundle> BundleData;
+ // Bundles are listed in the order of their first occurrence.
+ std::vector<BundleKey> BundleList;
+ } StringTableData;
+
+private:
+ Error handleError(Error Err, const RCResource *Res);
+
+ Error
+ writeResource(const RCResource *Res,
+ Error (ResourceFileWriter::*BodyWriter)(const RCResource *));
+
+ // NullResource
+ Error writeNullBody(const RCResource *);
+
+ // AcceleratorsResource
+ Error writeSingleAccelerator(const AcceleratorsResource::Accelerator &,
+ bool IsLastItem);
+ Error writeAcceleratorsBody(const RCResource *);
+
+ // BitmapResource
+ Error visitBitmapResource(const RCResource *) override;
+ Error writeBitmapBody(const RCResource *);
+
+ // CursorResource and IconResource
+ Error visitIconOrCursorResource(const RCResource *);
+ Error visitIconOrCursorGroup(const RCResource *);
+ Error visitSingleIconOrCursor(const RCResource *);
+ Error writeSingleIconOrCursorBody(const RCResource *);
+ Error writeIconOrCursorGroupBody(const RCResource *);
+
+ // DialogResource
+ Error writeSingleDialogControl(const Control &, bool IsExtended);
+ Error writeDialogBody(const RCResource *);
+
+ // HTMLResource
+ Error writeHTMLBody(const RCResource *);
+
+ // MenuResource
+ Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &,
+ uint16_t Flags);
+ Error writeMenuDefinitionList(const MenuDefinitionList &List);
+ Error writeMenuBody(const RCResource *);
+
+ // StringTableResource
+ Error visitStringTableBundle(const RCResource *);
+ Error writeStringTableBundleBody(const RCResource *);
+ Error insertStringIntoBundle(StringTableInfo::Bundle &Bundle,
+ uint16_t StringID,
+ const std::vector<StringRef> &String);
+
+ // User defined resource
+ Error writeUserDefinedBody(const RCResource *);
+
+ // VersionInfoResource
+ Error writeVersionInfoBody(const RCResource *);
+ Error writeVersionInfoBlock(const VersionInfoBlock &);
+ Error writeVersionInfoValue(const VersionInfoValue &);
+
+ const WriterParams &Params;
+
+ // Output stream handling.
+ std::unique_ptr<raw_fd_ostream> FS;
+
+ uint64_t tell() const { return FS->tell(); }
+
+ uint64_t writeObject(const ArrayRef<uint8_t> Data);
+
+ template <typename T> uint64_t writeInt(const T &Value) {
+ support::detail::packed_endian_specific_integral<T, support::little,
+ support::unaligned>
+ Object(Value);
+ return writeObject(Object);
+ }
+
+ template <typename T> uint64_t writeObject(const T &Value) {
+ return writeObject(ArrayRef<uint8_t>(
+ reinterpret_cast<const uint8_t *>(&Value), sizeof(T)));
+ }
+
+ template <typename T> void writeObjectAt(const T &Value, uint64_t Position) {
+ FS->pwrite((const char *)&Value, sizeof(T), Position);
+ }
+
+ Error writeCString(StringRef Str, bool WriteTerminator = true);
+
+ Error writeIdentifier(const IntOrString &Ident);
+ Error writeIntOrString(const IntOrString &Data);
+
+ void writeRCInt(RCInt);
+
+ Error appendFile(StringRef Filename);
+
+ void padStream(uint64_t Length);
+
+ Expected<std::unique_ptr<MemoryBuffer>> loadFile(StringRef File) const;
+
+ // Icon and cursor IDs are allocated starting from 1 and increasing for
+ // each icon/cursor dumped. This maintains the current ID to be allocated.
+ uint16_t IconCursorID;
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp
new file mode 100644
index 0000000000..6657aa54cf
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.cpp
@@ -0,0 +1,111 @@
+//===-- ResourceScriptCppFilter.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This file implements an interface defined in ResourceScriptCppFilter.h.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptCppFilter.h"
+#include "llvm/ADT/StringExtras.h"
+
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+class Filter {
+public:
+ explicit Filter(StringRef Input) : Data(Input), DataLength(Input.size()) {}
+
+ std::string run();
+
+private:
+ // Parse the line, returning whether the line should be included in
+ // the output.
+ bool parseLine(StringRef Line);
+
+ bool streamEof() const;
+
+ StringRef Data;
+ size_t DataLength;
+
+ size_t Pos = 0;
+ bool Outputting = true;
+};
+
+std::string Filter::run() {
+ std::vector<StringRef> Output;
+
+ while (!streamEof() && Pos != StringRef::npos) {
+ size_t LineStart = Pos;
+ Pos = Data.find_first_of("\r\n", Pos);
+ Pos = Data.find_first_not_of("\r\n", Pos);
+ StringRef Line = Data.take_front(Pos).drop_front(LineStart);
+
+ if (parseLine(Line))
+ Output.push_back(Line);
+ }
+
+ return llvm::join(Output, "");
+}
+
+bool Filter::parseLine(StringRef Line) {
+ Line = Line.ltrim();
+
+ if (!Line.consume_front("#")) {
+ // A normal content line, filtered according to the current mode.
+ return Outputting;
+ }
+
+ // Found a preprocessing directive line. From here on, we always return
+ // false since the preprocessing directives should be filtered out.
+
+ Line.consume_front("line");
+ if (!Line.startswith(" "))
+ return false; // Not a line directive (pragma etc).
+
+ // #line 123 "path/file.h"
+ // # 123 "path/file.h" 1
+
+ Line =
+ Line.ltrim(); // There could be multiple spaces after the #line directive
+
+ size_t N;
+ if (Line.consumeInteger(10, N)) // Returns true to signify an error
+ return false;
+
+ Line = Line.ltrim();
+
+ if (!Line.consume_front("\""))
+ return false; // Malformed line, no quote found.
+
+ // Split the string at the last quote (in case the path name had
+ // escaped quotes as well).
+ Line = Line.rsplit('"').first;
+
+ StringRef Ext = Line.rsplit('.').second;
+
+ if (Ext.equals_insensitive("h") || Ext.equals_insensitive("c")) {
+ Outputting = false;
+ } else {
+ Outputting = true;
+ }
+
+ return false;
+}
+
+bool Filter::streamEof() const { return Pos == DataLength; }
+
+} // anonymous namespace
+
+namespace llvm {
+
+std::string filterCppOutput(StringRef Input) { return Filter(Input).run(); }
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h
new file mode 100644
index 0000000000..780db317c9
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptCppFilter.h
@@ -0,0 +1,34 @@
+//===-- ResourceScriptCppFilter.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This filters the input to llvm-rc for preprocessor markers, removing
+// preprocessing directives that a preprocessor can output or leave behind.
+//
+// It also filters out any contribution from files named *.h or *.c, based
+// on preprocessor line markers. When preprocessing RC files, the included
+// headers can leave behind C declarations, that RC doesn't understand.
+// Rc.exe simply discards anything that comes from files named *.h or *.h.
+//
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa381033(v=vs.85).aspx
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H
+
+#include "llvm/ADT/StringRef.h"
+
+#include <string>
+
+namespace llvm {
+
+std::string filterCppOutput(StringRef Input);
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp
new file mode 100644
index 0000000000..7cb4d02e3c
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.cpp
@@ -0,0 +1,856 @@
+//===-- ResourceScriptParser.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements the parser defined in ResourceScriptParser.h.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptParser.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+// Take an expression returning llvm::Error and forward the error if it exists.
+#define RETURN_IF_ERROR(Expr) \
+ if (auto Err = (Expr)) \
+ return std::move(Err);
+
+// Take an expression returning llvm::Expected<T> and assign it to Var or
+// forward the error out of the function.
+#define ASSIGN_OR_RETURN(Var, Expr) \
+ auto Var = (Expr); \
+ if (!Var) \
+ return Var.takeError();
+
+namespace llvm {
+namespace rc {
+
+RCParser::ParserError::ParserError(const Twine &Expected, const LocIter CurLoc,
+ const LocIter End)
+ : ErrorLoc(CurLoc), FileEnd(End) {
+ CurMessage = "Error parsing file: expected " + Expected.str() + ", got " +
+ (CurLoc == End ? "<EOF>" : CurLoc->value()).str();
+}
+
+char RCParser::ParserError::ID = 0;
+
+RCParser::RCParser(std::vector<RCToken> TokenList)
+ : Tokens(std::move(TokenList)), CurLoc(Tokens.begin()), End(Tokens.end()) {}
+
+bool RCParser::isEof() const { return CurLoc == End; }
+
+RCParser::ParseType RCParser::parseSingleResource() {
+ // The first thing we read is usually a resource's name. However, in some
+ // cases (LANGUAGE and STRINGTABLE) the resources don't have their names
+ // and the first token to be read is the type.
+ ASSIGN_OR_RETURN(NameToken, readTypeOrName());
+
+ if (NameToken->equalsLower("LANGUAGE"))
+ return parseLanguageResource();
+ else if (NameToken->equalsLower("STRINGTABLE"))
+ return parseStringTableResource();
+
+ // If it's not an unnamed resource, what we've just read is a name. Now,
+ // read resource type;
+ ASSIGN_OR_RETURN(TypeToken, readTypeOrName());
+
+ ParseType Result = std::unique_ptr<RCResource>();
+ (void)!Result;
+
+ if (TypeToken->equalsLower("ACCELERATORS"))
+ Result = parseAcceleratorsResource();
+ else if (TypeToken->equalsLower("BITMAP"))
+ Result = parseBitmapResource();
+ else if (TypeToken->equalsLower("CURSOR"))
+ Result = parseCursorResource();
+ else if (TypeToken->equalsLower("DIALOG"))
+ Result = parseDialogResource(false);
+ else if (TypeToken->equalsLower("DIALOGEX"))
+ Result = parseDialogResource(true);
+ else if (TypeToken->equalsLower("HTML"))
+ Result = parseHTMLResource();
+ else if (TypeToken->equalsLower("ICON"))
+ Result = parseIconResource();
+ else if (TypeToken->equalsLower("MENU"))
+ Result = parseMenuResource();
+ else if (TypeToken->equalsLower("RCDATA"))
+ Result = parseUserDefinedResource(RkRcData);
+ else if (TypeToken->equalsLower("VERSIONINFO"))
+ Result = parseVersionInfoResource();
+ else
+ Result = parseUserDefinedResource(*TypeToken);
+
+ if (Result)
+ (*Result)->setName(*NameToken);
+
+ return Result;
+}
+
+bool RCParser::isNextTokenKind(Kind TokenKind) const {
+ return !isEof() && look().kind() == TokenKind;
+}
+
+const RCToken &RCParser::look() const {
+ assert(!isEof());
+ return *CurLoc;
+}
+
+const RCToken &RCParser::read() {
+ assert(!isEof());
+ return *CurLoc++;
+}
+
+void RCParser::consume() {
+ assert(!isEof());
+ CurLoc++;
+}
+
+// An integer description might consist of a single integer or
+// an arithmetic expression evaluating to the integer. The expressions
+// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning
+// is the same as in C++ except for 'not' expression.
+// The operators in the original RC implementation have the following
+// precedence:
+// 1) Unary operators (- ~ not),
+// 2) Binary operators (+ - & |), with no precedence.
+//
+// 'not' expression is mostly useful for style values. It evaluates to 0,
+// but value given to the operator is stored separately from integer value.
+// It's mostly useful for control style expressions and causes bits from
+// default control style to be excluded from generated style. For binary
+// operators the mask from the right operand is applied to the left operand
+// and masks from both operands are combined in operator result.
+//
+// The following grammar is used to parse the expressions Exp1:
+// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2
+// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
+// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions,
+// separated by binary operators.)
+//
+// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2
+// is read by parseIntExpr2().
+//
+// The original Microsoft tool handles multiple unary operators incorrectly.
+// For example, in 16-bit little-endian integers:
+// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00;
+// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff.
+// Our implementation differs from the original one and handles these
+// operators correctly:
+// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
+// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
+
+Expected<RCInt> RCParser::readInt() {
+ ASSIGN_OR_RETURN(Value, parseIntExpr1());
+ return (*Value).getValue();
+}
+
+Expected<IntWithNotMask> RCParser::parseIntExpr1() {
+ // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
+ ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
+ IntWithNotMask Result = *FirstResult;
+
+ while (!isEof() && look().isBinaryOp()) {
+ auto OpToken = read();
+ ASSIGN_OR_RETURN(NextResult, parseIntExpr2());
+
+ switch (OpToken.kind()) {
+ case Kind::Plus:
+ Result += *NextResult;
+ break;
+
+ case Kind::Minus:
+ Result -= *NextResult;
+ break;
+
+ case Kind::Pipe:
+ Result |= *NextResult;
+ break;
+
+ case Kind::Amp:
+ Result &= *NextResult;
+ break;
+
+ default:
+ llvm_unreachable("Already processed all binary ops.");
+ }
+ }
+
+ return Result;
+}
+
+Expected<IntWithNotMask> RCParser::parseIntExpr2() {
+ // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1).
+ static const char ErrorMsg[] = "'-', '~', integer or '('";
+
+ if (isEof())
+ return getExpectedError(ErrorMsg);
+
+ switch (look().kind()) {
+ case Kind::Minus: {
+ consume();
+ ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ return -(*Result);
+ }
+
+ case Kind::Tilde: {
+ consume();
+ ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ return ~(*Result);
+ }
+
+ case Kind::Int:
+ return RCInt(read());
+
+ case Kind::LeftParen: {
+ consume();
+ ASSIGN_OR_RETURN(Result, parseIntExpr1());
+ RETURN_IF_ERROR(consumeType(Kind::RightParen));
+ return *Result;
+ }
+
+ case Kind::Identifier: {
+ if (!read().value().equals_insensitive("not"))
+ return getExpectedError(ErrorMsg, true);
+ ASSIGN_OR_RETURN(Result, parseIntExpr2());
+ return IntWithNotMask(0, (*Result).getValue());
+ }
+
+ default:
+ return getExpectedError(ErrorMsg);
+ }
+}
+
+Expected<StringRef> RCParser::readString() {
+ if (!isNextTokenKind(Kind::String))
+ return getExpectedError("string");
+ return read().value();
+}
+
+Expected<StringRef> RCParser::readFilename() {
+ if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
+ return getExpectedError("string");
+ return read().value();
+}
+
+Expected<StringRef> RCParser::readIdentifier() {
+ if (!isNextTokenKind(Kind::Identifier))
+ return getExpectedError("identifier");
+ return read().value();
+}
+
+Expected<IntOrString> RCParser::readIntOrString() {
+ if (!isNextTokenKind(Kind::Int) && !isNextTokenKind(Kind::String))
+ return getExpectedError("int or string");
+ return IntOrString(read());
+}
+
+Expected<IntOrString> RCParser::readTypeOrName() {
+ // We suggest that the correct resource name or type should be either an
+ // identifier or an integer. The original RC tool is much more liberal.
+ if (!isNextTokenKind(Kind::Identifier) && !isNextTokenKind(Kind::Int))
+ return getExpectedError("int or identifier");
+ return IntOrString(read());
+}
+
+Error RCParser::consumeType(Kind TokenKind) {
+ if (isNextTokenKind(TokenKind)) {
+ consume();
+ return Error::success();
+ }
+
+ switch (TokenKind) {
+#define TOKEN(TokenName) \
+ case Kind::TokenName: \
+ return getExpectedError(#TokenName);
+#define SHORT_TOKEN(TokenName, TokenCh) \
+ case Kind::TokenName: \
+ return getExpectedError(#TokenCh);
+#include "ResourceScriptTokenList.def"
+ }
+
+ llvm_unreachable("All case options exhausted.");
+}
+
+bool RCParser::consumeOptionalType(Kind TokenKind) {
+ if (isNextTokenKind(TokenKind)) {
+ consume();
+ return true;
+ }
+
+ return false;
+}
+
+Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
+ size_t MaxCount) {
+ assert(MinCount <= MaxCount);
+
+ SmallVector<RCInt, 8> Result;
+
+ auto FailureHandler =
+ [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
+ if (Result.size() < MinCount)
+ return std::move(Err);
+ consumeError(std::move(Err));
+ return Result;
+ };
+
+ for (size_t i = 0; i < MaxCount; ++i) {
+ // Try to read a comma unless we read the first token.
+ // Sometimes RC tool requires them and sometimes not. We decide to
+ // always require them.
+ if (i >= 1) {
+ if (auto CommaError = consumeType(Kind::Comma))
+ return FailureHandler(std::move(CommaError));
+ }
+
+ if (auto IntResult = readInt())
+ Result.push_back(*IntResult);
+ else
+ return FailureHandler(IntResult.takeError());
+ }
+
+ return std::move(Result);
+}
+
+Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc,
+ ArrayRef<uint32_t> FlagValues) {
+ assert(!FlagDesc.empty());
+ assert(FlagDesc.size() == FlagValues.size());
+
+ uint32_t Result = 0;
+ while (isNextTokenKind(Kind::Comma)) {
+ consume();
+ ASSIGN_OR_RETURN(FlagResult, readIdentifier());
+ bool FoundFlag = false;
+
+ for (size_t FlagId = 0; FlagId < FlagDesc.size(); ++FlagId) {
+ if (!FlagResult->equals_insensitive(FlagDesc[FlagId]))
+ continue;
+
+ Result |= FlagValues[FlagId];
+ FoundFlag = true;
+ break;
+ }
+
+ if (!FoundFlag)
+ return getExpectedError(join(FlagDesc, "/"), true);
+ }
+
+ return Result;
+}
+
+uint16_t RCParser::parseMemoryFlags(uint16_t Flags) {
+ while (!isEof()) {
+ const RCToken &Token = look();
+ if (Token.kind() != Kind::Identifier)
+ return Flags;
+ const StringRef Ident = Token.value();
+ if (Ident.equals_insensitive("PRELOAD"))
+ Flags |= MfPreload;
+ else if (Ident.equals_insensitive("LOADONCALL"))
+ Flags &= ~MfPreload;
+ else if (Ident.equals_insensitive("FIXED"))
+ Flags &= ~(MfMoveable | MfDiscardable);
+ else if (Ident.equals_insensitive("MOVEABLE"))
+ Flags |= MfMoveable;
+ else if (Ident.equals_insensitive("DISCARDABLE"))
+ Flags |= MfDiscardable | MfMoveable | MfPure;
+ else if (Ident.equals_insensitive("PURE"))
+ Flags |= MfPure;
+ else if (Ident.equals_insensitive("IMPURE"))
+ Flags &= ~(MfPure | MfDiscardable);
+ else if (Ident.equals_insensitive("SHARED"))
+ Flags |= MfPure;
+ else if (Ident.equals_insensitive("NONSHARED"))
+ Flags &= ~(MfPure | MfDiscardable);
+ else
+ return Flags;
+ consume();
+ }
+ return Flags;
+}
+
+Expected<OptionalStmtList>
+RCParser::parseOptionalStatements(OptStmtType StmtsType) {
+ OptionalStmtList Result;
+
+ // The last statement is always followed by the start of the block.
+ while (!isNextTokenKind(Kind::BlockBegin)) {
+ ASSIGN_OR_RETURN(SingleParse, parseSingleOptionalStatement(StmtsType));
+ Result.addStmt(std::move(*SingleParse));
+ }
+
+ return std::move(Result);
+}
+
+Expected<std::unique_ptr<OptionalStmt>>
+RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) {
+ ASSIGN_OR_RETURN(TypeToken, readIdentifier());
+ if (TypeToken->equals_insensitive("CHARACTERISTICS"))
+ return parseCharacteristicsStmt();
+ if (TypeToken->equals_insensitive("LANGUAGE"))
+ return parseLanguageStmt();
+ if (TypeToken->equals_insensitive("VERSION"))
+ return parseVersionStmt();
+
+ if (StmtsType != OptStmtType::BasicStmt) {
+ if (TypeToken->equals_insensitive("CAPTION"))
+ return parseCaptionStmt();
+ if (TypeToken->equals_insensitive("CLASS"))
+ return parseClassStmt();
+ if (TypeToken->equals_insensitive("EXSTYLE"))
+ return parseExStyleStmt();
+ if (TypeToken->equals_insensitive("FONT"))
+ return parseFontStmt(StmtsType);
+ if (TypeToken->equals_insensitive("STYLE"))
+ return parseStyleStmt();
+ }
+
+ return getExpectedError("optional statement type, BEGIN or '{'",
+ /* IsAlreadyRead = */ true);
+}
+
+RCParser::ParseType RCParser::parseLanguageResource() {
+ // Read LANGUAGE as an optional statement. If it's read correctly, we can
+ // upcast it to RCResource.
+ return parseLanguageStmt();
+}
+
+RCParser::ParseType RCParser::parseAcceleratorsResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ auto Accels = std::make_unique<AcceleratorsResource>(
+ std::move(*OptStatements), MemoryFlags);
+
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(EventResult, readIntOrString());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ ASSIGN_OR_RETURN(IDResult, readInt());
+ ASSIGN_OR_RETURN(
+ FlagsResult,
+ parseFlags(AcceleratorsResource::Accelerator::OptionsStr,
+ AcceleratorsResource::Accelerator::OptionsFlags));
+ Accels->addAccelerator(*EventResult, *IDResult, *FlagsResult);
+ }
+
+ return std::move(Accels);
+}
+
+RCParser::ParseType RCParser::parseCursorResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(CursorResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<CursorResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(DialogResource::getDefaultMemoryFlags());
+ // Dialog resources have the following format of the arguments:
+ // DIALOG: x, y, width, height [opt stmts...] {controls...}
+ // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...}
+ // These are very similar, so we parse them together.
+ ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4));
+
+ uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0.
+ if (IsExtended && consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(HelpIDResult, readInt());
+ HelpID = *HelpIDResult;
+ }
+
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements(
+ IsExtended ? OptStmtType::DialogExStmt
+ : OptStmtType::DialogStmt));
+
+ assert(isNextTokenKind(Kind::BlockBegin) &&
+ "parseOptionalStatements, when successful, halts on BlockBegin.");
+ consume();
+
+ auto Dialog = std::make_unique<DialogResource>(
+ (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3],
+ HelpID, std::move(*OptStatements), IsExtended, MemoryFlags);
+
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(ControlDefResult, parseControl());
+ Dialog->addControl(std::move(*ControlDefResult));
+ }
+
+ return std::move(Dialog);
+}
+
+RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags());
+ if (isEof())
+ return getExpectedError("filename, '{' or BEGIN");
+
+ // Check if this is a file resource.
+ switch (look().kind()) {
+ case Kind::String:
+ case Kind::Identifier:
+ return std::make_unique<UserDefinedResource>(Type, read().value(),
+ MemoryFlags);
+ default:
+ break;
+ }
+
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+ std::vector<IntOrString> Data;
+
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(Item, readIntOrString());
+ Data.push_back(*Item);
+
+ // There can be zero or more commas after each token (but not before
+ // the first one).
+ while (consumeOptionalType(Kind::Comma)) {
+ }
+ }
+
+ return std::make_unique<UserDefinedResource>(Type, std::move(Data),
+ MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseVersionInfoResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
+ ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
+ return std::make_unique<VersionInfoResource>(
+ std::move(**BlockResult), std::move(*FixedResult), MemoryFlags);
+}
+
+Expected<Control> RCParser::parseControl() {
+ // Each control definition (except CONTROL) follows one of the schemes below
+ // depending on the control class:
+ // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
+ // [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
+ // Note that control ids must be integers.
+ // Text might be either a string or an integer pointing to resource ID.
+ ASSIGN_OR_RETURN(ClassResult, readIdentifier());
+ std::string ClassUpper = ClassResult->upper();
+ auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
+ if (CtlInfo == Control::SupportedCtls.end())
+ return getExpectedError("control type, END or '}'", true);
+
+ // Read caption if necessary.
+ IntOrString Caption{StringRef()};
+ if (CtlInfo->getValue().HasTitle) {
+ ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ Caption = *CaptionResult;
+ }
+
+ ASSIGN_OR_RETURN(ID, readInt());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+
+ IntOrString Class;
+ Optional<IntWithNotMask> Style;
+ if (ClassUpper == "CONTROL") {
+ // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
+ ASSIGN_OR_RETURN(ClassStr, readString());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ Class = *ClassStr;
+ ASSIGN_OR_RETURN(StyleVal, parseIntExpr1());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ Style = *StyleVal;
+ } else {
+ Class = CtlInfo->getValue().CtlClass;
+ }
+
+ // x, y, width, height
+ ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4));
+
+ if (ClassUpper != "CONTROL") {
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Val, parseIntExpr1());
+ Style = *Val;
+ }
+ }
+
+ Optional<uint32_t> ExStyle;
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Val, readInt());
+ ExStyle = *Val;
+ }
+ Optional<uint32_t> HelpID;
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Val, readInt());
+ HelpID = *Val;
+ }
+
+ return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
+ (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
+}
+
+RCParser::ParseType RCParser::parseBitmapResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(BitmapResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<BitmapResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseIconResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(IconResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<IconResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseHTMLResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(HTMLResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(Arg, readFilename());
+ return std::make_unique<HTMLResource>(*Arg, MemoryFlags);
+}
+
+RCParser::ParseType RCParser::parseMenuResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(MenuResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+ ASSIGN_OR_RETURN(Items, parseMenuItemsList());
+ return std::make_unique<MenuResource>(std::move(*OptStatements),
+ std::move(*Items), MemoryFlags);
+}
+
+Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ MenuDefinitionList List;
+
+ // Read a set of items. Each item is of one of three kinds:
+ // MENUITEM SEPARATOR
+ // MENUITEM caption:String, result:Int [, menu flags]...
+ // POPUP caption:String [, menu flags]... { items... }
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
+
+ bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
+ bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
+ if (!IsMenuItem && !IsPopup)
+ return getExpectedError("MENUITEM, POPUP, END or '}'", true);
+
+ if (IsMenuItem && isNextTokenKind(Kind::Identifier)) {
+ // Now, expecting SEPARATOR.
+ ASSIGN_OR_RETURN(SeparatorResult, readIdentifier());
+ if (SeparatorResult->equals_insensitive("SEPARATOR")) {
+ List.addDefinition(std::make_unique<MenuSeparator>());
+ continue;
+ }
+
+ return getExpectedError("SEPARATOR or string", true);
+ }
+
+ // Not a separator. Read the caption.
+ ASSIGN_OR_RETURN(CaptionResult, readString());
+
+ // If MENUITEM, expect also a comma and an integer.
+ uint32_t MenuResult = -1;
+
+ if (IsMenuItem) {
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ ASSIGN_OR_RETURN(IntResult, readInt());
+ MenuResult = *IntResult;
+ }
+
+ ASSIGN_OR_RETURN(FlagsResult, parseFlags(MenuDefinition::OptionsStr,
+ MenuDefinition::OptionsFlags));
+
+ if (IsPopup) {
+ // If POPUP, read submenu items recursively.
+ ASSIGN_OR_RETURN(SubMenuResult, parseMenuItemsList());
+ List.addDefinition(std::make_unique<PopupItem>(
+ *CaptionResult, *FlagsResult, std::move(*SubMenuResult)));
+ continue;
+ }
+
+ assert(IsMenuItem);
+ List.addDefinition(
+ std::make_unique<MenuItem>(*CaptionResult, MenuResult, *FlagsResult));
+ }
+
+ return std::move(List);
+}
+
+RCParser::ParseType RCParser::parseStringTableResource() {
+ uint16_t MemoryFlags =
+ parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
+ ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
+ MemoryFlags);
+
+ // Read strings until we reach the end of the block.
+ while (!consumeOptionalType(Kind::BlockEnd)) {
+ // Each definition consists of string's ID (an integer) and a string.
+ // Some examples in documentation suggest that there might be a comma in
+ // between, however we strictly adhere to the single statement definition.
+ ASSIGN_OR_RETURN(IDResult, readInt());
+ consumeOptionalType(Kind::Comma);
+
+ std::vector<StringRef> Strings;
+ ASSIGN_OR_RETURN(StrResult, readString());
+ Strings.push_back(*StrResult);
+ while (isNextTokenKind(Kind::String))
+ Strings.push_back(read().value());
+
+ Table->addStrings(*IDResult, std::move(Strings));
+ }
+
+ return std::move(Table);
+}
+
+Expected<std::unique_ptr<VersionInfoBlock>>
+RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
+ RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
+
+ auto Contents = std::make_unique<VersionInfoBlock>(BlockName);
+
+ while (!isNextTokenKind(Kind::BlockEnd)) {
+ ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
+ Contents->addStmt(std::move(*Stmt));
+ }
+
+ consume(); // Consume BlockEnd.
+
+ return std::move(Contents);
+}
+
+Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
+ // Expect either BLOCK or VALUE, then a name or a key (a string).
+ ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+
+ if (TypeResult->equals_insensitive("BLOCK")) {
+ ASSIGN_OR_RETURN(NameResult, readString());
+ return parseVersionInfoBlockContents(*NameResult);
+ }
+
+ if (TypeResult->equals_insensitive("VALUE")) {
+ ASSIGN_OR_RETURN(KeyResult, readString());
+ // Read a non-empty list of strings and/or ints, each
+ // possibly preceded by a comma. Unfortunately, the tool behavior depends
+ // on them existing or not, so we need to memorize where we found them.
+ std::vector<IntOrString> Values;
+ BitVector PrecedingCommas;
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ while (!isNextTokenKind(Kind::Identifier) &&
+ !isNextTokenKind(Kind::BlockEnd)) {
+ // Try to eat a comma if it's not the first statement.
+ bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
+ ASSIGN_OR_RETURN(ValueResult, readIntOrString());
+ Values.push_back(*ValueResult);
+ PrecedingCommas.push_back(HadComma);
+ }
+ return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
+ std::move(PrecedingCommas));
+ }
+
+ return getExpectedError("BLOCK or VALUE", true);
+}
+
+Expected<VersionInfoResource::VersionInfoFixed>
+RCParser::parseVersionInfoFixed() {
+ using RetType = VersionInfoResource::VersionInfoFixed;
+ RetType Result;
+
+ // Read until the beginning of the block.
+ while (!isNextTokenKind(Kind::BlockBegin)) {
+ ASSIGN_OR_RETURN(TypeResult, readIdentifier());
+ auto FixedType = RetType::getFixedType(*TypeResult);
+
+ if (!RetType::isTypeSupported(FixedType))
+ return getExpectedError("fixed VERSIONINFO statement type", true);
+ if (Result.IsTypePresent[FixedType])
+ return getExpectedError("yet unread fixed VERSIONINFO statement type",
+ true);
+
+ // VERSION variations take multiple integers.
+ size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
+ ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(1, NumInts));
+ SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
+ while (ArgInts.size() < NumInts)
+ ArgInts.push_back(0);
+ Result.setValue(FixedType, ArgInts);
+ }
+
+ return Result;
+}
+
+RCParser::ParseOptionType RCParser::parseLanguageStmt() {
+ ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
+ return std::make_unique<LanguageResource>((*Args)[0], (*Args)[1]);
+}
+
+RCParser::ParseOptionType RCParser::parseCharacteristicsStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<CharacteristicsStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseVersionStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<VersionStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseCaptionStmt() {
+ ASSIGN_OR_RETURN(Arg, readString());
+ return std::make_unique<CaptionStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseClassStmt() {
+ ASSIGN_OR_RETURN(Arg, readIntOrString());
+ return std::make_unique<ClassStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
+ assert(DialogType != OptStmtType::BasicStmt);
+
+ ASSIGN_OR_RETURN(SizeResult, readInt());
+ RETURN_IF_ERROR(consumeType(Kind::Comma));
+ ASSIGN_OR_RETURN(NameResult, readString());
+
+ // Default values for the optional arguments.
+ uint32_t FontWeight = 0;
+ bool FontItalic = false;
+ uint32_t FontCharset = 1;
+ if (DialogType == OptStmtType::DialogExStmt) {
+ if (consumeOptionalType(Kind::Comma)) {
+ ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 0, /* max = */ 3));
+ if (Args->size() >= 1)
+ FontWeight = (*Args)[0];
+ if (Args->size() >= 2)
+ FontItalic = (*Args)[1] != 0;
+ if (Args->size() >= 3)
+ FontCharset = (*Args)[2];
+ }
+ }
+ return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
+ FontItalic, FontCharset);
+}
+
+RCParser::ParseOptionType RCParser::parseStyleStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<StyleStmt>(*Arg);
+}
+
+RCParser::ParseOptionType RCParser::parseExStyleStmt() {
+ ASSIGN_OR_RETURN(Arg, readInt());
+ return std::make_unique<ExStyleStmt>(*Arg);
+}
+
+Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) {
+ return make_error<ParserError>(
+ Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End);
+}
+
+} // namespace rc
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h
new file mode 100644
index 0000000000..a7d3b595f7
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptParser.h
@@ -0,0 +1,189 @@
+//===-- ResourceScriptParser.h ----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This defines the RC scripts parser. It takes a sequence of RC tokens
+// and then provides the method to parse the resources one by one.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
+
+#include "ResourceScriptStmt.h"
+#include "ResourceScriptToken.h"
+
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <system_error>
+#include <vector>
+
+namespace llvm {
+namespace rc {
+
+class RCParser {
+public:
+ using LocIter = std::vector<RCToken>::iterator;
+ using ParseType = Expected<std::unique_ptr<RCResource>>;
+ using ParseOptionType = Expected<std::unique_ptr<OptionalStmt>>;
+
+ // Class describing a single failure of parser.
+ class ParserError : public ErrorInfo<ParserError> {
+ public:
+ ParserError(const Twine &Expected, const LocIter CurLoc, const LocIter End);
+
+ void log(raw_ostream &OS) const override { OS << CurMessage; }
+ std::error_code convertToErrorCode() const override {
+ return std::make_error_code(std::errc::invalid_argument);
+ }
+ const std::string &getMessage() const { return CurMessage; }
+
+ static char ID; // Keep llvm::Error happy.
+
+ private:
+ std::string CurMessage;
+ LocIter ErrorLoc, FileEnd;
+ };
+
+ explicit RCParser(std::vector<RCToken> TokenList);
+
+ // Reads and returns a single resource definition, or error message if any
+ // occurred.
+ ParseType parseSingleResource();
+
+ bool isEof() const;
+
+private:
+ using Kind = RCToken::Kind;
+
+ // Checks if the current parser state points to the token of type TokenKind.
+ bool isNextTokenKind(Kind TokenKind) const;
+
+ // These methods assume that the parser is not in EOF state.
+
+ // Take a look at the current token. Do not fetch it.
+ const RCToken &look() const;
+ // Read the current token and advance the state by one token.
+ const RCToken &read();
+ // Advance the state by one token, discarding the current token.
+ void consume();
+
+ // The following methods try to read a single token, check if it has the
+ // correct type and then parse it.
+ // Each integer can be written as an arithmetic expression producing an
+ // unsigned 32-bit integer.
+ Expected<RCInt> readInt(); // Parse an integer.
+ Expected<StringRef> readString(); // Parse a string.
+ Expected<StringRef> readIdentifier(); // Parse an identifier.
+ Expected<StringRef> readFilename(); // Parse a filename.
+ Expected<IntOrString> readIntOrString(); // Parse an integer or a string.
+ Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier.
+
+ // Helper integer expression parsing methods.
+ Expected<IntWithNotMask> parseIntExpr1();
+ Expected<IntWithNotMask> parseIntExpr2();
+
+ // Advance the state by one, discarding the current token.
+ // If the discarded token had an incorrect type, fail.
+ Error consumeType(Kind TokenKind);
+
+ // Check the current token type. If it's TokenKind, discard it.
+ // Return true if the parser consumed this token successfully.
+ bool consumeOptionalType(Kind TokenKind);
+
+ // Read at least MinCount, and at most MaxCount integers separated by
+ // commas. The parser stops reading after fetching MaxCount integers
+ // or after an error occurs. Whenever the parser reads a comma, it
+ // expects an integer to follow.
+ Expected<SmallVector<RCInt, 8>> readIntsWithCommas(size_t MinCount,
+ size_t MaxCount);
+
+ // Read an unknown number of flags preceded by commas. Each correct flag
+ // has an entry in FlagDesc array of length NumFlags. In case i-th
+ // flag (0-based) has been read, the result is OR-ed with FlagValues[i].
+ // As long as parser has a comma to read, it expects to be fed with
+ // a correct flag afterwards.
+ Expected<uint32_t> parseFlags(ArrayRef<StringRef> FlagDesc,
+ ArrayRef<uint32_t> FlagValues);
+
+ // Reads a set of optional statements. These can change the behavior of
+ // a number of resource types (e.g. STRINGTABLE, MENU or DIALOG) if provided
+ // before the main block with the contents of the resource.
+ // Usually, resources use a basic set of optional statements:
+ // CHARACTERISTICS, LANGUAGE, VERSION
+ // However, DIALOG and DIALOGEX extend this list by the following items:
+ // CAPTION, CLASS, EXSTYLE, FONT, MENU, STYLE
+ // UseExtendedStatements flag (off by default) allows the parser to read
+ // the additional types of statements.
+ //
+ // Ref (to the list of all optional statements):
+ // msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx
+ enum class OptStmtType { BasicStmt, DialogStmt, DialogExStmt };
+
+ uint16_t parseMemoryFlags(uint16_t DefaultFlags);
+
+ Expected<OptionalStmtList>
+ parseOptionalStatements(OptStmtType StmtsType = OptStmtType::BasicStmt);
+
+ // Read a single optional statement.
+ Expected<std::unique_ptr<OptionalStmt>>
+ parseSingleOptionalStatement(OptStmtType StmtsType = OptStmtType::BasicStmt);
+
+ // Top-level resource parsers.
+ ParseType parseLanguageResource();
+ ParseType parseAcceleratorsResource();
+ ParseType parseBitmapResource();
+ ParseType parseCursorResource();
+ ParseType parseDialogResource(bool IsExtended);
+ ParseType parseIconResource();
+ ParseType parseHTMLResource();
+ ParseType parseMenuResource();
+ ParseType parseStringTableResource();
+ ParseType parseUserDefinedResource(IntOrString Type);
+ ParseType parseVersionInfoResource();
+
+ // Helper DIALOG parser - a single control.
+ Expected<Control> parseControl();
+
+ // Helper MENU parser.
+ Expected<MenuDefinitionList> parseMenuItemsList();
+
+ // Helper VERSIONINFO parser - read the contents of a single BLOCK statement,
+ // from BEGIN to END.
+ Expected<std::unique_ptr<VersionInfoBlock>>
+ parseVersionInfoBlockContents(StringRef BlockName);
+ // Helper VERSIONINFO parser - read either VALUE or BLOCK statement.
+ Expected<std::unique_ptr<VersionInfoStmt>> parseVersionInfoStmt();
+ // Helper VERSIONINFO parser - read fixed VERSIONINFO statements.
+ Expected<VersionInfoResource::VersionInfoFixed> parseVersionInfoFixed();
+
+ // Optional statement parsers.
+ ParseOptionType parseLanguageStmt();
+ ParseOptionType parseCharacteristicsStmt();
+ ParseOptionType parseVersionStmt();
+ ParseOptionType parseCaptionStmt();
+ ParseOptionType parseClassStmt();
+ ParseOptionType parseExStyleStmt();
+ ParseOptionType parseFontStmt(OptStmtType DialogType);
+ ParseOptionType parseStyleStmt();
+
+ // Raises an error. If IsAlreadyRead = false (default), this complains about
+ // the token that couldn't be parsed. If the flag is on, this complains about
+ // the correctly read token that makes no sense (that is, the current parser
+ // state is beyond the erroneous token.)
+ Error getExpectedError(const Twine &Message, bool IsAlreadyRead = false);
+
+ std::vector<RCToken> Tokens;
+ LocIter CurLoc;
+ const LocIter End;
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp
new file mode 100644
index 0000000000..ef8c345418
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.cpp
@@ -0,0 +1,294 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This implements methods defined in ResourceScriptStmt.h.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptStmt.h"
+
+namespace llvm {
+namespace rc {
+
+raw_ostream &operator<<(raw_ostream &OS, const IntOrString &Item) {
+ if (Item.IsInt)
+ return OS << Item.Data.Int;
+ else
+ return OS << Item.Data.String;
+}
+
+raw_ostream &OptionalStmtList::log(raw_ostream &OS) const {
+ for (const auto &Stmt : Statements) {
+ OS << " Option: ";
+ Stmt->log(OS);
+ }
+ return OS;
+}
+
+raw_ostream &LanguageResource::log(raw_ostream &OS) const {
+ return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n";
+}
+
+StringRef AcceleratorsResource::Accelerator::OptionsStr
+ [AcceleratorsResource::Accelerator::NumFlags] = {
+ "ASCII", "VIRTKEY", "NOINVERT", "ALT", "SHIFT", "CONTROL"};
+
+uint32_t AcceleratorsResource::Accelerator::OptionsFlags
+ [AcceleratorsResource::Accelerator::NumFlags] = {ASCII, VIRTKEY, NOINVERT,
+ ALT, SHIFT, CONTROL};
+
+raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const {
+ OS << "Accelerators (" << ResName << "): \n";
+ OptStatements->log(OS);
+ for (const auto &Acc : Accelerators) {
+ OS << " Accelerator: " << Acc.Event << " " << Acc.Id;
+ for (size_t i = 0; i < Accelerator::NumFlags; ++i)
+ if (Acc.Flags & Accelerator::OptionsFlags[i])
+ OS << " " << Accelerator::OptionsStr[i];
+ OS << "\n";
+ }
+ return OS;
+}
+
+raw_ostream &BitmapResource::log(raw_ostream &OS) const {
+ return OS << "Bitmap (" << ResName << "): " << BitmapLoc << "\n";
+}
+
+raw_ostream &CursorResource::log(raw_ostream &OS) const {
+ return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n";
+}
+
+raw_ostream &IconResource::log(raw_ostream &OS) const {
+ return OS << "Icon (" << ResName << "): " << IconLoc << "\n";
+}
+
+raw_ostream &HTMLResource::log(raw_ostream &OS) const {
+ return OS << "HTML (" << ResName << "): " << HTMLLoc << "\n";
+}
+
+StringRef MenuDefinition::OptionsStr[MenuDefinition::NumFlags] = {
+ "CHECKED", "GRAYED", "HELP", "INACTIVE", "MENUBARBREAK", "MENUBREAK"};
+
+uint32_t MenuDefinition::OptionsFlags[MenuDefinition::NumFlags] = {
+ CHECKED, GRAYED, HELP, INACTIVE, MENUBARBREAK, MENUBREAK};
+
+raw_ostream &MenuDefinition::logFlags(raw_ostream &OS, uint16_t Flags) {
+ for (size_t i = 0; i < NumFlags; ++i)
+ if (Flags & OptionsFlags[i])
+ OS << " " << OptionsStr[i];
+ return OS;
+}
+
+raw_ostream &MenuDefinitionList::log(raw_ostream &OS) const {
+ OS << " Menu list starts\n";
+ for (auto &Item : Definitions)
+ Item->log(OS);
+ return OS << " Menu list ends\n";
+}
+
+raw_ostream &MenuItem::log(raw_ostream &OS) const {
+ OS << " MenuItem (" << Name << "), ID = " << Id;
+ logFlags(OS, Flags);
+ return OS << "\n";
+}
+
+raw_ostream &MenuSeparator::log(raw_ostream &OS) const {
+ return OS << " Menu separator\n";
+}
+
+raw_ostream &PopupItem::log(raw_ostream &OS) const {
+ OS << " Popup (" << Name << ")";
+ logFlags(OS, Flags);
+ OS << ":\n";
+ return SubItems.log(OS);
+}
+
+raw_ostream &MenuResource::log(raw_ostream &OS) const {
+ OS << "Menu (" << ResName << "):\n";
+ OptStatements->log(OS);
+ return Elements.log(OS);
+}
+
+raw_ostream &StringTableResource::log(raw_ostream &OS) const {
+ OS << "StringTable:\n";
+ OptStatements->log(OS);
+ for (const auto &String : Table) {
+ OS << " " << String.first << " =>";
+ for (const auto &S : String.second)
+ OS << " " << S;
+ OS << "\n";
+ }
+ return OS;
+}
+
+const StringMap<Control::CtlInfo> Control::SupportedCtls = {
+ {"LTEXT", CtlInfo{0x50020000, ClsStatic, true}},
+ {"CTEXT", CtlInfo{0x50020001, ClsStatic, true}},
+ {"RTEXT", CtlInfo{0x50020002, ClsStatic, true}},
+ {"ICON", CtlInfo{0x50000003, ClsStatic, true}},
+ {"PUSHBUTTON", CtlInfo{0x50010000, ClsButton, true}},
+ {"DEFPUSHBUTTON", CtlInfo{0x50010001, ClsButton, true}},
+ {"AUTO3STATE", CtlInfo{0x50010006, ClsButton, true}},
+ {"AUTOCHECKBOX", CtlInfo{0x50010003, ClsButton, true}},
+ {"AUTORADIOBUTTON", CtlInfo{0x50000009, ClsButton, true}},
+ {"CHECKBOX", CtlInfo{0x50010002, ClsButton, true}},
+ {"GROUPBOX", CtlInfo{0x50000007, ClsButton, true}},
+ {"RADIOBUTTON", CtlInfo{0x50000004, ClsButton, true}},
+ {"STATE3", CtlInfo{0x50010005, ClsButton, true}},
+ {"PUSHBOX", CtlInfo{0x5001000A, ClsButton, true}},
+ {"EDITTEXT", CtlInfo{0x50810000, ClsEdit, false}},
+ {"COMBOBOX", CtlInfo{0x50000000, ClsComboBox, false}},
+ {"LISTBOX", CtlInfo{0x50800001, ClsListBox, false}},
+ {"SCROLLBAR", CtlInfo{0x50000000, ClsScrollBar, false}},
+ {"CONTROL", CtlInfo{0x50000000, 0, true}},
+};
+
+raw_ostream &Control::log(raw_ostream &OS) const {
+ OS << " Control (" << ID << "): " << Type << ", title: " << Title
+ << ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height
+ << "]";
+ if (Style)
+ OS << ", style: " << (*Style).getValue();
+ if (ExtStyle)
+ OS << ", ext. style: " << *ExtStyle;
+ if (HelpID)
+ OS << ", help ID: " << *HelpID;
+ return OS << "\n";
+}
+
+raw_ostream &DialogResource::log(raw_ostream &OS) const {
+ OS << "Dialog" << (IsExtended ? "Ex" : "") << " (" << ResName << "): loc: ("
+ << X << ", " << Y << "), size: [" << Width << ", " << Height
+ << "], help ID: " << HelpID << "\n";
+ OptStatements->log(OS);
+ for (auto &Ctl : Controls)
+ Ctl.log(OS);
+ return OS;
+}
+
+raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const {
+ OS << " Start of block (name: " << Name << ")\n";
+ for (auto &Stmt : Stmts)
+ Stmt->log(OS);
+ return OS << " End of block\n";
+}
+
+raw_ostream &VersionInfoValue::log(raw_ostream &OS) const {
+ OS << " " << Key << " =>";
+ size_t NumValues = Values.size();
+ for (size_t Id = 0; Id < NumValues; ++Id) {
+ if (Id > 0 && HasPrecedingComma[Id])
+ OS << ",";
+ OS << " " << Values[Id];
+ }
+ return OS << "\n";
+}
+
+using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
+using VersionInfoFixedType = VersionInfoFixed::VersionInfoFixedType;
+
+const StringRef
+ VersionInfoFixed::FixedFieldsNames[VersionInfoFixed::FtNumTypes] = {
+ "", "FILEVERSION", "PRODUCTVERSION", "FILEFLAGSMASK",
+ "FILEFLAGS", "FILEOS", "FILETYPE", "FILESUBTYPE"};
+
+const StringMap<VersionInfoFixedType> VersionInfoFixed::FixedFieldsInfoMap = {
+ {FixedFieldsNames[FtFileVersion], FtFileVersion},
+ {FixedFieldsNames[FtProductVersion], FtProductVersion},
+ {FixedFieldsNames[FtFileFlagsMask], FtFileFlagsMask},
+ {FixedFieldsNames[FtFileFlags], FtFileFlags},
+ {FixedFieldsNames[FtFileOS], FtFileOS},
+ {FixedFieldsNames[FtFileType], FtFileType},
+ {FixedFieldsNames[FtFileSubtype], FtFileSubtype}};
+
+VersionInfoFixedType VersionInfoFixed::getFixedType(StringRef Type) {
+ auto UpperType = Type.upper();
+ auto Iter = FixedFieldsInfoMap.find(UpperType);
+ if (Iter != FixedFieldsInfoMap.end())
+ return Iter->getValue();
+ return FtUnknown;
+}
+
+bool VersionInfoFixed::isTypeSupported(VersionInfoFixedType Type) {
+ return FtUnknown < Type && Type < FtNumTypes;
+}
+
+bool VersionInfoFixed::isVersionType(VersionInfoFixedType Type) {
+ switch (Type) {
+ case FtFileVersion:
+ case FtProductVersion:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+raw_ostream &VersionInfoFixed::log(raw_ostream &OS) const {
+ for (int Type = FtUnknown; Type < FtNumTypes; ++Type) {
+ if (!isTypeSupported((VersionInfoFixedType)Type))
+ continue;
+ OS << " Fixed: " << FixedFieldsNames[Type] << ":";
+ for (uint32_t Val : FixedInfo[Type])
+ OS << " " << Val;
+ OS << "\n";
+ }
+ return OS;
+}
+
+raw_ostream &VersionInfoResource::log(raw_ostream &OS) const {
+ OS << "VersionInfo (" << ResName << "):\n";
+ FixedData.log(OS);
+ return MainBlock.log(OS);
+}
+
+raw_ostream &UserDefinedResource::log(raw_ostream &OS) const {
+ OS << "User-defined (type: " << Type << ", name: " << ResName << "): ";
+ if (IsFileResource)
+ return OS << FileLoc << "\n";
+ OS << "data = ";
+ for (auto &Item : Contents)
+ OS << Item << " ";
+ return OS << "\n";
+}
+
+raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const {
+ return OS << "Characteristics: " << Value << "\n";
+}
+
+raw_ostream &VersionStmt::log(raw_ostream &OS) const {
+ return OS << "Version: " << Value << "\n";
+}
+
+raw_ostream &CaptionStmt::log(raw_ostream &OS) const {
+ return OS << "Caption: " << Value << "\n";
+}
+
+raw_ostream &ClassStmt::log(raw_ostream &OS) const {
+ return OS << "Class: " << Value << "\n";
+}
+
+raw_ostream &FontStmt::log(raw_ostream &OS) const {
+ OS << "Font: size = " << Size << ", face = " << Name
+ << ", weight = " << Weight;
+ if (Italic)
+ OS << ", italic";
+ return OS << ", charset = " << Charset << "\n";
+}
+
+raw_ostream &StyleStmt::log(raw_ostream &OS) const {
+ return OS << "Style: " << Value << "\n";
+}
+
+raw_ostream &ExStyleStmt::log(raw_ostream &OS) const {
+ return OS << "ExStyle: " << Value << "\n";
+}
+
+} // namespace rc
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h
new file mode 100644
index 0000000000..4b659f083b
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptStmt.h
@@ -0,0 +1,954 @@
+//===-- ResourceScriptStmt.h ------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This lists all the resource and statement types occurring in RC scripts.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
+
+#include "ResourceScriptToken.h"
+#include "ResourceVisitor.h"
+
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/StringSet.h"
+
+namespace llvm {
+namespace rc {
+
+// Integer wrapper that also holds information whether the user declared
+// the integer to be long (by appending L to the end of the integer) or not.
+// It allows to be implicitly cast from and to uint32_t in order
+// to be compatible with the parts of code that don't care about the integers
+// being marked long.
+class RCInt {
+ uint32_t Val;
+ bool Long;
+
+public:
+ RCInt(const RCToken &Token)
+ : Val(Token.intValue()), Long(Token.isLongInt()) {}
+ RCInt(uint32_t Value) : Val(Value), Long(false) {}
+ RCInt(uint32_t Value, bool IsLong) : Val(Value), Long(IsLong) {}
+ operator uint32_t() const { return Val; }
+ bool isLong() const { return Long; }
+
+ RCInt &operator+=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val + Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt &operator-=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val - Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt &operator|=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt &operator&=(const RCInt &Rhs) {
+ std::tie(Val, Long) = std::make_pair(Val & Rhs.Val, Long | Rhs.Long);
+ return *this;
+ }
+
+ RCInt operator-() const { return {-Val, Long}; }
+ RCInt operator~() const { return {~Val, Long}; }
+
+ friend raw_ostream &operator<<(raw_ostream &OS, const RCInt &Int) {
+ return OS << Int.Val << (Int.Long ? "L" : "");
+ }
+};
+
+class IntWithNotMask {
+private:
+ RCInt Value;
+ int32_t NotMask;
+
+public:
+ IntWithNotMask() : IntWithNotMask(RCInt(0)) {}
+ IntWithNotMask(RCInt Value, int32_t NotMask = 0) : Value(Value), NotMask(NotMask) {}
+
+ RCInt getValue() const {
+ return Value;
+ }
+
+ uint32_t getNotMask() const {
+ return NotMask;
+ }
+
+ IntWithNotMask &operator+=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value += Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask &operator-=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value -= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask &operator|=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value |= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask &operator&=(const IntWithNotMask &Rhs) {
+ Value &= ~Rhs.NotMask;
+ Value &= Rhs.Value;
+ NotMask |= Rhs.NotMask;
+ return *this;
+ }
+
+ IntWithNotMask operator-() const { return {-Value, NotMask}; }
+ IntWithNotMask operator~() const { return {~Value, 0}; }
+
+ friend raw_ostream &operator<<(raw_ostream &OS, const IntWithNotMask &Int) {
+ return OS << Int.Value;
+ }
+};
+
+// A class holding a name - either an integer or a reference to the string.
+class IntOrString {
+private:
+ union Data {
+ RCInt Int;
+ StringRef String;
+ Data(RCInt Value) : Int(Value) {}
+ Data(const StringRef Value) : String(Value) {}
+ Data(const RCToken &Token) {
+ if (Token.kind() == RCToken::Kind::Int)
+ Int = RCInt(Token);
+ else
+ String = Token.value();
+ }
+ } Data;
+ bool IsInt;
+
+public:
+ IntOrString() : IntOrString(RCInt(0)) {}
+ IntOrString(uint32_t Value) : Data(Value), IsInt(true) {}
+ IntOrString(RCInt Value) : Data(Value), IsInt(true) {}
+ IntOrString(StringRef Value) : Data(Value), IsInt(false) {}
+ IntOrString(const RCToken &Token)
+ : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {}
+
+ bool equalsLower(const char *Str) {
+ return !IsInt && Data.String.equals_insensitive(Str);
+ }
+
+ bool isInt() const { return IsInt; }
+
+ RCInt getInt() const {
+ assert(IsInt);
+ return Data.Int;
+ }
+
+ const StringRef &getString() const {
+ assert(!IsInt);
+ return Data.String;
+ }
+
+ operator Twine() const {
+ return isInt() ? Twine(getInt()) : Twine(getString());
+ }
+
+ friend raw_ostream &operator<<(raw_ostream &, const IntOrString &);
+};
+
+enum ResourceKind {
+ // These resource kinds have corresponding .res resource type IDs
+ // (TYPE in RESOURCEHEADER structure). The numeric value assigned to each
+ // kind is equal to this type ID.
+ RkNull = 0,
+ RkSingleCursor = 1,
+ RkBitmap = 2,
+ RkSingleIcon = 3,
+ RkMenu = 4,
+ RkDialog = 5,
+ RkStringTableBundle = 6,
+ RkAccelerators = 9,
+ RkRcData = 10,
+ RkCursorGroup = 12,
+ RkIconGroup = 14,
+ RkVersionInfo = 16,
+ RkHTML = 23,
+
+ // These kinds don't have assigned type IDs (they might be the resources
+ // of invalid kind, expand to many resource structures in .res files,
+ // or have variable type ID). In order to avoid ID clashes with IDs above,
+ // we assign the kinds the values 256 and larger.
+ RkInvalid = 256,
+ RkBase,
+ RkCursor,
+ RkIcon,
+ RkStringTable,
+ RkUser,
+ RkSingleCursorOrIconRes,
+ RkCursorOrIconGroupRes,
+};
+
+// Non-zero memory flags.
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648027(v=vs.85).aspx
+enum MemoryFlags {
+ MfMoveable = 0x10,
+ MfPure = 0x20,
+ MfPreload = 0x40,
+ MfDiscardable = 0x1000
+};
+
+// Base resource. All the resources should derive from this base.
+class RCResource {
+public:
+ IntOrString ResName;
+ uint16_t MemoryFlags = getDefaultMemoryFlags();
+ void setName(const IntOrString &Name) { ResName = Name; }
+ virtual raw_ostream &log(raw_ostream &OS) const {
+ return OS << "Base statement\n";
+ };
+ RCResource() {}
+ RCResource(uint16_t Flags) : MemoryFlags(Flags) {}
+ virtual ~RCResource() {}
+
+ virtual Error visit(Visitor *) const {
+ llvm_unreachable("This is unable to call methods from Visitor base");
+ }
+
+ // Apply the statements attached to this resource. Generic resources
+ // don't have any.
+ virtual Error applyStmts(Visitor *) const { return Error::success(); }
+
+ // By default, memory flags are DISCARDABLE | PURE | MOVEABLE.
+ static uint16_t getDefaultMemoryFlags() {
+ return MfDiscardable | MfPure | MfMoveable;
+ }
+
+ virtual ResourceKind getKind() const { return RkBase; }
+ static bool classof(const RCResource *Res) { return true; }
+
+ virtual IntOrString getResourceType() const {
+ llvm_unreachable("This cannot be called on objects without types.");
+ }
+ virtual Twine getResourceTypeName() const {
+ llvm_unreachable("This cannot be called on objects without types.");
+ };
+};
+
+// An empty resource. It has no content, type 0, ID 0 and all of its
+// characteristics are equal to 0.
+class NullResource : public RCResource {
+public:
+ NullResource() : RCResource(0) {}
+ raw_ostream &log(raw_ostream &OS) const override {
+ return OS << "Null resource\n";
+ }
+ Error visit(Visitor *V) const override { return V->visitNullResource(this); }
+ IntOrString getResourceType() const override { return 0; }
+ Twine getResourceTypeName() const override { return "(NULL)"; }
+};
+
+// Optional statement base. All such statements should derive from this base.
+class OptionalStmt : public RCResource {};
+
+class OptionalStmtList : public OptionalStmt {
+ std::vector<std::unique_ptr<OptionalStmt>> Statements;
+
+public:
+ OptionalStmtList() {}
+ raw_ostream &log(raw_ostream &OS) const override;
+
+ void addStmt(std::unique_ptr<OptionalStmt> Stmt) {
+ Statements.push_back(std::move(Stmt));
+ }
+
+ Error visit(Visitor *V) const override {
+ for (auto &StmtPtr : Statements)
+ if (auto Err = StmtPtr->visit(V))
+ return Err;
+ return Error::success();
+ }
+};
+
+class OptStatementsRCResource : public RCResource {
+public:
+ std::unique_ptr<OptionalStmtList> OptStatements;
+
+ OptStatementsRCResource(OptionalStmtList &&Stmts,
+ uint16_t Flags = RCResource::getDefaultMemoryFlags())
+ : RCResource(Flags),
+ OptStatements(std::make_unique<OptionalStmtList>(std::move(Stmts))) {}
+
+ Error applyStmts(Visitor *V) const override {
+ return OptStatements->visit(V);
+ }
+};
+
+// LANGUAGE statement. It can occur both as a top-level statement (in such
+// a situation, it changes the default language until the end of the file)
+// and as an optional resource statement (then it changes the language
+// of a single resource).
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx
+class LanguageResource : public OptionalStmt {
+public:
+ uint32_t Lang, SubLang;
+
+ LanguageResource(uint32_t LangId, uint32_t SubLangId)
+ : Lang(LangId), SubLang(SubLangId) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ // This is not a regular top-level statement; when it occurs, it just
+ // modifies the language context.
+ Error visit(Visitor *V) const override { return V->visitLanguageStmt(this); }
+ Twine getResourceTypeName() const override { return "LANGUAGE"; }
+};
+
+// ACCELERATORS resource. Defines a named table of accelerators for the app.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380610(v=vs.85).aspx
+class AcceleratorsResource : public OptStatementsRCResource {
+public:
+ class Accelerator {
+ public:
+ IntOrString Event;
+ uint32_t Id;
+ uint16_t Flags;
+
+ enum Options {
+ // This is actually 0x0000 (accelerator is assumed to be ASCII if it's
+ // not VIRTKEY). However, rc.exe behavior is different in situations
+ // "only ASCII defined" and "neither ASCII nor VIRTKEY defined".
+ // Therefore, we include ASCII as another flag. This must be zeroed
+ // when serialized.
+ ASCII = 0x8000,
+ VIRTKEY = 0x0001,
+ NOINVERT = 0x0002,
+ ALT = 0x0010,
+ SHIFT = 0x0004,
+ CONTROL = 0x0008
+ };
+
+ static constexpr size_t NumFlags = 6;
+ static StringRef OptionsStr[NumFlags];
+ static uint32_t OptionsFlags[NumFlags];
+ };
+
+ AcceleratorsResource(OptionalStmtList &&List, uint16_t Flags)
+ : OptStatementsRCResource(std::move(List), Flags) {}
+
+ std::vector<Accelerator> Accelerators;
+
+ void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) {
+ Accelerators.push_back(Accelerator{Event, Id, Flags});
+ }
+ raw_ostream &log(raw_ostream &) const override;
+
+ IntOrString getResourceType() const override { return RkAccelerators; }
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+ Twine getResourceTypeName() const override { return "ACCELERATORS"; }
+
+ Error visit(Visitor *V) const override {
+ return V->visitAcceleratorsResource(this);
+ }
+ ResourceKind getKind() const override { return RkAccelerators; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkAccelerators;
+ }
+};
+
+// BITMAP resource. Represents a bitmap (".bmp") file.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380680(v=vs.85).aspx
+class BitmapResource : public RCResource {
+public:
+ StringRef BitmapLoc;
+
+ BitmapResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), BitmapLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ IntOrString getResourceType() const override { return RkBitmap; }
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+
+ Twine getResourceTypeName() const override { return "BITMAP"; }
+ Error visit(Visitor *V) const override {
+ return V->visitBitmapResource(this);
+ }
+ ResourceKind getKind() const override { return RkBitmap; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkBitmap;
+ }
+};
+
+// CURSOR resource. Represents a single cursor (".cur") file.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx
+class CursorResource : public RCResource {
+public:
+ StringRef CursorLoc;
+
+ CursorResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), CursorLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "CURSOR"; }
+ static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; }
+ Error visit(Visitor *V) const override {
+ return V->visitCursorResource(this);
+ }
+ ResourceKind getKind() const override { return RkCursor; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkCursor;
+ }
+};
+
+// ICON resource. Represents a single ".ico" file containing a group of icons.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381018(v=vs.85).aspx
+class IconResource : public RCResource {
+public:
+ StringRef IconLoc;
+
+ IconResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), IconLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "ICON"; }
+ static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; }
+ Error visit(Visitor *V) const override { return V->visitIconResource(this); }
+ ResourceKind getKind() const override { return RkIcon; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkIcon;
+ }
+};
+
+// HTML resource. Represents a local webpage that is to be embedded into the
+// resulting resource file. It embeds a file only - no additional resources
+// (images etc.) are included with this resource.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa966018(v=vs.85).aspx
+class HTMLResource : public RCResource {
+public:
+ StringRef HTMLLoc;
+
+ HTMLResource(StringRef Location, uint16_t Flags)
+ : RCResource(Flags), HTMLLoc(Location) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Error visit(Visitor *V) const override { return V->visitHTMLResource(this); }
+
+ // Curiously, file resources don't have DISCARDABLE flag set.
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+ IntOrString getResourceType() const override { return RkHTML; }
+ Twine getResourceTypeName() const override { return "HTML"; }
+ ResourceKind getKind() const override { return RkHTML; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkHTML;
+ }
+};
+
+// -- MENU resource and its helper classes --
+// This resource describes the contents of an application menu
+// (usually located in the upper part of the dialog.)
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381025(v=vs.85).aspx
+
+// Description of a single submenu item.
+class MenuDefinition {
+public:
+ enum Options {
+ CHECKED = 0x0008,
+ GRAYED = 0x0001,
+ HELP = 0x4000,
+ INACTIVE = 0x0002,
+ MENUBARBREAK = 0x0020,
+ MENUBREAK = 0x0040
+ };
+
+ enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup };
+
+ static constexpr size_t NumFlags = 6;
+ static StringRef OptionsStr[NumFlags];
+ static uint32_t OptionsFlags[NumFlags];
+ static raw_ostream &logFlags(raw_ostream &, uint16_t Flags);
+ virtual raw_ostream &log(raw_ostream &OS) const {
+ return OS << "Base menu definition\n";
+ }
+ virtual ~MenuDefinition() {}
+
+ virtual uint16_t getResFlags() const { return 0; }
+ virtual MenuDefKind getKind() const { return MkBase; }
+};
+
+// Recursive description of a whole submenu.
+class MenuDefinitionList : public MenuDefinition {
+public:
+ std::vector<std::unique_ptr<MenuDefinition>> Definitions;
+
+ void addDefinition(std::unique_ptr<MenuDefinition> Def) {
+ Definitions.push_back(std::move(Def));
+ }
+ raw_ostream &log(raw_ostream &) const override;
+};
+
+// Separator in MENU definition (MENUITEM SEPARATOR).
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
+class MenuSeparator : public MenuDefinition {
+public:
+ raw_ostream &log(raw_ostream &) const override;
+
+ MenuDefKind getKind() const override { return MkSeparator; }
+ static bool classof(const MenuDefinition *D) {
+ return D->getKind() == MkSeparator;
+ }
+};
+
+// MENUITEM statement definition.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
+class MenuItem : public MenuDefinition {
+public:
+ StringRef Name;
+ uint32_t Id;
+ uint16_t Flags;
+
+ MenuItem(StringRef Caption, uint32_t ItemId, uint16_t ItemFlags)
+ : Name(Caption), Id(ItemId), Flags(ItemFlags) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ uint16_t getResFlags() const override { return Flags; }
+ MenuDefKind getKind() const override { return MkMenuItem; }
+ static bool classof(const MenuDefinition *D) {
+ return D->getKind() == MkMenuItem;
+ }
+};
+
+// POPUP statement definition.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx
+class PopupItem : public MenuDefinition {
+public:
+ StringRef Name;
+ uint16_t Flags;
+ MenuDefinitionList SubItems;
+
+ PopupItem(StringRef Caption, uint16_t ItemFlags,
+ MenuDefinitionList &&SubItemsList)
+ : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ // This has an additional (0x10) flag. It doesn't match with documented
+ // 0x01 flag, though.
+ uint16_t getResFlags() const override { return Flags | 0x10; }
+ MenuDefKind getKind() const override { return MkPopup; }
+ static bool classof(const MenuDefinition *D) {
+ return D->getKind() == MkPopup;
+ }
+};
+
+// Menu resource definition.
+class MenuResource : public OptStatementsRCResource {
+public:
+ MenuDefinitionList Elements;
+
+ MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items,
+ uint16_t Flags)
+ : OptStatementsRCResource(std::move(OptStmts), Flags),
+ Elements(std::move(Items)) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ IntOrString getResourceType() const override { return RkMenu; }
+ Twine getResourceTypeName() const override { return "MENU"; }
+ Error visit(Visitor *V) const override { return V->visitMenuResource(this); }
+ ResourceKind getKind() const override { return RkMenu; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkMenu;
+ }
+};
+
+// STRINGTABLE resource. Contains a list of strings, each having its unique ID.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx
+class StringTableResource : public OptStatementsRCResource {
+public:
+ std::vector<std::pair<uint32_t, std::vector<StringRef>>> Table;
+
+ StringTableResource(OptionalStmtList &&List, uint16_t Flags)
+ : OptStatementsRCResource(std::move(List), Flags) {}
+ void addStrings(uint32_t ID, std::vector<StringRef> &&Strings) {
+ Table.emplace_back(ID, Strings);
+ }
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "STRINGTABLE"; }
+ Error visit(Visitor *V) const override {
+ return V->visitStringTableResource(this);
+ }
+};
+
+// -- DIALOG(EX) resource and its helper classes --
+//
+// This resource describes dialog boxes and controls residing inside them.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381003(v=vs.85).aspx
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx
+
+// Single control definition.
+class Control {
+public:
+ StringRef Type;
+ IntOrString Title;
+ uint32_t ID, X, Y, Width, Height;
+ Optional<IntWithNotMask> Style;
+ Optional<uint32_t> ExtStyle, HelpID;
+ IntOrString Class;
+
+ // Control classes as described in DLGITEMTEMPLATEEX documentation.
+ //
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms645389.aspx
+ enum CtlClasses {
+ ClsButton = 0x80,
+ ClsEdit = 0x81,
+ ClsStatic = 0x82,
+ ClsListBox = 0x83,
+ ClsScrollBar = 0x84,
+ ClsComboBox = 0x85
+ };
+
+ // Simple information about a single control type.
+ struct CtlInfo {
+ uint32_t Style;
+ uint16_t CtlClass;
+ bool HasTitle;
+ };
+
+ Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID,
+ uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight,
+ Optional<IntWithNotMask> ItemStyle, Optional<uint32_t> ExtItemStyle,
+ Optional<uint32_t> CtlHelpID, IntOrString CtlClass)
+ : Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY),
+ Width(ItemWidth), Height(ItemHeight), Style(ItemStyle),
+ ExtStyle(ExtItemStyle), HelpID(CtlHelpID), Class(CtlClass) {}
+
+ static const StringMap<CtlInfo> SupportedCtls;
+
+ raw_ostream &log(raw_ostream &) const;
+};
+
+// Single dialog definition. We don't create distinct classes for DIALOG and
+// DIALOGEX because of their being too similar to each other. We only have a
+// flag determining the type of the dialog box.
+class DialogResource : public OptStatementsRCResource {
+public:
+ uint32_t X, Y, Width, Height, HelpID;
+ std::vector<Control> Controls;
+ bool IsExtended;
+
+ DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth,
+ uint32_t DlgHeight, uint32_t DlgHelpID,
+ OptionalStmtList &&OptStmts, bool IsDialogEx, uint16_t Flags)
+ : OptStatementsRCResource(std::move(OptStmts), Flags), X(PosX), Y(PosY),
+ Width(DlgWidth), Height(DlgHeight), HelpID(DlgHelpID),
+ IsExtended(IsDialogEx) {}
+
+ void addControl(Control &&Ctl) { Controls.push_back(std::move(Ctl)); }
+
+ raw_ostream &log(raw_ostream &) const override;
+
+ // It was a weird design decision to assign the same resource type number
+ // both for DIALOG and DIALOGEX (and the same structure version number).
+ // It makes it possible for DIALOG to be mistaken for DIALOGEX.
+ IntOrString getResourceType() const override { return RkDialog; }
+ Twine getResourceTypeName() const override {
+ return "DIALOG" + Twine(IsExtended ? "EX" : "");
+ }
+ Error visit(Visitor *V) const override {
+ return V->visitDialogResource(this);
+ }
+ ResourceKind getKind() const override { return RkDialog; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkDialog;
+ }
+};
+
+// User-defined resource. It is either:
+// * a link to the file, e.g. NAME TYPE "filename",
+// * or contains a list of integers and strings, e.g. NAME TYPE {1, "a", 2}.
+class UserDefinedResource : public RCResource {
+public:
+ IntOrString Type;
+ StringRef FileLoc;
+ std::vector<IntOrString> Contents;
+ bool IsFileResource;
+
+ UserDefinedResource(IntOrString ResourceType, StringRef FileLocation,
+ uint16_t Flags)
+ : RCResource(Flags), Type(ResourceType), FileLoc(FileLocation),
+ IsFileResource(true) {}
+ UserDefinedResource(IntOrString ResourceType, std::vector<IntOrString> &&Data,
+ uint16_t Flags)
+ : RCResource(Flags), Type(ResourceType), Contents(std::move(Data)),
+ IsFileResource(false) {}
+
+ raw_ostream &log(raw_ostream &) const override;
+ IntOrString getResourceType() const override { return Type; }
+ Twine getResourceTypeName() const override { return Type; }
+ static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
+
+ Error visit(Visitor *V) const override {
+ return V->visitUserDefinedResource(this);
+ }
+ ResourceKind getKind() const override { return RkUser; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkUser;
+ }
+};
+
+// -- VERSIONINFO resource and its helper classes --
+//
+// This resource lists the version information on the executable/library.
+// The declaration consists of the following items:
+// * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS)
+// * BEGIN
+// * A number of BLOCK and/or VALUE statements. BLOCK recursively defines
+// another block of version information, whereas VALUE defines a
+// key -> value correspondence. There might be more than one value
+// corresponding to the single key.
+// * END
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx
+
+// A single VERSIONINFO statement;
+class VersionInfoStmt {
+public:
+ enum StmtKind { StBase = 0, StBlock = 1, StValue = 2 };
+
+ virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; }
+ virtual ~VersionInfoStmt() {}
+
+ virtual StmtKind getKind() const { return StBase; }
+ static bool classof(const VersionInfoStmt *S) {
+ return S->getKind() == StBase;
+ }
+};
+
+// BLOCK definition; also the main VERSIONINFO declaration is considered a
+// BLOCK, although it has no name.
+// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't
+// care about them at the parsing phase.
+class VersionInfoBlock : public VersionInfoStmt {
+public:
+ std::vector<std::unique_ptr<VersionInfoStmt>> Stmts;
+ StringRef Name;
+
+ VersionInfoBlock(StringRef BlockName) : Name(BlockName) {}
+ void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) {
+ Stmts.push_back(std::move(Stmt));
+ }
+ raw_ostream &log(raw_ostream &) const override;
+
+ StmtKind getKind() const override { return StBlock; }
+ static bool classof(const VersionInfoStmt *S) {
+ return S->getKind() == StBlock;
+ }
+};
+
+class VersionInfoValue : public VersionInfoStmt {
+public:
+ StringRef Key;
+ std::vector<IntOrString> Values;
+ BitVector HasPrecedingComma;
+
+ VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals,
+ BitVector &&CommasBeforeVals)
+ : Key(InfoKey), Values(std::move(Vals)),
+ HasPrecedingComma(std::move(CommasBeforeVals)) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ StmtKind getKind() const override { return StValue; }
+ static bool classof(const VersionInfoStmt *S) {
+ return S->getKind() == StValue;
+ }
+};
+
+class VersionInfoResource : public RCResource {
+public:
+ // A class listing fixed VERSIONINFO statements (occuring before main BEGIN).
+ // If any of these is not specified, it is assumed by the original tool to
+ // be equal to 0.
+ class VersionInfoFixed {
+ public:
+ enum VersionInfoFixedType {
+ FtUnknown,
+ FtFileVersion,
+ FtProductVersion,
+ FtFileFlagsMask,
+ FtFileFlags,
+ FtFileOS,
+ FtFileType,
+ FtFileSubtype,
+ FtNumTypes
+ };
+
+ private:
+ static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap;
+ static const StringRef FixedFieldsNames[FtNumTypes];
+
+ public:
+ SmallVector<uint32_t, 4> FixedInfo[FtNumTypes];
+ SmallVector<bool, FtNumTypes> IsTypePresent;
+
+ static VersionInfoFixedType getFixedType(StringRef Type);
+ static bool isTypeSupported(VersionInfoFixedType Type);
+ static bool isVersionType(VersionInfoFixedType Type);
+
+ VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {}
+
+ void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) {
+ FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end());
+ IsTypePresent[Type] = true;
+ }
+
+ raw_ostream &log(raw_ostream &) const;
+ };
+
+ VersionInfoBlock MainBlock;
+ VersionInfoFixed FixedData;
+
+ VersionInfoResource(VersionInfoBlock &&TopLevelBlock,
+ VersionInfoFixed &&FixedInfo, uint16_t Flags)
+ : RCResource(Flags), MainBlock(std::move(TopLevelBlock)),
+ FixedData(std::move(FixedInfo)) {}
+
+ raw_ostream &log(raw_ostream &) const override;
+ IntOrString getResourceType() const override { return RkVersionInfo; }
+ static uint16_t getDefaultMemoryFlags() { return MfMoveable | MfPure; }
+ Twine getResourceTypeName() const override { return "VERSIONINFO"; }
+ Error visit(Visitor *V) const override {
+ return V->visitVersionInfoResource(this);
+ }
+ ResourceKind getKind() const override { return RkVersionInfo; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkVersionInfo;
+ }
+};
+
+// CHARACTERISTICS optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx
+class CharacteristicsStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ CharacteristicsStmt(uint32_t Characteristic) : Value(Characteristic) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "CHARACTERISTICS"; }
+ Error visit(Visitor *V) const override {
+ return V->visitCharacteristicsStmt(this);
+ }
+};
+
+// VERSION optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx
+class VersionStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ VersionStmt(uint32_t Version) : Value(Version) {}
+ raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "VERSION"; }
+ Error visit(Visitor *V) const override { return V->visitVersionStmt(this); }
+};
+
+// CAPTION optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380778(v=vs.85).aspx
+class CaptionStmt : public OptionalStmt {
+public:
+ StringRef Value;
+
+ CaptionStmt(StringRef Caption) : Value(Caption) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "CAPTION"; }
+ Error visit(Visitor *V) const override { return V->visitCaptionStmt(this); }
+};
+
+// FONT optional statement.
+// Note that the documentation is inaccurate: it expects five arguments to be
+// given, however the example provides only two. In fact, the original tool
+// expects two arguments - point size and name of the typeface.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381013(v=vs.85).aspx
+class FontStmt : public OptionalStmt {
+public:
+ uint32_t Size, Weight, Charset;
+ StringRef Name;
+ bool Italic;
+
+ FontStmt(uint32_t FontSize, StringRef FontName, uint32_t FontWeight,
+ bool FontItalic, uint32_t FontCharset)
+ : Size(FontSize), Weight(FontWeight), Charset(FontCharset),
+ Name(FontName), Italic(FontItalic) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "FONT"; }
+ Error visit(Visitor *V) const override { return V->visitFontStmt(this); }
+};
+
+// STYLE optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381051(v=vs.85).aspx
+class StyleStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ StyleStmt(uint32_t Style) : Value(Style) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "STYLE"; }
+ Error visit(Visitor *V) const override { return V->visitStyleStmt(this); }
+};
+
+// EXSTYLE optional statement.
+//
+// Ref: docs.microsoft.com/en-us/windows/desktop/menurc/exstyle-statement
+class ExStyleStmt : public OptionalStmt {
+public:
+ uint32_t Value;
+
+ ExStyleStmt(uint32_t ExStyle) : Value(ExStyle) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "EXSTYLE"; }
+ Error visit(Visitor *V) const override { return V->visitExStyleStmt(this); }
+};
+
+// CLASS optional statement.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380883(v=vs.85).aspx
+class ClassStmt : public OptionalStmt {
+public:
+ IntOrString Value;
+
+ ClassStmt(IntOrString Class) : Value(Class) {}
+ raw_ostream &log(raw_ostream &) const override;
+ Twine getResourceTypeName() const override { return "CLASS"; }
+ Error visit(Visitor *V) const override { return V->visitClassStmt(this); }
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp
new file mode 100644
index 0000000000..a8f40abdf8
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.cpp
@@ -0,0 +1,367 @@
+//===-- ResourceScriptToken.cpp ---------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This file implements an interface defined in ResourceScriptToken.h.
+// In particular, it defines an .rc script tokenizer.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ResourceScriptToken.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstdlib>
+#include <utility>
+
+using namespace llvm;
+
+using Kind = RCToken::Kind;
+
+// Checks if Representation is a correct description of an RC integer.
+// It should be a 32-bit unsigned integer, either decimal, octal (0[0-7]+),
+// or hexadecimal (0x[0-9a-f]+). It might be followed by a single 'L'
+// character (that is the difference between our representation and
+// StringRef's one). If Representation is correct, 'true' is returned and
+// the return value is put back in Num.
+static bool rcGetAsInteger(StringRef Representation, uint32_t &Num) {
+ size_t Length = Representation.size();
+ if (Length == 0)
+ return false;
+ // Strip the last 'L' if unnecessary.
+ if (std::toupper(Representation.back()) == 'L')
+ Representation = Representation.drop_back(1);
+
+ return !Representation.getAsInteger<uint32_t>(0, Num);
+}
+
+RCToken::RCToken(RCToken::Kind RCTokenKind, StringRef Value)
+ : TokenKind(RCTokenKind), TokenValue(Value) {}
+
+uint32_t RCToken::intValue() const {
+ assert(TokenKind == Kind::Int);
+ // We assume that the token already is a correct integer (checked by
+ // rcGetAsInteger).
+ uint32_t Result;
+ bool IsSuccess = rcGetAsInteger(TokenValue, Result);
+ assert(IsSuccess);
+ (void)IsSuccess; // Silence the compiler warning when -DNDEBUG flag is on.
+ return Result;
+}
+
+bool RCToken::isLongInt() const {
+ return TokenKind == Kind::Int && std::toupper(TokenValue.back()) == 'L';
+}
+
+StringRef RCToken::value() const { return TokenValue; }
+
+Kind RCToken::kind() const { return TokenKind; }
+
+bool RCToken::isBinaryOp() const {
+ switch (TokenKind) {
+ case Kind::Plus:
+ case Kind::Minus:
+ case Kind::Pipe:
+ case Kind::Amp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static Error getStringError(const Twine &message) {
+ return make_error<StringError>("Error parsing file: " + message,
+ inconvertibleErrorCode());
+}
+
+namespace {
+
+class Tokenizer {
+public:
+ Tokenizer(StringRef Input) : Data(Input), DataLength(Input.size()), Pos(0) {}
+
+ Expected<std::vector<RCToken>> run();
+
+private:
+ // All 'advancing' methods return boolean values; if they're equal to false,
+ // the stream has ended or failed.
+ bool advance(size_t Amount = 1);
+ bool skipWhitespaces();
+
+ // Consumes a token. If any problem occurred, a non-empty Error is returned.
+ Error consumeToken(const Kind TokenKind);
+
+ // Check if tokenizer is about to read FollowingChars.
+ bool willNowRead(StringRef FollowingChars) const;
+
+ // Check if tokenizer can start reading an identifier at current position.
+ // The original tool did non specify the rules to determine what is a correct
+ // identifier. We assume they should follow the C convention:
+ // [a-zA-Z_][a-zA-Z0-9_]*.
+ bool canStartIdentifier() const;
+ // Check if tokenizer can continue reading an identifier.
+ bool canContinueIdentifier() const;
+
+ // Check if tokenizer can start reading an integer.
+ // A correct integer always starts with a 0-9 digit,
+ // can contain characters 0-9A-Fa-f (digits),
+ // Ll (marking the integer is 32-bit), Xx (marking the representation
+ // is hexadecimal). As some kind of separator should come after the
+ // integer, we can consume the integer until a non-alphanumeric
+ // character.
+ bool canStartInt() const;
+ bool canContinueInt() const;
+
+ bool canStartString() const;
+
+ // Check if tokenizer can start reading a single line comment (e.g. a comment
+ // that begins with '//')
+ bool canStartLineComment() const;
+
+ // Check if tokenizer can start or finish reading a block comment (e.g. a
+ // comment that begins with '/*' and ends with '*/')
+ bool canStartBlockComment() const;
+
+ // Throw away all remaining characters on the current line.
+ void skipCurrentLine();
+
+ bool streamEof() const;
+
+ // Classify the token that is about to be read from the current position.
+ Kind classifyCurrentToken() const;
+
+ // Process the Kind::Identifier token - check if it is
+ // an identifier describing a block start or end.
+ void processIdentifier(RCToken &token) const;
+
+ StringRef Data;
+ size_t DataLength, Pos;
+};
+
+void Tokenizer::skipCurrentLine() {
+ Pos = Data.find_first_of("\r\n", Pos);
+ Pos = Data.find_first_not_of("\r\n", Pos);
+
+ if (Pos == StringRef::npos)
+ Pos = DataLength;
+}
+
+Expected<std::vector<RCToken>> Tokenizer::run() {
+ Pos = 0;
+ std::vector<RCToken> Result;
+
+ // Consume an optional UTF-8 Byte Order Mark.
+ if (willNowRead("\xef\xbb\xbf"))
+ advance(3);
+
+ while (!streamEof()) {
+ if (!skipWhitespaces())
+ break;
+
+ Kind TokenKind = classifyCurrentToken();
+ if (TokenKind == Kind::Invalid)
+ return getStringError("Invalid token found at position " + Twine(Pos));
+
+ const size_t TokenStart = Pos;
+ if (Error TokenError = consumeToken(TokenKind))
+ return std::move(TokenError);
+
+ // Comments are just deleted, don't bother saving them.
+ if (TokenKind == Kind::LineComment || TokenKind == Kind::StartComment)
+ continue;
+
+ RCToken Token(TokenKind, Data.take_front(Pos).drop_front(TokenStart));
+ if (TokenKind == Kind::Identifier) {
+ processIdentifier(Token);
+ } else if (TokenKind == Kind::Int) {
+ uint32_t TokenInt;
+ if (!rcGetAsInteger(Token.value(), TokenInt)) {
+ // The integer has incorrect format or cannot be represented in
+ // a 32-bit integer.
+ return getStringError("Integer invalid or too large: " +
+ Token.value().str());
+ }
+ }
+
+ Result.push_back(Token);
+ }
+
+ return Result;
+}
+
+bool Tokenizer::advance(size_t Amount) {
+ Pos += Amount;
+ return !streamEof();
+}
+
+bool Tokenizer::skipWhitespaces() {
+ while (!streamEof() && isSpace(Data[Pos]))
+ advance();
+ return !streamEof();
+}
+
+Error Tokenizer::consumeToken(const Kind TokenKind) {
+ switch (TokenKind) {
+ // One-character token consumption.
+#define TOKEN(Name)
+#define SHORT_TOKEN(Name, Ch) case Kind::Name:
+#include "ResourceScriptTokenList.def"
+ advance();
+ return Error::success();
+
+ case Kind::LineComment:
+ advance(2);
+ skipCurrentLine();
+ return Error::success();
+
+ case Kind::StartComment: {
+ advance(2);
+ auto EndPos = Data.find("*/", Pos);
+ if (EndPos == StringRef::npos)
+ return getStringError(
+ "Unclosed multi-line comment beginning at position " + Twine(Pos));
+ advance(EndPos - Pos);
+ advance(2);
+ return Error::success();
+ }
+ case Kind::Identifier:
+ while (!streamEof() && canContinueIdentifier())
+ advance();
+ return Error::success();
+
+ case Kind::Int:
+ while (!streamEof() && canContinueInt())
+ advance();
+ return Error::success();
+
+ case Kind::String:
+ // Consume the preceding 'L', if there is any.
+ if (std::toupper(Data[Pos]) == 'L')
+ advance();
+ // Consume the double-quote.
+ advance();
+
+ // Consume the characters until the end of the file, line or string.
+ while (true) {
+ if (streamEof()) {
+ return getStringError("Unterminated string literal.");
+ } else if (Data[Pos] == '"') {
+ // Consume the ending double-quote.
+ advance();
+ // However, if another '"' follows this double-quote, the string didn't
+ // end and we just included '"' into the string.
+ if (!willNowRead("\""))
+ return Error::success();
+ } else if (Data[Pos] == '\n') {
+ return getStringError("String literal not terminated in the line.");
+ }
+
+ advance();
+ }
+
+ case Kind::Invalid:
+ assert(false && "Cannot consume an invalid token.");
+ }
+
+ llvm_unreachable("Unknown RCToken::Kind");
+}
+
+bool Tokenizer::willNowRead(StringRef FollowingChars) const {
+ return Data.drop_front(Pos).startswith(FollowingChars);
+}
+
+bool Tokenizer::canStartIdentifier() const {
+ assert(!streamEof());
+
+ const char CurChar = Data[Pos];
+ return std::isalpha(CurChar) || CurChar == '_' || CurChar == '.';
+}
+
+bool Tokenizer::canContinueIdentifier() const {
+ assert(!streamEof());
+ const char CurChar = Data[Pos];
+ return std::isalnum(CurChar) || CurChar == '_' || CurChar == '.' ||
+ CurChar == '/' || CurChar == '\\' || CurChar == '-';
+}
+
+bool Tokenizer::canStartInt() const {
+ assert(!streamEof());
+ return std::isdigit(Data[Pos]);
+}
+
+bool Tokenizer::canStartBlockComment() const {
+ assert(!streamEof());
+ return Data.drop_front(Pos).startswith("/*");
+}
+
+bool Tokenizer::canStartLineComment() const {
+ assert(!streamEof());
+ return Data.drop_front(Pos).startswith("//");
+}
+
+bool Tokenizer::canContinueInt() const {
+ assert(!streamEof());
+ return std::isalnum(Data[Pos]);
+}
+
+bool Tokenizer::canStartString() const {
+ return willNowRead("\"") || willNowRead("L\"") || willNowRead("l\"");
+}
+
+bool Tokenizer::streamEof() const { return Pos == DataLength; }
+
+Kind Tokenizer::classifyCurrentToken() const {
+ if (canStartBlockComment())
+ return Kind::StartComment;
+ if (canStartLineComment())
+ return Kind::LineComment;
+
+ if (canStartInt())
+ return Kind::Int;
+ if (canStartString())
+ return Kind::String;
+ // BEGIN and END are at this point of lexing recognized as identifiers.
+ if (canStartIdentifier())
+ return Kind::Identifier;
+
+ const char CurChar = Data[Pos];
+
+ switch (CurChar) {
+ // One-character token classification.
+#define TOKEN(Name)
+#define SHORT_TOKEN(Name, Ch) \
+ case Ch: \
+ return Kind::Name;
+#include "ResourceScriptTokenList.def"
+
+ default:
+ return Kind::Invalid;
+ }
+}
+
+void Tokenizer::processIdentifier(RCToken &Token) const {
+ assert(Token.kind() == Kind::Identifier);
+ StringRef Name = Token.value();
+
+ if (Name.equals_insensitive("begin"))
+ Token = RCToken(Kind::BlockBegin, Name);
+ else if (Name.equals_insensitive("end"))
+ Token = RCToken(Kind::BlockEnd, Name);
+}
+
+} // anonymous namespace
+
+namespace llvm {
+
+Expected<std::vector<RCToken>> tokenizeRC(StringRef Input) {
+ return Tokenizer(Input).run();
+}
+
+} // namespace llvm
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h
new file mode 100644
index 0000000000..cc8ca48b49
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptToken.h
@@ -0,0 +1,81 @@
+//===-- ResourceScriptToken.h -----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This declares the .rc script tokens and defines an interface for tokenizing
+// the input data. The list of available tokens is located at
+// ResourceScriptTokenList.def.
+//
+// Note that the tokenizer does not support preprocessor directives. The
+// preprocessor should do its work on the .rc file before running llvm-rc.
+//
+// As for now, it is possible to parse ASCII files only (the behavior on
+// UTF files might be undefined). However, it already consumes UTF-8 BOM, if
+// there is any. Thus, ASCII-compatible UTF-8 files are tokenized correctly.
+//
+// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H
+#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace llvm {
+
+// A definition of a single resource script token. Each token has its kind
+// (declared in ResourceScriptTokenList) and holds a value - a reference
+// representation of the token.
+// RCToken does not claim ownership on its value. A memory buffer containing
+// the token value should be stored in a safe place and cannot be freed
+// nor reallocated.
+class RCToken {
+public:
+ enum class Kind {
+#define TOKEN(Name) Name,
+#define SHORT_TOKEN(Name, Ch) Name,
+#include "ResourceScriptTokenList.def"
+ };
+
+ RCToken(RCToken::Kind RCTokenKind, StringRef Value);
+
+ // Get an integer value of the integer token.
+ uint32_t intValue() const;
+ bool isLongInt() const;
+
+ StringRef value() const;
+ Kind kind() const;
+
+ // Check if a token describes a binary operator.
+ bool isBinaryOp() const;
+
+private:
+ Kind TokenKind;
+ StringRef TokenValue;
+};
+
+// Tokenize Input.
+// In case no error occurred, the return value contains
+// tokens in order they were in the input file.
+// In case of any error, the return value contains
+// a textual representation of error.
+//
+// Tokens returned by this function hold only references to the parts
+// of the Input. Memory buffer containing Input cannot be freed,
+// modified or reallocated.
+Expected<std::vector<RCToken>> tokenizeRC(StringRef Input);
+
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def
new file mode 100644
index 0000000000..a61a96461f
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceScriptTokenList.def
@@ -0,0 +1,39 @@
+//===-- ResourceScriptTokenList.h -------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This is a part of llvm-rc tokenizer. It lists all the possible tokens
+// that might occur in a correct .rc script.
+//
+//===---------------------------------------------------------------------===//
+
+
+// Long tokens. They might consist of more than one character.
+TOKEN(Invalid) // Invalid token. Should not occur in a valid script.
+TOKEN(Int) // Integer (decimal, octal or hexadecimal).
+TOKEN(String) // String value.
+TOKEN(Identifier) // Script identifier (resource name or type).
+TOKEN(LineComment) // Beginning of single-line comment.
+TOKEN(StartComment) // Beginning of multi-line comment.
+
+// Short tokens. They usually consist of exactly one character.
+// The definitions are of the form SHORT_TOKEN(TokenName, TokenChar).
+// TokenChar is the one-character token representation occuring in the correct
+// .rc scripts.
+SHORT_TOKEN(BlockBegin, '{') // Start of the script block; can also be BEGIN.
+SHORT_TOKEN(BlockEnd, '}') // End of the block; can also be END.
+SHORT_TOKEN(Comma, ',') // Comma - resource arguments separator.
+SHORT_TOKEN(Plus, '+') // Addition operator.
+SHORT_TOKEN(Minus, '-') // Subtraction operator.
+SHORT_TOKEN(Pipe, '|') // Bitwise-OR operator.
+SHORT_TOKEN(Amp, '&') // Bitwise-AND operator.
+SHORT_TOKEN(Tilde, '~') // Bitwise-NOT operator.
+SHORT_TOKEN(LeftParen, '(') // Left parenthesis in the script expressions.
+SHORT_TOKEN(RightParen, ')') // Right parenthesis.
+
+#undef TOKEN
+#undef SHORT_TOKEN
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h b/contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h
new file mode 100644
index 0000000000..843c8d898a
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ResourceVisitor.h
@@ -0,0 +1,61 @@
+//===-- ResourceVisitor.h ---------------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This defines a base class visiting resource script resources.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
+#define LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
+
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace rc {
+
+class RCResource;
+
+class CaptionStmt;
+class ClassStmt;
+class CharacteristicsStmt;
+class ExStyleStmt;
+class FontStmt;
+class LanguageResource;
+class StyleStmt;
+class VersionStmt;
+
+class Visitor {
+public:
+ virtual Error visitNullResource(const RCResource *) = 0;
+ virtual Error visitAcceleratorsResource(const RCResource *) = 0;
+ virtual Error visitBitmapResource(const RCResource *) = 0;
+ virtual Error visitCursorResource(const RCResource *) = 0;
+ virtual Error visitDialogResource(const RCResource *) = 0;
+ virtual Error visitHTMLResource(const RCResource *) = 0;
+ virtual Error visitIconResource(const RCResource *) = 0;
+ virtual Error visitMenuResource(const RCResource *) = 0;
+ virtual Error visitStringTableResource(const RCResource *) = 0;
+ virtual Error visitUserDefinedResource(const RCResource *) = 0;
+ virtual Error visitVersionInfoResource(const RCResource *) = 0;
+
+ virtual Error visitCaptionStmt(const CaptionStmt *) = 0;
+ virtual Error visitClassStmt(const ClassStmt *) = 0;
+ virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0;
+ virtual Error visitExStyleStmt(const ExStyleStmt *) = 0;
+ virtual Error visitFontStmt(const FontStmt *) = 0;
+ virtual Error visitLanguageStmt(const LanguageResource *) = 0;
+ virtual Error visitStyleStmt(const StyleStmt *) = 0;
+ virtual Error visitVersionStmt(const VersionStmt *) = 0;
+
+ virtual ~Visitor() {}
+};
+
+} // namespace rc
+} // namespace llvm
+
+#endif
diff --git a/contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td b/contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td
new file mode 100644
index 0000000000..3c75c85ece
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/WindresOpts.td
@@ -0,0 +1,62 @@
+include "llvm/Option/OptParser.td"
+
+multiclass Long<string name, string help> {
+ def NAME: Separate<["--"], name>;
+ def NAME # _eq: Joined<["--"], name # "=">, Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
+}
+
+multiclass LongAlias<string name, Option orig> {
+ def NAME: Separate<["--"], name>, Alias<orig>;
+ def NAME # _eq: Joined<["--"], name # "=">, Alias<orig>;
+}
+
+multiclass LongShort<string short, string long, string help> {
+ def NAME: Separate<["--"], long>;
+ def NAME # _eq: Joined<["--"], long # "=">, Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
+ def NAME # _short: JoinedOrSeparate<["-"], short>, Alias<!cast<Separate>(NAME)>;
+}
+
+multiclass F<string short, string long, string help> {
+ def NAME: Flag<["-"], short>;
+ def NAME # _long: Flag<["--"], long>, Alias<!cast<Flag>(NAME)>,
+ HelpText<help>;
+}
+
+defm input : LongShort<"i", "input", "Input file">;
+
+defm output : LongShort<"o", "output", "Output file">;
+
+defm input_format : LongShort<"J", "input-format", "Input format">;
+
+defm output_format : LongShort<"O", "output-format", "Output format">;
+
+defm preprocessor : Long<"preprocessor", "Custom preprocessor command">;
+defm preprocessor_arg : Long<"preprocessor-arg", "Preprocessor command argument">;
+
+defm target : LongShort<"F", "target", "Target BFD format name">;
+
+defm include_dir : LongShort<"I", "include-dir", "Include directory">;
+defm include_alias : LongAlias<"include", include_dir>;
+
+defm define : LongShort<"D", "define", "Define to pass to the preprocessor">;
+
+defm undef : LongShort<"U", "undefine", "Undefine to pass to the preprocessor">;
+
+defm codepage : LongShort<"c", "codepage", "Default codepage to use">;
+
+defm language : LongShort<"l", "language", "Default language to use (0x0-0xffff)">;
+
+defm verbose : F<"v", "verbose", "Enable verbose output">;
+defm version : F<"V", "version", "Display version">;
+
+defm help : F<"h", "help", "Display this message and exit">;
+
+// Print (but do not run) the commands to run for preprocessing
+def _HASH_HASH_HASH : Flag<["-"], "###">;
+
+def no_preprocess : Flag<["--"], "no-preprocess">;
+
+// Unimplemented options for compatibility
+def use_temp_file: Flag<["--"], "use-temp-file">;
diff --git a/contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp b/contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp
new file mode 100644
index 0000000000..d43994deec
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/llvm-rc.cpp
@@ -0,0 +1,755 @@
+//===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Compile .rc scripts into .res files. This is intended to be a
+// platform-independent port of Microsoft's rc.exe tool.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ResourceFileWriter.h"
+#include "ResourceScriptCppFilter.h"
+#include "ResourceScriptParser.h"
+#include "ResourceScriptStmt.h"
+#include "ResourceScriptToken.h"
+
+#include "llvm/ADT/Triple.h"
+#include "llvm/Object/WindowsResource.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <system_error>
+
+using namespace llvm;
+using namespace llvm::rc;
+
+namespace {
+
+// Input options tables.
+
+enum ID {
+ OPT_INVALID = 0, // This is not a correct option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class RcOptTable : public opt::OptTable {
+public:
+ RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
+};
+
+enum Windres_ID {
+ WINDRES_INVALID = 0, // This is not a correct option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ WINDRES_##ID,
+#include "WindresOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const WINDRES_##NAME[] = VALUE;
+#include "WindresOpts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info WindresInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ WINDRES_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, WINDRES_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, WINDRES_##GROUP, \
+ WINDRES_##ALIAS, ALIASARGS, VALUES},
+#include "WindresOpts.inc"
+#undef OPTION
+};
+
+class WindresOptTable : public opt::OptTable {
+public:
+ WindresOptTable() : OptTable(WindresInfoTable, /* IgnoreCase = */ false) {}
+};
+
+static ExitOnError ExitOnErr;
+static FileRemover TempPreprocFile;
+static FileRemover TempResFile;
+
+[[noreturn]] static void fatalError(const Twine &Message) {
+ errs() << Message << "\n";
+ exit(1);
+}
+
+std::string createTempFile(const Twine &Prefix, StringRef Suffix) {
+ std::error_code EC;
+ SmallString<128> FileName;
+ if ((EC = sys::fs::createTemporaryFile(Prefix, Suffix, FileName)))
+ fatalError("Unable to create temp file: " + EC.message());
+ return static_cast<std::string>(FileName);
+}
+
+ErrorOr<std::string> findClang(const char *Argv0) {
+ StringRef Parent = llvm::sys::path::parent_path(Argv0);
+ ErrorOr<std::string> Path = std::error_code();
+ if (!Parent.empty()) {
+ // First look for the tool with all potential names in the specific
+ // directory of Argv0, if known
+ for (const auto *Name : {"clang", "clang-cl"}) {
+ Path = sys::findProgramByName(Name, Parent);
+ if (Path)
+ return Path;
+ }
+ }
+ // If no parent directory known, or not found there, look everywhere in PATH
+ for (const auto *Name : {"clang", "clang-cl"}) {
+ Path = sys::findProgramByName(Name);
+ if (Path)
+ return Path;
+ }
+ return Path;
+}
+
+bool isUsableArch(Triple::ArchType Arch) {
+ switch (Arch) {
+ case Triple::x86:
+ case Triple::x86_64:
+ case Triple::arm:
+ case Triple::thumb:
+ case Triple::aarch64:
+ // These work properly with the clang driver, setting the expected
+ // defines such as _WIN32 etc.
+ return true;
+ default:
+ // Other archs aren't set up for use with windows as target OS, (clang
+ // doesn't define e.g. _WIN32 etc), so with them we need to set a
+ // different default arch.
+ return false;
+ }
+}
+
+Triple::ArchType getDefaultFallbackArch() {
+ return Triple::x86_64;
+}
+
+std::string getClangClTriple() {
+ Triple T(sys::getDefaultTargetTriple());
+ if (!isUsableArch(T.getArch()))
+ T.setArch(getDefaultFallbackArch());
+ T.setOS(Triple::Win32);
+ T.setVendor(Triple::PC);
+ T.setEnvironment(Triple::MSVC);
+ T.setObjectFormat(Triple::COFF);
+ return T.str();
+}
+
+std::string getMingwTriple() {
+ Triple T(sys::getDefaultTargetTriple());
+ if (!isUsableArch(T.getArch()))
+ T.setArch(getDefaultFallbackArch());
+ if (T.isWindowsGNUEnvironment())
+ return T.str();
+ // Write out the literal form of the vendor/env here, instead of
+ // constructing them with enum values (which end up with them in
+ // normalized form). The literal form of the triple can matter for
+ // finding include files.
+ return (Twine(T.getArchName()) + "-w64-mingw32").str();
+}
+
+enum Format { Rc, Res, Coff, Unknown };
+
+struct RcOptions {
+ bool Preprocess = true;
+ bool PrintCmdAndExit = false;
+ std::string Triple;
+ std::vector<std::string> PreprocessCmd;
+ std::vector<std::string> PreprocessArgs;
+
+ std::string InputFile;
+ Format InputFormat = Rc;
+ std::string OutputFile;
+ Format OutputFormat = Res;
+
+ bool BeVerbose = false;
+ WriterParams Params;
+ bool AppendNull = false;
+ bool IsDryRun = false;
+ // Set the default language; choose en-US arbitrarily.
+ unsigned LangId = (/*PrimaryLangId*/ 0x09) | (/*SubLangId*/ 0x01 << 10);
+};
+
+bool preprocess(StringRef Src, StringRef Dst, const RcOptions &Opts,
+ const char *Argv0) {
+ std::string Clang;
+ if (Opts.PrintCmdAndExit) {
+ Clang = "clang";
+ } else {
+ ErrorOr<std::string> ClangOrErr = findClang(Argv0);
+ if (ClangOrErr) {
+ Clang = *ClangOrErr;
+ } else {
+ errs() << "llvm-rc: Unable to find clang, skipping preprocessing."
+ << "\n";
+ errs() << "Pass -no-cpp to disable preprocessing. This will be an error "
+ "in the future."
+ << "\n";
+ return false;
+ }
+ }
+
+ SmallVector<StringRef, 8> Args = {
+ Clang, "--driver-mode=gcc", "-target", Opts.Triple, "-E",
+ "-xc", "-DRC_INVOKED"};
+ if (!Opts.PreprocessCmd.empty()) {
+ Args.clear();
+ for (const auto &S : Opts.PreprocessCmd)
+ Args.push_back(S);
+ }
+ Args.push_back(Src);
+ Args.push_back("-o");
+ Args.push_back(Dst);
+ for (const auto &S : Opts.PreprocessArgs)
+ Args.push_back(S);
+ if (Opts.PrintCmdAndExit || Opts.BeVerbose) {
+ for (const auto &A : Args) {
+ outs() << " ";
+ sys::printArg(outs(), A, Opts.PrintCmdAndExit);
+ }
+ outs() << "\n";
+ if (Opts.PrintCmdAndExit)
+ exit(0);
+ }
+ // The llvm Support classes don't handle reading from stdout of a child
+ // process; otherwise we could avoid using a temp file.
+ int Res = sys::ExecuteAndWait(Clang, Args);
+ if (Res) {
+ fatalError("llvm-rc: Preprocessing failed.");
+ }
+ return true;
+}
+
+static std::pair<bool, std::string> isWindres(llvm::StringRef Argv0) {
+ StringRef ProgName = llvm::sys::path::stem(Argv0);
+ // x86_64-w64-mingw32-windres -> x86_64-w64-mingw32, windres
+ // llvm-rc -> "", llvm-rc
+ // aarch64-w64-mingw32-llvm-windres-10.exe -> aarch64-w64-mingw32, llvm-windres
+ ProgName = ProgName.rtrim("0123456789.-");
+ if (!ProgName.consume_back_insensitive("windres"))
+ return std::make_pair<bool, std::string>(false, "");
+ ProgName.consume_back_insensitive("llvm-");
+ ProgName.consume_back_insensitive("-");
+ return std::make_pair<bool, std::string>(true, ProgName.str());
+}
+
+Format parseFormat(StringRef S) {
+ Format F = StringSwitch<Format>(S.lower())
+ .Case("rc", Rc)
+ .Case("res", Res)
+ .Case("coff", Coff)
+ .Default(Unknown);
+ if (F == Unknown)
+ fatalError("Unable to parse '" + Twine(S) + "' as a format");
+ return F;
+}
+
+void deduceFormat(Format &Dest, StringRef File) {
+ Format F = StringSwitch<Format>(sys::path::extension(File.lower()))
+ .Case(".rc", Rc)
+ .Case(".res", Res)
+ .Case(".o", Coff)
+ .Case(".obj", Coff)
+ .Default(Unknown);
+ if (F != Unknown)
+ Dest = F;
+}
+
+std::string unescape(StringRef S) {
+ std::string Out;
+ Out.reserve(S.size());
+ for (int I = 0, E = S.size(); I < E; I++) {
+ if (S[I] == '\\') {
+ if (I + 1 < E)
+ Out.push_back(S[++I]);
+ else
+ fatalError("Unterminated escape");
+ continue;
+ }
+ Out.push_back(S[I]);
+ }
+ return Out;
+}
+
+std::vector<std::string> unescapeSplit(StringRef S) {
+ std::vector<std::string> OutArgs;
+ std::string Out;
+ bool InQuote = false;
+ for (int I = 0, E = S.size(); I < E; I++) {
+ if (S[I] == '\\') {
+ if (I + 1 < E)
+ Out.push_back(S[++I]);
+ else
+ fatalError("Unterminated escape");
+ continue;
+ }
+ if (S[I] == '"') {
+ InQuote = !InQuote;
+ continue;
+ }
+ if (S[I] == ' ' && !InQuote) {
+ OutArgs.push_back(Out);
+ Out.clear();
+ continue;
+ }
+ Out.push_back(S[I]);
+ }
+ if (InQuote)
+ fatalError("Unterminated quote");
+ if (!Out.empty())
+ OutArgs.push_back(Out);
+ return OutArgs;
+}
+
+RcOptions parseWindresOptions(ArrayRef<const char *> ArgsArr,
+ ArrayRef<const char *> InputArgsArray,
+ std::string Prefix) {
+ WindresOptTable T;
+ RcOptions Opts;
+ unsigned MAI, MAC;
+ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ // The tool prints nothing when invoked with no command-line arguments.
+ if (InputArgs.hasArg(WINDRES_help)) {
+ T.printHelp(outs(), "windres [options] file...",
+ "LLVM windres (GNU windres compatible)", false, true);
+ exit(0);
+ }
+
+ if (InputArgs.hasArg(WINDRES_version)) {
+ outs() << "llvm-windres, compatible with GNU windres\n";
+ cl::PrintVersionMessage();
+ exit(0);
+ }
+
+ std::vector<std::string> FileArgs = InputArgs.getAllArgValues(WINDRES_INPUT);
+ FileArgs.insert(FileArgs.end(), InputArgsArray.begin(), InputArgsArray.end());
+
+ if (InputArgs.hasArg(WINDRES_input)) {
+ Opts.InputFile = InputArgs.getLastArgValue(WINDRES_input).str();
+ } else if (!FileArgs.empty()) {
+ Opts.InputFile = FileArgs.front();
+ FileArgs.erase(FileArgs.begin());
+ } else {
+ // TODO: GNU windres takes input on stdin in this case.
+ fatalError("Missing input file");
+ }
+
+ if (InputArgs.hasArg(WINDRES_output)) {
+ Opts.OutputFile = InputArgs.getLastArgValue(WINDRES_output).str();
+ } else if (!FileArgs.empty()) {
+ Opts.OutputFile = FileArgs.front();
+ FileArgs.erase(FileArgs.begin());
+ } else {
+ // TODO: GNU windres writes output in rc form to stdout in this case.
+ fatalError("Missing output file");
+ }
+
+ if (InputArgs.hasArg(WINDRES_input_format)) {
+ Opts.InputFormat =
+ parseFormat(InputArgs.getLastArgValue(WINDRES_input_format));
+ } else {
+ deduceFormat(Opts.InputFormat, Opts.InputFile);
+ }
+ if (Opts.InputFormat == Coff)
+ fatalError("Unsupported input format");
+
+ if (InputArgs.hasArg(WINDRES_output_format)) {
+ Opts.OutputFormat =
+ parseFormat(InputArgs.getLastArgValue(WINDRES_output_format));
+ } else {
+ // The default in windres differs from the default in RcOptions
+ Opts.OutputFormat = Coff;
+ deduceFormat(Opts.OutputFormat, Opts.OutputFile);
+ }
+ if (Opts.OutputFormat == Rc)
+ fatalError("Unsupported output format");
+ if (Opts.InputFormat == Opts.OutputFormat) {
+ outs() << "Nothing to do.\n";
+ exit(0);
+ }
+
+ Opts.PrintCmdAndExit = InputArgs.hasArg(WINDRES__HASH_HASH_HASH);
+ Opts.Preprocess = !InputArgs.hasArg(WINDRES_no_preprocess);
+ Triple TT(Prefix);
+ if (InputArgs.hasArg(WINDRES_target)) {
+ StringRef Value = InputArgs.getLastArgValue(WINDRES_target);
+ if (Value == "pe-i386")
+ Opts.Triple = "i686-w64-mingw32";
+ else if (Value == "pe-x86-64")
+ Opts.Triple = "x86_64-w64-mingw32";
+ else
+ // Implicit extension; if the --target value isn't one of the known
+ // BFD targets, allow setting the full triple string via this instead.
+ Opts.Triple = Value.str();
+ } else if (TT.getArch() != Triple::UnknownArch)
+ Opts.Triple = Prefix;
+ else
+ Opts.Triple = getMingwTriple();
+
+ for (const auto *Arg :
+ InputArgs.filtered(WINDRES_include_dir, WINDRES_define, WINDRES_undef,
+ WINDRES_preprocessor_arg)) {
+ // GNU windres passes the arguments almost as-is on to popen() (it only
+ // backslash escapes spaces in the arguments), where a shell would
+ // unescape backslash escapes for quotes and similar. This means that
+ // when calling GNU windres, callers need to double escape chars like
+ // quotes, e.g. as -DSTRING=\\\"1.2.3\\\".
+ //
+ // Exactly how the arguments are interpreted depends on the platform
+ // though - but the cases where this matters (where callers would have
+ // done this double escaping) probably is confined to cases like these
+ // quoted string defines, and those happen to work the same across unix
+ // and windows.
+ std::string Unescaped = unescape(Arg->getValue());
+ switch (Arg->getOption().getID()) {
+ case WINDRES_include_dir:
+ // Technically, these are handled the same way as e.g. defines, but
+ // the way we consistently unescape the unix way breaks windows paths
+ // with single backslashes. Alternatively, our unescape function would
+ // need to mimic the platform specific command line parsing/unescaping
+ // logic.
+ Opts.Params.Include.push_back(Arg->getValue());
+ Opts.PreprocessArgs.push_back("-I");
+ Opts.PreprocessArgs.push_back(Arg->getValue());
+ break;
+ case WINDRES_define:
+ Opts.PreprocessArgs.push_back("-D");
+ Opts.PreprocessArgs.push_back(Unescaped);
+ break;
+ case WINDRES_undef:
+ Opts.PreprocessArgs.push_back("-U");
+ Opts.PreprocessArgs.push_back(Unescaped);
+ break;
+ case WINDRES_preprocessor_arg:
+ Opts.PreprocessArgs.push_back(Unescaped);
+ break;
+ }
+ }
+ if (InputArgs.hasArg(WINDRES_preprocessor))
+ Opts.PreprocessCmd =
+ unescapeSplit(InputArgs.getLastArgValue(WINDRES_preprocessor));
+
+ Opts.Params.CodePage = CpWin1252; // Different default
+ if (InputArgs.hasArg(WINDRES_codepage)) {
+ if (InputArgs.getLastArgValue(WINDRES_codepage)
+ .getAsInteger(0, Opts.Params.CodePage))
+ fatalError("Invalid code page: " +
+ InputArgs.getLastArgValue(WINDRES_codepage));
+ }
+ if (InputArgs.hasArg(WINDRES_language)) {
+ StringRef Val = InputArgs.getLastArgValue(WINDRES_language);
+ Val.consume_front_insensitive("0x");
+ if (Val.getAsInteger(16, Opts.LangId))
+ fatalError("Invalid language id: " +
+ InputArgs.getLastArgValue(WINDRES_language));
+ }
+
+ Opts.BeVerbose = InputArgs.hasArg(WINDRES_verbose);
+
+ return Opts;
+}
+
+RcOptions parseRcOptions(ArrayRef<const char *> ArgsArr,
+ ArrayRef<const char *> InputArgsArray) {
+ RcOptTable T;
+ RcOptions Opts;
+ unsigned MAI, MAC;
+ opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ // The tool prints nothing when invoked with no command-line arguments.
+ if (InputArgs.hasArg(OPT_help)) {
+ T.printHelp(outs(), "rc [options] file...", "Resource Converter", false);
+ exit(0);
+ }
+
+ std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
+ InArgsInfo.insert(InArgsInfo.end(), InputArgsArray.begin(),
+ InputArgsArray.end());
+ if (InArgsInfo.size() != 1) {
+ fatalError("Exactly one input file should be provided.");
+ }
+
+ Opts.PrintCmdAndExit = InputArgs.hasArg(OPT__HASH_HASH_HASH);
+ Opts.Triple = getClangClTriple();
+ for (const auto *Arg :
+ InputArgs.filtered(OPT_includepath, OPT_define, OPT_undef)) {
+ switch (Arg->getOption().getID()) {
+ case OPT_includepath:
+ Opts.PreprocessArgs.push_back("-I");
+ break;
+ case OPT_define:
+ Opts.PreprocessArgs.push_back("-D");
+ break;
+ case OPT_undef:
+ Opts.PreprocessArgs.push_back("-U");
+ break;
+ }
+ Opts.PreprocessArgs.push_back(Arg->getValue());
+ }
+
+ Opts.InputFile = InArgsInfo[0];
+ Opts.BeVerbose = InputArgs.hasArg(OPT_verbose);
+ Opts.Preprocess = !InputArgs.hasArg(OPT_no_preprocess);
+ Opts.Params.Include = InputArgs.getAllArgValues(OPT_includepath);
+ Opts.Params.NoInclude = InputArgs.hasArg(OPT_noinclude);
+ if (Opts.Params.NoInclude) {
+ // Clear the INLCUDE variable for the external preprocessor
+#ifdef _WIN32
+ ::_putenv("INCLUDE=");
+#else
+ ::unsetenv("INCLUDE");
+#endif
+ }
+ if (InputArgs.hasArg(OPT_codepage)) {
+ if (InputArgs.getLastArgValue(OPT_codepage)
+ .getAsInteger(10, Opts.Params.CodePage))
+ fatalError("Invalid code page: " +
+ InputArgs.getLastArgValue(OPT_codepage));
+ }
+ Opts.IsDryRun = InputArgs.hasArg(OPT_dry_run);
+ auto OutArgsInfo = InputArgs.getAllArgValues(OPT_fileout);
+ if (OutArgsInfo.empty()) {
+ SmallString<128> OutputFile(Opts.InputFile);
+ llvm::sys::fs::make_absolute(OutputFile);
+ llvm::sys::path::replace_extension(OutputFile, "res");
+ OutArgsInfo.push_back(std::string(OutputFile.str()));
+ }
+ if (!Opts.IsDryRun) {
+ if (OutArgsInfo.size() != 1)
+ fatalError(
+ "No more than one output file should be provided (using /FO flag).");
+ Opts.OutputFile = OutArgsInfo[0];
+ }
+ Opts.AppendNull = InputArgs.hasArg(OPT_add_null);
+ if (InputArgs.hasArg(OPT_lang_id)) {
+ StringRef Val = InputArgs.getLastArgValue(OPT_lang_id);
+ Val.consume_front_insensitive("0x");
+ if (Val.getAsInteger(16, Opts.LangId))
+ fatalError("Invalid language id: " +
+ InputArgs.getLastArgValue(OPT_lang_id));
+ }
+ return Opts;
+}
+
+RcOptions getOptions(const char *Argv0, ArrayRef<const char *> ArgsArr,
+ ArrayRef<const char *> InputArgs) {
+ std::string Prefix;
+ bool IsWindres;
+ std::tie(IsWindres, Prefix) = isWindres(Argv0);
+ if (IsWindres)
+ return parseWindresOptions(ArgsArr, InputArgs, Prefix);
+ else
+ return parseRcOptions(ArgsArr, InputArgs);
+}
+
+void doRc(std::string Src, std::string Dest, RcOptions &Opts,
+ const char *Argv0) {
+ std::string PreprocessedFile = Src;
+ if (Opts.Preprocess) {
+ std::string OutFile = createTempFile("preproc", "rc");
+ TempPreprocFile.setFile(OutFile);
+ if (preprocess(Src, OutFile, Opts, Argv0))
+ PreprocessedFile = OutFile;
+ }
+
+ // Read and tokenize the input file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> File =
+ MemoryBuffer::getFile(PreprocessedFile);
+ if (!File) {
+ fatalError("Error opening file '" + Twine(PreprocessedFile) +
+ "': " + File.getError().message());
+ }
+
+ std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
+ StringRef Contents = FileContents->getBuffer();
+
+ std::string FilteredContents = filterCppOutput(Contents);
+ std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents));
+
+ if (Opts.BeVerbose) {
+ const Twine TokenNames[] = {
+#define TOKEN(Name) #Name,
+#define SHORT_TOKEN(Name, Ch) #Name,
+#include "ResourceScriptTokenList.def"
+ };
+
+ for (const RCToken &Token : Tokens) {
+ outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
+ << Token.value();
+ if (Token.kind() == RCToken::Kind::Int)
+ outs() << "; int value = " << Token.intValue();
+
+ outs() << "\n";
+ }
+ }
+
+ WriterParams &Params = Opts.Params;
+ SmallString<128> InputFile(Src);
+ llvm::sys::fs::make_absolute(InputFile);
+ Params.InputFilePath = InputFile;
+
+ switch (Params.CodePage) {
+ case CpAcp:
+ case CpWin1252:
+ case CpUtf8:
+ break;
+ default:
+ fatalError("Unsupported code page, only 0, 1252 and 65001 are supported!");
+ }
+
+ std::unique_ptr<ResourceFileWriter> Visitor;
+
+ if (!Opts.IsDryRun) {
+ std::error_code EC;
+ auto FOut = std::make_unique<raw_fd_ostream>(
+ Dest, EC, sys::fs::FA_Read | sys::fs::FA_Write);
+ if (EC)
+ fatalError("Error opening output file '" + Dest + "': " + EC.message());
+ Visitor = std::make_unique<ResourceFileWriter>(Params, std::move(FOut));
+ Visitor->AppendNull = Opts.AppendNull;
+
+ ExitOnErr(NullResource().visit(Visitor.get()));
+
+ unsigned PrimaryLangId = Opts.LangId & 0x3ff;
+ unsigned SubLangId = Opts.LangId >> 10;
+ ExitOnErr(LanguageResource(PrimaryLangId, SubLangId).visit(Visitor.get()));
+ }
+
+ rc::RCParser Parser{std::move(Tokens)};
+ while (!Parser.isEof()) {
+ auto Resource = ExitOnErr(Parser.parseSingleResource());
+ if (Opts.BeVerbose)
+ Resource->log(outs());
+ if (!Opts.IsDryRun)
+ ExitOnErr(Resource->visit(Visitor.get()));
+ }
+
+ // STRINGTABLE resources come at the very end.
+ if (!Opts.IsDryRun)
+ ExitOnErr(Visitor->dumpAllStringTables());
+}
+
+void doCvtres(std::string Src, std::string Dest, std::string TargetTriple) {
+ object::WindowsResourceParser Parser;
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFile(Src);
+ if (!BufferOrErr)
+ fatalError("Error opening file '" + Twine(Src) +
+ "': " + BufferOrErr.getError().message());
+ std::unique_ptr<MemoryBuffer> &Buffer = BufferOrErr.get();
+ std::unique_ptr<object::WindowsResource> Binary =
+ ExitOnErr(object::WindowsResource::createWindowsResource(
+ Buffer->getMemBufferRef()));
+
+ std::vector<std::string> Duplicates;
+ ExitOnErr(Parser.parse(Binary.get(), Duplicates));
+ for (const auto &DupeDiag : Duplicates)
+ fatalError("Duplicate resources: " + DupeDiag);
+
+ Triple T(TargetTriple);
+ COFF::MachineTypes MachineType;
+ switch (T.getArch()) {
+ case Triple::x86:
+ MachineType = COFF::IMAGE_FILE_MACHINE_I386;
+ break;
+ case Triple::x86_64:
+ MachineType = COFF::IMAGE_FILE_MACHINE_AMD64;
+ break;
+ case Triple::arm:
+ case Triple::thumb:
+ MachineType = COFF::IMAGE_FILE_MACHINE_ARMNT;
+ break;
+ case Triple::aarch64:
+ MachineType = COFF::IMAGE_FILE_MACHINE_ARM64;
+ break;
+ default:
+ fatalError("Unsupported architecture in target '" + Twine(TargetTriple) +
+ "'");
+ }
+
+ std::unique_ptr<MemoryBuffer> OutputBuffer =
+ ExitOnErr(object::writeWindowsResourceCOFF(MachineType, Parser,
+ /*DateTimeStamp*/ 0));
+ std::unique_ptr<FileOutputBuffer> FileBuffer =
+ ExitOnErr(FileOutputBuffer::create(Dest, OutputBuffer->getBufferSize()));
+ std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
+ FileBuffer->getBufferStart());
+ ExitOnErr(FileBuffer->commit());
+}
+
+} // anonymous namespace
+
+int main(int Argc, const char **Argv) {
+ InitLLVM X(Argc, Argv);
+ ExitOnErr.setBanner("llvm-rc: ");
+
+ const char **DashDash = std::find_if(
+ Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; });
+ ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash);
+ ArrayRef<const char *> FileArgsArr;
+ if (DashDash != Argv + Argc)
+ FileArgsArr = makeArrayRef(DashDash + 1, Argv + Argc);
+
+ RcOptions Opts = getOptions(Argv[0], ArgsArr, FileArgsArr);
+
+ std::string ResFile = Opts.OutputFile;
+ if (Opts.InputFormat == Rc) {
+ if (Opts.OutputFormat == Coff) {
+ ResFile = createTempFile("rc", "res");
+ TempResFile.setFile(ResFile);
+ }
+ doRc(Opts.InputFile, ResFile, Opts, Argv[0]);
+ } else {
+ ResFile = Opts.InputFile;
+ }
+ if (Opts.OutputFormat == Coff) {
+ doCvtres(ResFile, Opts.OutputFile, Opts.Triple);
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/llvm14/tools/llvm-rc/ya.make b/contrib/libs/llvm14/tools/llvm-rc/ya.make
new file mode 100644
index 0000000000..6b75c5a0c1
--- /dev/null
+++ b/contrib/libs/llvm14/tools/llvm-rc/ya.make
@@ -0,0 +1,44 @@
+# Generated by devtools/yamaker.
+
+PROGRAM()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/llvm14
+ contrib/libs/llvm14/include
+ contrib/libs/llvm14/lib/BinaryFormat
+ contrib/libs/llvm14/lib/Bitcode/Reader
+ contrib/libs/llvm14/lib/Bitstream/Reader
+ contrib/libs/llvm14/lib/Demangle
+ contrib/libs/llvm14/lib/IR
+ contrib/libs/llvm14/lib/MC
+ contrib/libs/llvm14/lib/MC/MCParser
+ contrib/libs/llvm14/lib/Object
+ contrib/libs/llvm14/lib/Option
+ contrib/libs/llvm14/lib/Remarks
+ contrib/libs/llvm14/lib/Support
+ contrib/libs/llvm14/lib/TextAPI
+)
+
+ADDINCL(
+ ${ARCADIA_BUILD_ROOT}/contrib/libs/llvm14/tools/llvm-rc
+ contrib/libs/llvm14/tools/llvm-rc
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ ResourceFileWriter.cpp
+ ResourceScriptCppFilter.cpp
+ ResourceScriptParser.cpp
+ ResourceScriptStmt.cpp
+ ResourceScriptToken.cpp
+ llvm-rc.cpp
+)
+
+END()