llvm.org GIT mirror llvm / e37621b
[llvm-rc] Serialize HTML resources to .res files (serialization, pt 1). This allows to process HTML resources defined in .rc scripts and output them to resulting .res files. Additionally, some infrastructure allowing to output these files is created. This is the first resource type we can operate on. Thanks to Nico Weber for his original work in this area. Differential Revision: reviews.llvm.org/D37283 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@314538 91177308-0d34-0410-b5e6-96231b3b80d8 Marek Sokolowski 2 years ago
15 changed file(s) with 593 addition(s) and 45 deletion(s). Raw diff Collapse all Expand all
0 1 HTML "some-really-nonexistent-file.html"
0 100 HTML "webpage1.html"
1 Kitten HTML "webpage2.html"
66 ; CHECK-DAG: USAGE: rc [options]
77 ; CHECK-DAG: OPTIONS:
88 ; CHECK-NEXT: /? Display this help and exit.
9 ; CHECK-NEXT: /dry-run Don't compile the input; only try to parse it.
910 ; CHECK-NEXT: /D Define a symbol for the C preprocessor.
1011 ; CHECK-NEXT: /FO Change the output file location.
1112 ; CHECK-NEXT: /H Display this help and exit.
None ; RUN: llvm-rc /V %p/Inputs/parser-expr.rc | FileCheck %s
0 ; RUN: llvm-rc /dry-run /V %p/Inputs/parser-expr.rc | FileCheck %s
11
22 ; CHECK: Language: 5, Sublanguage: 1
33 ; CHECK-NEXT: Language: 3, Sublanguage: 2
1616 ; CHECK-NEXT: Language: 5, Sublanguage: 7
1717
1818
19 ; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-binary-1.rc 2>&1 | FileCheck %s --check-prefix BINARY1
19 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-expr-bad-binary-1.rc 2>&1 | FileCheck %s --check-prefix BINARY1
2020
2121 ; BINARY1: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got &
2222
2323
24 ; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-binary-2.rc 2>&1 | FileCheck %s --check-prefix BINARY2
24 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-expr-bad-binary-2.rc 2>&1 | FileCheck %s --check-prefix BINARY2
2525
2626 ; BINARY2: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got |
2727
2828
29 ; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-binary-3.rc 2>&1 | FileCheck %s --check-prefix BINARY3
29 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-expr-bad-binary-3.rc 2>&1 | FileCheck %s --check-prefix BINARY3
3030
3131 ; BINARY3: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got +
3232
3333
34 ; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-unary.rc 2>&1 | FileCheck %s --check-prefix UNARY
34 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-expr-bad-unary.rc 2>&1 | FileCheck %s --check-prefix UNARY
3535
3636 ; UNARY: llvm-rc: Error parsing file: expected ',', got ~
3737
3838
39 ; RUN: not llvm-rc /V %p/Inputs/parser-expr-unbalanced-1.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED1
39 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-expr-unbalanced-1.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED1
4040
4141 ; UNBALANCED1: llvm-rc: Error parsing file: expected ')', got ,
4242
4343
44 ; RUN: not llvm-rc /V %p/Inputs/parser-expr-unbalanced-2.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED2
44 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-expr-unbalanced-2.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED2
4545
4646 ; UNBALANCED2: llvm-rc: Error parsing file: expected ',', got )
4747
4848
49 ; RUN: not llvm-rc /V %p/Inputs/parser-expr-unbalanced-3.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED3
49 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-expr-unbalanced-3.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED3
5050
5151 ; UNBALANCED3: llvm-rc: Error parsing file: expected ',', got )
None ; RUN: llvm-rc /V %p/Inputs/parser-correct-everything.rc | FileCheck %s --check-prefix PGOOD
0 ; RUN: llvm-rc /dry-run /V %p/Inputs/parser-correct-everything.rc | FileCheck %s --check-prefix PGOOD
11
22 ; PGOOD: Icon (meh): "hello.bmp"
33 ; PGOOD-NEXT: Icon (Icon): "Icon"
9999
100100
101101
102 ; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE1
102 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-stringtable-no-string.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE1
103103
104104 ; PSTRINGTABLE1: llvm-rc: Error parsing file: expected string, got }
105105
106106
107 ; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-weird-option.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE2
107 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-stringtable-weird-option.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE2
108108
109109 ; PSTRINGTABLE2: llvm-rc: Error parsing file: expected optional statement type, BEGIN or '{', got NONSENSETYPE
110110
111111
112 ; RUN: not llvm-rc /V %p/Inputs/parser-eof.rc 2>&1 | FileCheck %s --check-prefix PEOF
112 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-eof.rc 2>&1 | FileCheck %s --check-prefix PEOF
113113
114114 ; PEOF: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got
115115
116116
117 ; RUN: not llvm-rc /V %p/Inputs/parser-no-characteristics-arg.rc 2>&1 | FileCheck %s --check-prefix PCHARACTERISTICS1
117 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-no-characteristics-arg.rc 2>&1 | FileCheck %s --check-prefix PCHARACTERISTICS1
118118
119119 ; PCHARACTERISTICS1: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got BEGIN
120120
121121
122 ; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-token.rc 2>&1 | FileCheck %s --check-prefix PNONSENSE1
122 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-nonsense-token.rc 2>&1 | FileCheck %s --check-prefix PNONSENSE1
123123
124124 ; PNONSENSE1: llvm-rc: Error parsing file: expected int or identifier, got &
125125
126126
127 ; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-type.rc 2>&1 | FileCheck %s --check-prefix PNONSENSE2
127 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-nonsense-type.rc 2>&1 | FileCheck %s --check-prefix PNONSENSE2
128128
129129 ; PNONSENSE2: llvm-rc: Error parsing file: expected filename, '{' or BEGIN, got
130130
131131
132 ; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-type-eof.rc 2>&1 | FileCheck %s --check-prefix PNONSENSE3
132 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-nonsense-type-eof.rc 2>&1 | FileCheck %s --check-prefix PNONSENSE3
133133
134134 ; PNONSENSE3: llvm-rc: Error parsing file: expected int or identifier, got
135135
136136
137 ; RUN: not llvm-rc /V %p/Inputs/parser-language-no-comma.rc 2>&1 | FileCheck %s --check-prefix PLANGUAGE1
137 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-language-no-comma.rc 2>&1 | FileCheck %s --check-prefix PLANGUAGE1
138138
139139 ; PLANGUAGE1: llvm-rc: Error parsing file: expected ',', got 7
140140
141141
142 ; RUN: not llvm-rc /V %p/Inputs/parser-language-too-many-commas.rc 2>&1 | FileCheck %s --check-prefix PLANGUAGE2
142 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-language-too-many-commas.rc 2>&1 | FileCheck %s --check-prefix PLANGUAGE2
143143
144144 ; PLANGUAGE2: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got ,
145145
146146
147 ; RUN: not llvm-rc /V %p/Inputs/parser-html-bad-string.rc 2>&1 | FileCheck %s --check-prefix PHTML1
147 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-html-bad-string.rc 2>&1 | FileCheck %s --check-prefix PHTML1
148148
149149 ; PHTML1: llvm-rc: Error parsing file: expected string, got ThisPassesInTheOriginalToolButDocSaysItShouldBeQuoted
150150
151151
152 ; RUN: not llvm-rc /V %p/Inputs/parser-html-extra-comma.rc 2>&1 | FileCheck %s --check-prefix PHTML2
152 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-html-extra-comma.rc 2>&1 | FileCheck %s --check-prefix PHTML2
153153
154154 ; PHTML2: llvm-rc: Error parsing file: expected string, got ,
155155
156156
157 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS1
157 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-accelerators-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS1
158158
159159 ; PACCELERATORS1: llvm-rc: Error parsing file: expected ASCII/VIRTKEY/NOINVERT/ALT/SHIFT/CONTROL, got HELLO
160160
161161
162 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-bad-int-or-string.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS2
162 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-accelerators-bad-int-or-string.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS2
163163
164164 ; PACCELERATORS2: llvm-rc: Error parsing file: expected int or string, got NotIntOrString
165165
166166
167 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-no-comma.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS3
167 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-accelerators-no-comma.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS3
168168
169169 ; PACCELERATORS3: llvm-rc: Error parsing file: expected int or string, got CONTROL
170170
171171
172 ; RUN: not llvm-rc /V %p/Inputs/parser-accelerators-no-comma-2.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS4
172 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-accelerators-no-comma-2.rc 2>&1 | FileCheck %s --check-prefix PACCELERATORS4
173173
174174 ; PACCELERATORS4: llvm-rc: Error parsing file: expected ',', got 10
175175
176176
177 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-bad-id.rc 2>&1 | FileCheck %s --check-prefix PMENU1
177 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-menu-bad-id.rc 2>&1 | FileCheck %s --check-prefix PMENU1
178178
179179 ; PMENU1: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got A
180180
181181
182 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PMENU2
182 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-menu-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PMENU2
183183
184184 ; PMENU2: llvm-rc: Error parsing file: expected CHECKED/GRAYED/HELP/INACTIVE/MENUBARBREAK/MENUBREAK, got ERRONEOUS
185185
186186
187 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-missing-block.rc 2>&1 | FileCheck %s --check-prefix PMENU3
187 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-menu-missing-block.rc 2>&1 | FileCheck %s --check-prefix PMENU3
188188
189189 ; PMENU3: llvm-rc: Error parsing file: expected '{', got POPUP
190190
191191
192 ; RUN: not llvm-rc /V %p/Inputs/parser-menu-misspelled-separator.rc 2>&1 | FileCheck %s --check-prefix PMENU4
192 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-menu-misspelled-separator.rc 2>&1 | FileCheck %s --check-prefix PMENU4
193193
194194 ; PMENU4: llvm-rc: Error parsing file: expected SEPARATOR or string, got NOTSEPARATOR
195195
196196
197 ; RUN: not llvm-rc /V %p/Inputs/parser-dialog-cant-give-helpid.rc 2>&1 | FileCheck %s --check-prefix PDIALOG1
197 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-dialog-cant-give-helpid.rc 2>&1 | FileCheck %s --check-prefix PDIALOG1
198198
199199 ; PDIALOG1: llvm-rc: Error parsing file: expected identifier, got ,
200200
201201
202 ; RUN: not llvm-rc /V %p/Inputs/parser-dialog-too-few-args.rc 2>&1 | FileCheck %s --check-prefix PDIALOG2
202 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-dialog-too-few-args.rc 2>&1 | FileCheck %s --check-prefix PDIALOG2
203203
204204 ; PDIALOG2: llvm-rc: Error parsing file: expected ',', got }
205205
206206
207 ; RUN: not llvm-rc /V %p/Inputs/parser-dialog-too-many-args.rc 2>&1 | FileCheck %s --check-prefix PDIALOG3
207 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-dialog-too-many-args.rc 2>&1 | FileCheck %s --check-prefix PDIALOG3
208208
209209 ; PDIALOG3: llvm-rc: Error parsing file: expected identifier, got ,
210210
211211
212 ; RUN: not llvm-rc /V %p/Inputs/parser-dialog-unknown-type.rc 2>&1 | FileCheck %s --check-prefix PDIALOG4
212 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-dialog-unknown-type.rc 2>&1 | FileCheck %s --check-prefix PDIALOG4
213213
214214 ; PDIALOG4: llvm-rc: Error parsing file: expected control type, END or '}', got UNKNOWN
215215
216216
217 ; RUN: not llvm-rc /V %p/Inputs/parser-dialog-unnecessary-string.rc 2>&1 | FileCheck %s --check-prefix PDIALOG5
217 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-dialog-unnecessary-string.rc 2>&1 | FileCheck %s --check-prefix PDIALOG5
218218
219219 ; PDIALOG5: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got "This shouldn't be here"
220220
221221
222 ; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-wrong-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO1
222 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-versioninfo-wrong-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO1
223223
224224 ; PVERSIONINFO1: llvm-rc: Error parsing file: expected fixed VERSIONINFO statement type, got WEIRDFIXED
225225
226226
227 ; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-named-main-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO2
227 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-versioninfo-named-main-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO2
228228
229229 ; PVERSIONINFO2: llvm-rc: Error parsing file: expected fixed VERSIONINFO statement type, got BLOCK
230230
231231
232 ; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-unnamed-inner-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO3
232 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-versioninfo-unnamed-inner-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO3
233233
234234 ; PVERSIONINFO3: llvm-rc: Error parsing file: expected string, got {
235235
236236
237 ; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-unnamed-value.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO4
237 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-versioninfo-unnamed-value.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO4
238238
239239 ; PVERSIONINFO4: llvm-rc: Error parsing file: expected string, got END
240240
241241
242 ; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-bad-type.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO5
242 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-versioninfo-bad-type.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO5
243243
244244 ; PVERSIONINFO5: llvm-rc: Error parsing file: expected BLOCK or VALUE, got INCORRECT
245245
246246
247 ; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-repeated-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO6
247 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-versioninfo-repeated-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO6
248248
249249 ; PVERSIONINFO6: llvm-rc: Error parsing file: expected yet unread fixed VERSIONINFO statement type, got FILEVERSION
250250
251251
252 ; RUN: not llvm-rc /V %p/Inputs/parser-user-invalid-contents.rc 2>&1 | FileCheck %s --check-prefix PUSER1
252 ; RUN: not llvm-rc /dry-run /V %p/Inputs/parser-user-invalid-contents.rc 2>&1 | FileCheck %s --check-prefix PUSER1
253253
254254 ; PUSER1: llvm-rc: Error parsing file: expected int or string, got InvalidToken
0 ; RUN: rm -rf %t && mkdir %t && cd %t
1 ; RUN: cp %p/Inputs/webpage*.html .
2 ; RUN: llvm-rc /FO %t/tag-html.res %p/Inputs/tag-html.rc
3 ; RUN: llvm-readobj %t/tag-html.res | FileCheck %s --check-prefix HTML
4
5 ; HTML: Resource type (int): 23
6 ; HTML-NEXT: Resource name (int): 100
7 ; HTML-NEXT: Data version: 0
8 ; HTML-NEXT: Memory flags: 0x30
9 ; HTML-NEXT: Language ID: 1033
10 ; HTML-NEXT: Version (major): 0
11 ; HTML-NEXT: Version (minor): 0
12 ; HTML-NEXT: Characteristics: 0
13 ; HTML-NEXT: Data size: 45
14 ; HTML-NEXT: Data: (
15 ; HTML-NEXT: 0000: 3C68746D 6C3E0A20 203C626F 64793E0A |. .|
16 ; HTML-NEXT: 0010: 20202020 48656C6C 6F210A20 203C2F62 | Hello!.
17 ; HTML-NEXT: 0020: 6F64793E 0A3C2F68 746D6C3E 0A |ody>..|
18 ; HTML-NEXT: )
19
20 ; HTML-DAG: Resource type (int): 23
21 ; HTML-NEXT: Resource name (string): KITTEN
22 ; HTML-NEXT: Data version: 0
23 ; HTML-NEXT: Memory flags: 0x30
24 ; HTML-NEXT: Language ID: 1033
25 ; HTML-NEXT: Version (major): 0
26 ; HTML-NEXT: Version (minor): 0
27 ; HTML-NEXT: Characteristics: 0
28 ; HTML-NEXT: Data size: 61
29 ; HTML-NEXT: Data: (
30 ; HTML-NEXT: 0000: 3C212D2D 2053686F 756C6420 6E6F7420 |.33 ; HTML-NEXT: 0030: 69747465 6E732E62 6D70223E 0A |ittens.bmp">.|
34 ; HTML-NEXT: )
35
36
37 ; RUN: not llvm-rc /FO %t/tag-html-wrong.res %p/Inputs/tag-html-wrong.rc 2>&1 | FileCheck %s --check-prefix NOFILE
38
39 ; NOFILE: llvm-rc: Error in HTML statement (ID 1):
40 ; NOFILE-NEXT: Error opening file 'some-really-nonexistent-file.html':
99
1010 add_llvm_tool(llvm-rc
1111 llvm-rc.cpp
12 ResourceFileWriter.cpp
1213 ResourceScriptParser.cpp
1314 ResourceScriptStmt.cpp
1415 ResourceScriptToken.cpp
3131 Alias,
3232 HelpText<"Display this help and exit.">;
3333
34 def DRY_RUN : Flag<[ "/", "-" ], "dry-run">,
35 HelpText<"Don't compile the input; only try to parse it.">;
36
3437 // Unused switches (at least for now). These will stay unimplemented
3538 // in an early stage of development and can be ignored. However, we need to
3639 // parse them in order to preserve the compatibility with the original tool.
0 //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===---------------------------------------------------------------------===//
8 //
9 // This implements the visitor serializing resources to a .res stream.
10 //
11 //===---------------------------------------------------------------------===//
12
13 #include "ResourceFileWriter.h"
14
15 #include "llvm/Object/WindowsResource.h"
16 #include "llvm/Support/ConvertUTF.h"
17 #include "llvm/Support/Endian.h"
18 #include "llvm/Support/EndianStream.h"
19
20 using namespace llvm::support;
21
22 // Take an expression returning llvm::Error and forward the error if it exists.
23 #define RETURN_IF_ERROR(Expr) \
24 if (auto Err = (Expr)) \
25 return Err;
26
27 namespace llvm {
28 namespace rc {
29
30 static Error createError(Twine Message,
31 std::errc Type = std::errc::invalid_argument) {
32 return make_error(Message, std::make_error_code(Type));
33 }
34
35 static Error checkNumberFits(uint32_t Number, size_t MaxBits, Twine FieldName) {
36 assert(1 <= MaxBits && MaxBits <= 32);
37 if (!(Number >> MaxBits))
38 return Error::success();
39 return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
40 Twine(MaxBits) + " bits.",
41 std::errc::value_too_large);
42 }
43
44 template
45 static Error checkNumberFits(uint32_t Number, Twine FieldName) {
46 return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
47 }
48
49 static Error checkIntOrString(IntOrString Value, Twine FieldName) {
50 if (!Value.isInt())
51 return Error::success();
52 return checkNumberFits(Value.getInt(), FieldName);
53 }
54
55 static bool stripQuotes(StringRef &Str, bool &IsLongString) {
56 if (!Str.contains('"'))
57 return false;
58
59 // Just take the contents of the string, checking if it's been marked long.
60 IsLongString = Str.startswith_lower("L");
61 if (IsLongString)
62 Str = Str.drop_front();
63
64 bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
65 (void)StripSuccess;
66 assert(StripSuccess && "Strings should be enclosed in quotes.");
67 return true;
68 }
69
70 // Describes a way to handle '\0' characters when processing the string.
71 // rc.exe tool sometimes behaves in a weird way in postprocessing.
72 // If the string to be output is equivalent to a C-string (e.g. in MENU
73 // titles), string is (predictably) truncated after first 0-byte.
74 // When outputting a string table, the behavior is equivalent to appending
75 // '\0\0' at the end of the string, and then stripping the string
76 // before the first '\0\0' occurrence.
77 // Finally, when handling strings in user-defined resources, 0-bytes
78 // aren't stripped, nor do they terminate the string.
79
80 enum class NullHandlingMethod {
81 UserResource, // Don't terminate string on '\0'.
82 CutAtNull, // Terminate string on '\0'.
83 CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
84 };
85
86 // Parses an identifier or string and returns a processed version of it.
87 // For now, it only strips the string boundaries, but TODO:
88 // * Squash "" to a single ".
89 // * Replace the escape sequences with their processed version.
90 // For identifiers, this is no-op.
91 static Error processString(StringRef Str, NullHandlingMethod NullHandler,
92 bool &IsLongString, SmallVectorImpl &Result) {
93 assert(NullHandler == NullHandlingMethod::CutAtNull);
94
95 bool IsString = stripQuotes(Str, IsLongString);
96 convertUTF8ToUTF16String(Str, Result);
97
98 if (!IsString) {
99 // It's an identifier if it's not a string. Make all characters uppercase.
100 for (UTF16 &Ch : Result) {
101 assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
102 Ch = toupper(Ch);
103 }
104 return Error::success();
105 }
106
107 // We don't process the string contents. Only cut at '\0'.
108
109 for (size_t Pos = 0; Pos < Result.size(); ++Pos)
110 if (Result[Pos] == '\0')
111 Result.resize(Pos);
112
113 return Error::success();
114 }
115
116 uint64_t ResourceFileWriter::writeObject(const ArrayRef Data) {
117 uint64_t Result = tell();
118 FS->write((const char *)Data.begin(), Data.size());
119 return Result;
120 }
121
122 Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
123 SmallVector ProcessedString;
124 bool IsLongString;
125 RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
126 IsLongString, ProcessedString));
127 for (auto Ch : ProcessedString)
128 writeInt(Ch);
129 if (WriteTerminator)
130 writeInt(0);
131 return Error::success();
132 }
133
134 Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
135 return writeIntOrString(Ident);
136 }
137
138 Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
139 if (!Value.isInt())
140 return writeCString(Value.getString());
141
142 writeInt(0xFFFF);
143 writeInt(Value.getInt());
144 return Error::success();
145 }
146
147 Error ResourceFileWriter::appendFile(StringRef Filename) {
148 bool IsLong;
149 stripQuotes(Filename, IsLong);
150
151 // Filename path should be relative to the current working directory.
152 // FIXME: docs say so, but reality is more complicated, script
153 // location and include paths must be taken into account.
154 ErrorOr> File =
155 MemoryBuffer::getFile(Filename, -1, false);
156 if (!File)
157 return make_error("Error opening file '" + Filename +
158 "': " + File.getError().message(),
159 File.getError());
160 *FS << (*File)->getBuffer();
161 return Error::success();
162 }
163
164 void ResourceFileWriter::padStream(uint64_t Length) {
165 assert(Length > 0);
166 uint64_t Location = tell();
167 Location %= Length;
168 uint64_t Pad = (Length - Location) % Length;
169 for (uint64_t i = 0; i < Pad; ++i)
170 writeInt(0);
171 }
172
173 Error ResourceFileWriter::handleError(Error &&Err, const RCResource *Res) {
174 if (Err)
175 return joinErrors(createError("Error in " + Res->getResourceTypeName() +
176 " statement (ID " + Twine(Res->ResName) +
177 "): "),
178 std::move(Err));
179 return Error::success();
180 }
181
182 Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
183 return writeResource(Res, &ResourceFileWriter::writeNullBody);
184 }
185
186 Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
187 return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
188 }
189
190 Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
191 RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
192 RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
193 ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
194 return Error::success();
195 }
196
197 Error ResourceFileWriter::writeResource(
198 const RCResource *Res,
199 Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
200 // We don't know the sizes yet.
201 object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
202 uint64_t HeaderLoc = writeObject(HeaderPrefix);
203
204 auto ResType = Res->getResourceType();
205 RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
206 RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
207 RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
208 RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
209
210 padStream(sizeof(uint32_t));
211 object::WinResHeaderSuffix HeaderSuffix{
212 ulittle32_t(0), // DataVersion; seems to always be 0
213 ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo),
214 ulittle32_t(0), // VersionInfo
215 ulittle32_t(0)}; // Characteristics
216 writeObject(HeaderSuffix);
217
218 uint64_t DataLoc = tell();
219 RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
220 // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
221
222 // Update the sizes.
223 HeaderPrefix.DataSize = tell() - DataLoc;
224 HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
225 writeObjectAt(HeaderPrefix, HeaderLoc);
226 padStream(sizeof(uint32_t));
227
228 return Error::success();
229 }
230
231 Error ResourceFileWriter::writeNullBody(const RCResource *) {
232 return Error::success();
233 }
234
235 Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
236 return appendFile(cast(Base)->HTMLLoc);
237 }
238
239 } // namespace rc
240 } // namespace llvm
0 //===-- ResourceSerializator.h ----------------------------------*- C++-*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===---------------------------------------------------------------------===//
8 //
9 // This defines a visitor serializing resources to a .res stream.
10 //
11 //===---------------------------------------------------------------------===//
12
13 #ifndef LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
14 #define LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
15
16 #include "ResourceScriptStmt.h"
17 #include "ResourceVisitor.h"
18
19 #include "llvm/Support/Endian.h"
20
21 namespace llvm {
22 namespace rc {
23
24 class ResourceFileWriter : public Visitor {
25 public:
26 ResourceFileWriter(std::unique_ptr Stream)
27 : FS(std::move(Stream)) {
28 assert(FS && "Output stream needs to be provided to the serializator");
29 }
30
31 Error visitNullResource(const RCResource *) override;
32 Error visitHTMLResource(const RCResource *) override;
33
34 Error visitLanguageStmt(const LanguageResource *) override;
35
36 struct ObjectInfo {
37 uint16_t LanguageInfo;
38
39 ObjectInfo() : LanguageInfo(0) {}
40 } ObjectData;
41
42 private:
43 Error handleError(Error &&Err, const RCResource *Res);
44
45 Error
46 writeResource(const RCResource *Res,
47 Error (ResourceFileWriter::*BodyWriter)(const RCResource *));
48
49 Error writeNullBody(const RCResource *);
50 Error writeHTMLBody(const RCResource *);
51
52 // Output stream handling.
53 std::unique_ptr FS;
54
55 uint64_t tell() const { return FS->tell(); }
56
57 uint64_t writeObject(const ArrayRef Data);
58
59 template uint64_t writeInt(const T &Value) {
60 support::detail::packed_endian_specific_integral
61 support::unaligned>
62 Object(Value);
63 return writeObject(Object);
64 }
65
66 template uint64_t writeObject(const T &Value) {
67 return writeObject(ArrayRef(
68 reinterpret_cast(&Value), sizeof(T)));
69 }
70
71 template void writeObjectAt(const T &Value, uint64_t Position) {
72 FS->pwrite((const char *)&Value, sizeof(T), Position);
73 }
74
75 Error writeCString(StringRef Str, bool WriteTerminator = true);
76
77 Error writeIdentifier(const IntOrString &Ident);
78 Error writeIntOrString(const IntOrString &Data);
79
80 Error appendFile(StringRef Filename);
81
82 void padStream(uint64_t Length);
83 };
84
85 } // namespace rc
86 } // namespace llvm
87
88 #endif
1414 #define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
1515
1616 #include "ResourceScriptToken.h"
17 #include "ResourceVisitor.h"
1718
1819 #include "llvm/ADT/StringSet.h"
1920
4849 return !IsInt && Data.String.equals_lower(Str);
4950 }
5051
52 bool isInt() const { return IsInt; }
53
54 uint32_t getInt() const {
55 assert(IsInt);
56 return Data.Int;
57 }
58
59 const StringRef &getString() const {
60 assert(!IsInt);
61 return Data.String;
62 }
63
64 operator Twine() const {
65 return isInt() ? Twine(getInt()) : Twine(getString());
66 }
67
5168 friend raw_ostream &operator<<(raw_ostream &, const IntOrString &);
69 };
70
71 enum ResourceKind {
72 // These resource kinds have corresponding .res resource type IDs
73 // (TYPE in RESOURCEHEADER structure). The numeric value assigned to each
74 // kind is equal to this type ID.
75 RkNull = 0,
76 RkMenu = 4,
77 RkDialog = 5,
78 RkAccelerators = 9,
79 RkVersionInfo = 16,
80 RkHTML = 23,
81
82 // These kinds don't have assigned type IDs (they might be the resources
83 // of invalid kind, expand to many resource structures in .res files,
84 // or have variable type ID). In order to avoid ID clashes with IDs above,
85 // we assign the kinds the values 256 and larger.
86 RkInvalid = 256,
87 RkBase,
88 RkCursor,
89 RkIcon,
90 RkUser
91 };
92
93 // Non-zero memory flags.
94 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648027(v=vs.85).aspx
95 enum MemoryFlags {
96 MfMoveable = 0x10,
97 MfPure = 0x20,
98 MfPreload = 0x40,
99 MfDiscardable = 0x1000
52100 };
53101
54102 // Base resource. All the resources should derive from this base.
55103 class RCResource {
56 protected:
104 public:
57105 IntOrString ResName;
58106
59 public:
60107 RCResource() = default;
61108 RCResource(RCResource &&) = default;
62109 void setName(const IntOrString &Name) { ResName = Name; }
64111 return OS << "Base statement\n";
65112 };
66113 virtual ~RCResource() {}
114
115 virtual Error visit(Visitor *) const {
116 llvm_unreachable("This is unable to call methods from Visitor base");
117 }
118
119 // By default, memory flags are DISCARDABLE | PURE | MOVEABLE.
120 virtual uint16_t getMemoryFlags() const {
121 return MfDiscardable | MfPure | MfMoveable;
122 }
123 virtual ResourceKind getKind() const { return RkBase; }
124 static bool classof(const RCResource *Res) { return true; }
125
126 virtual IntOrString getResourceType() const {
127 llvm_unreachable("This cannot be called on objects without types.");
128 }
129 virtual Twine getResourceTypeName() const {
130 llvm_unreachable("This cannot be called on objects without types.");
131 };
132 };
133
134 // An empty resource. It has no content, type 0, ID 0 and all of its
135 // characteristics are equal to 0.
136 class NullResource : public RCResource {
137 public:
138 raw_ostream &log(raw_ostream &OS) const override {
139 return OS << "Null resource\n";
140 }
141 Error visit(Visitor *V) const override { return V->visitNullResource(this); }
142 IntOrString getResourceType() const override { return 0; }
143 Twine getResourceTypeName() const override { return "(NULL)"; }
144 uint16_t getMemoryFlags() const override { return 0; }
67145 };
68146
69147 // Optional statement base. All such statements should derive from this base.
88166 //
89167 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx
90168 class LanguageResource : public OptionalStmt {
169 public:
91170 uint32_t Lang, SubLang;
92171
93 public:
94172 LanguageResource(uint32_t LangId, uint32_t SubLangId)
95173 : Lang(LangId), SubLang(SubLangId) {}
96174 raw_ostream &log(raw_ostream &) const override;
175
176 // This is not a regular top-level statement; when it occurs, it just
177 // modifies the language context.
178 Error visit(Visitor *V) const override { return V->visitLanguageStmt(this); }
179 Twine getResourceTypeName() const override { return "LANGUAGE"; }
97180 };
98181
99182 // ACCELERATORS resource. Defines a named table of accelerators for the app.
160243 //
161244 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa966018(v=vs.85).aspx
162245 class HTMLResource : public RCResource {
246 public:
163247 StringRef HTMLLoc;
164248
165 public:
166249 HTMLResource(StringRef Location) : HTMLLoc(Location) {}
167250 raw_ostream &log(raw_ostream &) const override;
251
252 Error visit(Visitor *V) const override { return V->visitHTMLResource(this); }
253
254 // Curiously, file resources don't have DISCARDABLE flag set.
255 uint16_t getMemoryFlags() const override { return MfPure | MfMoveable; }
256 IntOrString getResourceType() const override { return RkHTML; }
257 Twine getResourceTypeName() const override { return "HTML"; }
258 ResourceKind getKind() const override { return RkHTML; }
259 static bool classof(const RCResource *Res) {
260 return Res->getKind() == RkHTML;
261 }
168262 };
169263
170264 // -- MENU resource and its helper classes --
0 //===-- ResourceVisitor.h ---------------------------------------*- C++-*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===---------------------------------------------------------------------===//
8 //
9 // This defines a base class visiting resource script resources.
10 //
11 //===---------------------------------------------------------------------===//
12
13 #ifndef LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
14 #define LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
15
16 #include "llvm/Support/Error.h"
17
18 namespace llvm {
19 namespace rc {
20
21 class RCResource;
22
23 class LanguageResource;
24
25 class Visitor {
26 public:
27 virtual Error visitNullResource(const RCResource *) = 0;
28 virtual Error visitHTMLResource(const RCResource *) = 0;
29
30 virtual Error visitLanguageStmt(const LanguageResource *) = 0;
31
32 virtual ~Visitor() {}
33 };
34
35 } // namespace rc
36 } // namespace llvm
37
38 #endif
1111 //
1212 //===----------------------------------------------------------------------===//
1313
14 #include "ResourceFileWriter.h"
15 #include "ResourceScriptParser.h"
16 #include "ResourceScriptStmt.h"
1417 #include "ResourceScriptToken.h"
15 #include "ResourceScriptParser.h"
1618
1719 #include "llvm/Option/Arg.h"
1820 #include "llvm/Option/ArgList.h"
1921 #include "llvm/Support/Error.h"
22 #include "llvm/Support/FileSystem.h"
2023 #include "llvm/Support/ManagedStatic.h"
2124 #include "llvm/Support/PrettyStackTrace.h"
2225 #include "llvm/Support/Process.h"
2629 #include
2730
2831 using namespace llvm;
32 using namespace llvm::rc;
2933
3034 namespace {
3135
133137 }
134138 }
135139
140 std::unique_ptr Visitor;
141 bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN);
142
143 if (!IsDryRun) {
144 auto OutArgsInfo = InputArgs.getAllArgValues(OPT_FILEOUT);
145 if (OutArgsInfo.size() != 1)
146 fatalError(
147 "Exactly one output file should be provided (using /FO flag).");
148
149 std::error_code EC;
150 auto FOut =
151 llvm::make_unique(OutArgsInfo[0], EC, sys::fs::F_RW);
152 if (EC)
153 fatalError("Error opening output file '" + OutArgsInfo[0] +
154 "': " + EC.message());
155 Visitor = llvm::make_unique(std::move(FOut));
156
157 ExitOnErr(NullResource().visit(Visitor.get()));
158
159 // Set the default language; choose en-US arbitrarily.
160 ExitOnErr(LanguageResource(0x09, 0x01).visit(Visitor.get()));
161 }
162
136163 rc::RCParser Parser{std::move(Tokens)};
137164 while (!Parser.isEof()) {
138165 auto Resource = ExitOnErr(Parser.parseSingleResource());
139166 if (BeVerbose)
140167 Resource->log(outs());
168 if (!IsDryRun)
169 ExitOnErr(Resource->visit(Visitor.get()));
141170 }
142171
143172 return 0;