[llvm] [llvm-rc] Accept filenames provided as multiple string literals (PR #68881)

Martin Storsjö via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 16 02:34:10 PDT 2023


https://github.com/mstorsjo updated https://github.com/llvm/llvm-project/pull/68881

>From ca020c7aa8a2f93328b80f93765582117a5109c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Fri, 31 Jul 2020 14:31:57 +0300
Subject: [PATCH] [llvm-rc] Accept filenames provided as multiple string
 literals

GNU windres supports this, while MS rc.exe doesn't.

MS rc.exe only supports treating consecutive string literals as
if they were fused into one in a few fixed locations (most of
which are already supported), while GNU windres supports this
essentially anywhere in any string. See
b989fcbae6f179ad887d19ceef83ace1c00b87cc for one recent change
that extended support for this in one specific resource.

A reasonable use case for multiple concatenated string literals
that GNU windres accepts is `1 ICON DIR "/name.ico"`, where the
directory is provided via the preprocessor, expanding to another
string literal; this is https://github.com/llvm/llvm-project/issues/51286.

Extend the parser to try to consume all consecutive string
tokens, whenever reading a filename. Adjust the handling of user
data resources read from a file to use the readFilename() helper.

While this probably doesn't cover every single case where GNU
windres might accept concatenated string literals, this is
the primary missing case that has been reported so far.
---
 llvm/test/tools/llvm-rc/Inputs/split-path.rc |  2 ++
 llvm/test/tools/llvm-rc/split-path.test      |  7 ++++++
 llvm/tools/llvm-rc/ResourceScriptParser.cpp  | 26 +++++++++++++++++---
 llvm/tools/llvm-rc/ResourceScriptParser.h    |  4 +++
 4 files changed, 35 insertions(+), 4 deletions(-)
 create mode 100644 llvm/test/tools/llvm-rc/Inputs/split-path.rc
 create mode 100644 llvm/test/tools/llvm-rc/split-path.test

diff --git a/llvm/test/tools/llvm-rc/Inputs/split-path.rc b/llvm/test/tools/llvm-rc/Inputs/split-path.rc
new file mode 100644
index 000000000000000..fb510e89698f747
--- /dev/null
+++ b/llvm/test/tools/llvm-rc/Inputs/split-path.rc
@@ -0,0 +1,2 @@
+100 ICON "subdir" "/icon-new.ico"
+101 24 "subdir" "/empty.manifest"
diff --git a/llvm/test/tools/llvm-rc/split-path.test b/llvm/test/tools/llvm-rc/split-path.test
new file mode 100644
index 000000000000000..a12fd2bc32c1161
--- /dev/null
+++ b/llvm/test/tools/llvm-rc/split-path.test
@@ -0,0 +1,7 @@
+; RUN: rm -rf %t
+; RUN: mkdir %t
+; RUN: cd %t
+; RUN: mkdir subdir
+; RUN: cp %p/Inputs/icon-new.ico subdir
+; RUN: touch subdir/empty.manifest
+; RUN: llvm-windres --no-preprocess %p/Inputs/split-path.rc %t/split-path.res
diff --git a/llvm/tools/llvm-rc/ResourceScriptParser.cpp b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
index 9e1047448831b37..4f02fa502d24fd5 100644
--- a/llvm/tools/llvm-rc/ResourceScriptParser.cpp
+++ b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
@@ -238,7 +238,24 @@ Expected<StringRef> RCParser::readString() {
 Expected<StringRef> RCParser::readFilename() {
   if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
     return getExpectedError("string");
-  return read().value();
+  const RCToken &Token = read();
+  StringRef Str = Token.value();
+  if (Token.kind() != Kind::String)
+    return Str;
+  while (isNextTokenKind(Kind::String)) {
+    const RCToken &NextToken = read();
+    StringRef Next = NextToken.value();
+    bool IsWide = Str.consume_front_insensitive("L");
+    Next.consume_front_insensitive("L");
+    bool StrUnquoted = Str.consume_front("\"") && Str.consume_back("\"");
+    bool NextUnquoted = Next.consume_front("\"") && Next.consume_back("\"");
+    assert(StrUnquoted && NextUnquoted);
+    (void)StrUnquoted;
+    (void)NextUnquoted;
+
+    Str = Saver.save(Twine(IsWide ? "L" : "") + "\"" + Str + Next + "\"");
+  }
+  return Str;
 }
 
 Expected<StringRef> RCParser::readIdentifier() {
@@ -499,9 +516,10 @@ RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
   // 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);
+  case Kind::Identifier: {
+    ASSIGN_OR_RETURN(Filename, readFilename());
+    return std::make_unique<UserDefinedResource>(Type, *Filename, MemoryFlags);
+  }
   default:
     break;
   }
diff --git a/llvm/tools/llvm-rc/ResourceScriptParser.h b/llvm/tools/llvm-rc/ResourceScriptParser.h
index 5c01cec0f151e8b..603afd8d73fb1aa 100644
--- a/llvm/tools/llvm-rc/ResourceScriptParser.h
+++ b/llvm/tools/llvm-rc/ResourceScriptParser.h
@@ -18,6 +18,7 @@
 #include "ResourceScriptToken.h"
 
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/StringSaver.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <system_error>
@@ -185,6 +186,9 @@ class RCParser {
   std::vector<RCToken> Tokens;
   LocIter CurLoc;
   const LocIter End;
+
+  BumpPtrAllocator Alloc;
+  StringSaver Saver{Alloc};
 };
 
 } // namespace rc



More information about the llvm-commits mailing list