Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-alt.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-alt.rc @@ -0,0 +1,4 @@ +2 ACCELERATORS { + "A", 15, ASCII, ALT +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-control.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-control.rc @@ -0,0 +1,4 @@ +2 ACCELERATORS { + "A", 15, ASCII, CONTROL +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-shift.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-shift.rc @@ -0,0 +1,4 @@ +2 ACCELERATORS { + "A", 15, ASCII, SHIFT +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-virtkey.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-ascii-virtkey.rc @@ -0,0 +1,4 @@ +2 ACCELERATORS { + "A", 15, ASCII, VIRTKEY +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-bad-id.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-bad-id.rc @@ -0,0 +1,4 @@ +1 ACCELERATORS { + "A", 1234567, VIRTKEY +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-bad-key-id.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-bad-key-id.rc @@ -0,0 +1,3 @@ +9 ACCELERATORS { + 1234567, 0, VIRTKEY +} Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-control-nonalpha.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-control-nonalpha.rc @@ -0,0 +1,4 @@ +100 ACCELERATORS { + "^5", 1, ASCII +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-long-virtkey.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-long-virtkey.rc @@ -0,0 +1,4 @@ +100 ACCELERATORS { + "^X", 10, VIRTKEY +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-no-caret.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-no-caret.rc @@ -0,0 +1,4 @@ +50 ACCELERATORS { + "XY", 1, ASCII +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-no-type.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-no-type.rc @@ -0,0 +1,4 @@ +5 ACCELERATORS { + 10, 42 +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-only-caret.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-only-caret.rc @@ -0,0 +1,4 @@ +555 ACCELERATORS { + "^", 100 +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-too-long.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-too-long.rc @@ -0,0 +1,4 @@ +12 ACCELERATORS { + "Hello", 5, ASCII +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-too-short.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-too-short.rc @@ -0,0 +1,4 @@ +10 ACCELERATORS { + "", 12, VIRTKEY +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators-virtual-nonalpha.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators-virtual-nonalpha.rc @@ -0,0 +1,4 @@ +42 ACCELERATORS { + "]", 1, VIRTKEY +} + Index: llvm/test/tools/llvm-rc/Inputs/tag-accelerators.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-accelerators.rc @@ -0,0 +1,114 @@ +1 ACCELERATORS +VERSION 700 +LANGUAGE 5, 1 +{ + "a", 3 + "a", 4, ASCII + "a", 5, VIRTKEY + "A", 6 + "A", 7, ASCII + "A", 8, VIRTKEY + "1", 9 + "1", 10, ASCII + "1", 11, VIRTKEY + "$", 12 + "$", 13, ASCII + "]", 15 + "]", 16, ASCII + "^a", 18 + "^a", 19, ASCII + 0, 37, ASCII + 0, 38, VIRTKEY + 1, 40, ASCII + 1, 41, VIRTKEY + 127, 43, ASCII + 127, 44, VIRTKEY + 128, 46, ASCII + 128, 47, VIRTKEY + 255, 49, ASCII + 255, 50, VIRTKEY + 256, 52, ASCII + 256, 53, VIRTKEY + "^A", 66 + "^A", 67, ASCII + 54321, 70, ASCII + 54321, 71, VIRTKEY +} + +2 ACCELERATORS { + 42, 0, ASCII + 42, 1, VIRTKEY + 42, 2, ASCII, NOINVERT + 42, 3, VIRTKEY, NOINVERT + 42, 4, VIRTKEY, CONTROL + 42, 5, VIRTKEY, SHIFT + 42, 6, VIRTKEY, ALT + 42, 7, VIRTKEY, NOINVERT, CONTROL + 42, 8, VIRTKEY, NOINVERT, SHIFT + 42, 9, VIRTKEY, NOINVERT, ALT + 42, 10, VIRTKEY, CONTROL, SHIFT + 42, 11, VIRTKEY, CONTROL, ALT + 42, 12, VIRTKEY, SHIFT, ALT + 42, 13, VIRTKEY, NOINVERT, CONTROL, SHIFT + 42, 14, VIRTKEY, NOINVERT, CONTROL, ALT + 42, 15, VIRTKEY, NOINVERT, SHIFT, ALT + 42, 16, VIRTKEY, CONTROL, SHIFT, ALT + 42, 17, VIRTKEY, NOINVERT, CONTROL, SHIFT, ALT + "f", 18, ASCII + "f", 19, VIRTKEY + "f", 20, ASCII, NOINVERT + "f", 21, VIRTKEY, NOINVERT + "f", 22, VIRTKEY, CONTROL + "f", 23, VIRTKEY, SHIFT + "f", 24, VIRTKEY, ALT + "f", 25, VIRTKEY, NOINVERT, CONTROL + "f", 26, VIRTKEY, NOINVERT, SHIFT + "f", 27, VIRTKEY, NOINVERT, ALT + "f", 28, VIRTKEY, CONTROL, SHIFT + "f", 29, VIRTKEY, CONTROL, ALT + "f", 30, VIRTKEY, SHIFT, ALT + "f", 31, VIRTKEY, NOINVERT, CONTROL, SHIFT + "f", 32, VIRTKEY, NOINVERT, CONTROL, ALT + "f", 33, VIRTKEY, NOINVERT, SHIFT, ALT + "f", 34, VIRTKEY, CONTROL, SHIFT, ALT + "f", 35, VIRTKEY, NOINVERT, CONTROL, SHIFT, ALT + "U", 36, ASCII + "U", 37, VIRTKEY + "U", 38, ASCII, NOINVERT + "U", 39, VIRTKEY, NOINVERT + "U", 40, VIRTKEY, CONTROL + "U", 41, VIRTKEY, SHIFT + "U", 42, VIRTKEY, ALT + "U", 43, VIRTKEY, NOINVERT, CONTROL + "U", 44, VIRTKEY, NOINVERT, SHIFT + "U", 45, VIRTKEY, NOINVERT, ALT + "U", 46, VIRTKEY, CONTROL, SHIFT + "U", 47, VIRTKEY, CONTROL, ALT + "U", 48, VIRTKEY, SHIFT, ALT + "U", 49, VIRTKEY, NOINVERT, CONTROL, SHIFT + "U", 50, VIRTKEY, NOINVERT, CONTROL, ALT + "U", 51, VIRTKEY, NOINVERT, SHIFT, ALT + "U", 52, VIRTKEY, CONTROL, SHIFT, ALT + "U", 53, VIRTKEY, NOINVERT, CONTROL, SHIFT, ALT + "7", 54, ASCII + "7", 55, VIRTKEY + "7", 56, ASCII, NOINVERT + "7", 57, VIRTKEY, NOINVERT + "7", 58, VIRTKEY, CONTROL + "7", 59, VIRTKEY, SHIFT + "7", 60, VIRTKEY, ALT + "7", 61, VIRTKEY, NOINVERT, CONTROL + "7", 62, VIRTKEY, NOINVERT, SHIFT + "7", 63, VIRTKEY, NOINVERT, ALT + "7", 64, VIRTKEY, CONTROL, SHIFT + "7", 65, VIRTKEY, CONTROL, ALT + "7", 66, VIRTKEY, SHIFT, ALT + "7", 67, VIRTKEY, NOINVERT, CONTROL, SHIFT + "7", 68, VIRTKEY, NOINVERT, CONTROL, ALT + "7", 69, VIRTKEY, NOINVERT, SHIFT, ALT + "7", 70, VIRTKEY, CONTROL, SHIFT, ALT + "7", 71, VIRTKEY, NOINVERT, CONTROL, SHIFT, ALT + "^j", 72, ASCII + "^j", 73, ASCII, NOINVERT +} + Index: llvm/test/tools/llvm-rc/tag-accelerators.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/tag-accelerators.test @@ -0,0 +1,80 @@ +; RUN: llvm-rc /FO %t %p/Inputs/tag-accelerators.rc +; RUN: diff %t %p/Inputs/tag-accelerators.res + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-bad-id.rc 2>&1 | FileCheck %s --check-prefix BADID + +; BADID: llvm-rc: Error in ACCELERATORS statement (ID 1): +; BADID-NEXT: ACCELERATORS entry ID (1234567) does not fit in 16 bits. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-ascii-virtkey.rc 2>&1 | FileCheck %s --check-prefix ASCII1 + +; ASCII1: llvm-rc: Error in ACCELERATORS statement (ID 2): +; ASCII1-NEXT: Accelerator ID 15: Accelerator can't be both ASCII and VIRTKEY + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-ascii-control.rc 2>&1 | FileCheck %s --check-prefix ASCII2 + +; ASCII2: llvm-rc: Error in ACCELERATORS statement (ID 2): +; ASCII2-NEXT: Accelerator ID 15: Can only apply ALT, SHIFT or CONTROL to VIRTKEY accelerators + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-ascii-shift.rc 2>&1 | FileCheck %s --check-prefix ASCII3 + +; ASCII3: llvm-rc: Error in ACCELERATORS statement (ID 2): +; ASCII3-NEXT: Accelerator ID 15: Can only apply ALT, SHIFT or CONTROL to VIRTKEY accelerators + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-ascii-alt.rc 2>&1 | FileCheck %s --check-prefix ASCII4 + +; ASCII4: llvm-rc: Error in ACCELERATORS statement (ID 2): +; ASCII4-NEXT: Accelerator ID 15: Can only apply ALT, SHIFT or CONTROL to VIRTKEY accelerators + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-bad-key-id.rc 2>&1 | FileCheck %s --check-prefix BADKEYID + +; BADKEYID: llvm-rc: Error in ACCELERATORS statement (ID 9): +; BADKEYID-NEXT: Numeric event key ID (1234567) does not fit in 16 bits. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-too-short.rc 2>&1 | FileCheck %s --check-prefix LENGTH1 + +; LENGTH1: llvm-rc: Error in ACCELERATORS statement (ID 10): +; LENGTH1-NEXT: Accelerator ID 12: Accelerator string events should have length 1 or 2 + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-too-long.rc 2>&1 | FileCheck %s --check-prefix LENGTH2 + +; LENGTH2: llvm-rc: Error in ACCELERATORS statement (ID 12): +; LENGTH2-NEXT: Accelerator ID 5: Accelerator string events should have length 1 or 2 + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-only-caret.rc 2>&1 | FileCheck %s --check-prefix CARET1 + +; CARET1: llvm-rc: Error in ACCELERATORS statement (ID 555): +; CARET1-NEXT: Accelerator ID 100: No character following '^' in accelerator event + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-no-caret.rc 2>&1 | FileCheck %s --check-prefix CARET2 + +; CARET2: llvm-rc: Error in ACCELERATORS statement (ID 50): +; CARET2-NEXT: Accelerator ID 1: Event string should be one-character, possibly preceded by '^' + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-long-virtkey.rc 2>&1 | FileCheck %s --check-prefix CARET3 + +; CARET3: llvm-rc: Error in ACCELERATORS statement (ID 100): +; CARET3-NEXT: Accelerator ID 10: VIRTKEY accelerator events can't be preceded by '^' + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-control-nonalpha.rc 2>&1 | FileCheck %s --check-prefix NONALPHA1 + +; NONALPHA1: llvm-rc: Error in ACCELERATORS statement (ID 100): +; NONALPHA1-NEXT: Accelerator ID 1: Control character accelerator event should be alphabetic + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-accelerators-virtual-nonalpha.rc 2>&1 | FileCheck %s --check-prefix NONALPHA2 + +; NONALPHA2: llvm-rc: Error in ACCELERATORS statement (ID 42): +; NONALPHA2-NEXT: Accelerator ID 1: Non-alphanumeric characters cannot describe virtual keys Index: llvm/tools/llvm-rc/ResourceScriptStmt.h =================================================================== --- llvm/tools/llvm-rc/ResourceScriptStmt.h +++ llvm/tools/llvm-rc/ResourceScriptStmt.h @@ -105,8 +105,6 @@ public: IntOrString ResName; - RCResource() = default; - RCResource(RCResource &&) = default; void setName(const IntOrString &Name) { ResName = Name; } virtual raw_ostream &log(raw_ostream &OS) const { return OS << "Base statement\n"; @@ -117,6 +115,10 @@ 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. virtual uint16_t getMemoryFlags() const { return MfDiscardable | MfPure | MfMoveable; @@ -153,11 +155,20 @@ public: OptionalStmtList() {} - virtual raw_ostream &log(raw_ostream &OS) const; + OptionalStmtList(OptionalStmtList &&Other) + : Statements(std::move(Other.Statements)) {} + raw_ostream &log(raw_ostream &OS) const override; void addStmt(std::unique_ptr 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 { @@ -166,6 +177,8 @@ OptStatementsRCResource(OptionalStmtList &&Stmts) : OptStatements(make_unique(std::move(Stmts))) {} + + virtual Error applyStmts(Visitor *V) const { return OptStatements->visit(V); } }; // LANGUAGE statement. It can occur both as a top-level statement (in such @@ -218,14 +231,27 @@ static uint32_t OptionsFlags[NumFlags]; }; + std::vector Accelerators; + using OptStatementsRCResource::OptStatementsRCResource; void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) { Accelerators.push_back(Accelerator{Event, Id, Flags}); } raw_ostream &log(raw_ostream &) const override; -private: - std::vector Accelerators; + IntOrString getResourceType() const override { return RkAccelerators; } + uint16_t getMemoryFlags() const override { + 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; + } }; // CURSOR resource. Represents a single cursor (".cur") file. @@ -532,22 +558,30 @@ // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx class CharacteristicsStmt : public OptionalStmt { +public: uint32_t Value; -public: 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; -public: 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. Index: llvm/tools/llvm-rc/ResourceSerializator.h =================================================================== --- llvm/tools/llvm-rc/ResourceSerializator.h +++ llvm/tools/llvm-rc/ResourceSerializator.h @@ -28,14 +28,19 @@ } Error visitNullResource(const RCResource *) override; + Error visitAcceleratorsResource(const RCResource *) override; Error visitHTMLResource(const RCResource *) override; + Error visitCharacteristicsStmt(const CharacteristicsStmt *) override; Error visitLanguageStmt(const LanguageResource *) override; + Error visitVersionStmt(const VersionStmt *) override; struct ObjectInfo { uint16_t LanguageInfo; + uint32_t Characteristics; + uint32_t VersionInfo; - ObjectInfo() : LanguageInfo(0) {} + ObjectInfo() : LanguageInfo(0), Characteristics(0), VersionInfo(0) {} } ObjectData; private: @@ -45,7 +50,15 @@ writeResource(const RCResource *Res, Error (ResourceSerializator::*BodyWriter)(const RCResource *)); + // NullResource Error writeNullBody(const RCResource *); + + // AcceleratorsResource + Error writeSingleAccelerator(const AcceleratorsResource::Accelerator &, + bool IsLastItem); + Error writeAcceleratorsBody(const RCResource *); + + // HTMLResource Error writeHTMLBody(const RCResource *); // Output stream handling. Index: llvm/tools/llvm-rc/ResourceSerializator.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceSerializator.cpp +++ llvm/tools/llvm-rc/ResourceSerializator.cpp @@ -28,6 +28,19 @@ namespace llvm { namespace rc { +// Class that employs RAII to save the current serializator 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 { + ResourceSerializator *Serializator; + ResourceSerializator::ObjectInfo SavedInfo; + +public: + ContextKeeper(ResourceSerializator *V) + : Serializator(V), SavedInfo(V->ObjectData) {} + ~ContextKeeper() { Serializator->ObjectData = SavedInfo; } +}; + static Error createError(Twine Message, std::errc Type = std::errc::invalid_argument) { return make_error(Message, std::make_error_code(Type)); @@ -184,10 +197,20 @@ return writeResource(Res, &ResourceSerializator::writeNullBody); } +Error ResourceSerializator::visitAcceleratorsResource(const RCResource *Res) { + return writeResource(Res, &ResourceSerializator::writeAcceleratorsBody); +} + Error ResourceSerializator::visitHTMLResource(const RCResource *Res) { return writeResource(Res, &ResourceSerializator::writeHTMLBody); } +Error ResourceSerializator::visitCharacteristicsStmt( + const CharacteristicsStmt *Stmt) { + ObjectData.Characteristics = Stmt->Value; + return Error::success(); +} + Error ResourceSerializator::visitLanguageStmt(const LanguageResource *Stmt) { RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID")); RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID")); @@ -195,6 +218,11 @@ return Error::success(); } +Error ResourceSerializator::visitVersionStmt(const VersionStmt *Stmt) { + ObjectData.VersionInfo = Stmt->Value; + return Error::success(); +} + Error ResourceSerializator::writeResource( const RCResource *Res, Error (ResourceSerializator::*BodyWriter)(const RCResource *)) { @@ -208,12 +236,16 @@ 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->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo), - ulittle32_t(0), // VersionInfo - ulittle32_t(0)}; // Characteristics + ulittle32_t(ObjectData.VersionInfo), + ulittle32_t(ObjectData.Characteristics)}; writeObject(HeaderSuffix); uint64_t DataLoc = tell(); @@ -229,10 +261,122 @@ return Error::success(); } +// --- NullResource helpers. --- // + Error ResourceSerializator::writeNullBody(const RCResource *) { return Error::success(); } +// --- AcceleratorsResource helpers. --- // + +Error ResourceSerializator::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(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(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 ResourceSerializator::writeAcceleratorsBody(const RCResource *Base) { + auto *Res = cast(Base); + size_t AcceleratorId = 0; + for (auto &Acc : Res->Accelerators) { + ++AcceleratorId; + RETURN_IF_ERROR( + writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size())); + } + return Error::success(); +} + +// --- HTMLResource helpers. --- // + Error ResourceSerializator::writeHTMLBody(const RCResource *Base) { return appendFile(cast(Base)->HTMLLoc); } Index: llvm/tools/llvm-rc/ResourceVisitor.h =================================================================== --- llvm/tools/llvm-rc/ResourceVisitor.h +++ llvm/tools/llvm-rc/ResourceVisitor.h @@ -21,14 +21,19 @@ class RCResource; +class CharacteristicsStmt; class LanguageResource; +class VersionStmt; class Visitor { public: virtual Error visitNullResource(const RCResource *) = 0; + virtual Error visitAcceleratorsResource(const RCResource *) = 0; virtual Error visitHTMLResource(const RCResource *) = 0; + virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0; virtual Error visitLanguageStmt(const LanguageResource *) = 0; + virtual Error visitVersionStmt(const VersionStmt *) = 0; virtual ~Visitor() {} };