Skip to content

Commit 00d0f31

Browse files
committed
feat!: throw an error on UTF-16 or UTF-32 BOM
BREAKING CHANGE: UTF-16 and UTF-32 files that have the byte-order mark are no longer passed through with `-p`/`--pass-through`. v0.16.0
1 parent c75ea8e commit 00d0f31

3 files changed

Lines changed: 42 additions & 9 deletions

File tree

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,11 @@ Commands act on all of them at the same time.
220220
### Text encodings
221221

222222
Initool is encoding-naive and assumes one character is one byte.
223-
It correctly processes UTF-8-encoded files when given UTF-8 command-line arguments but can't open files in UTF-16 or UTF-32.
224-
On Windows, it will receive the command-line arguments in the encoding for your system's language for non-Unicode programs (e.g., [Windows-1252](https://en.wikipedia.org/wiki/Windows-1252)),
223+
It correctly processes UTF-8-encoded files when given UTF-8 command-line arguments.
224+
It exits with an encoding error if it detects the UTF-16 or UTF-32 [BOM](https://en.wikipedia.org/wiki/Byte_order_mark).
225+
Trying to open a UTF-16 or UTF-32 file without the BOM results in an "invalid line" error because initool is unable to parse it.
226+
227+
On Windows, initool will receive the command-line arguments in the encoding for your system's language for non-Unicode programs (e.g., [Windows-1252](https://en.wikipedia.org/wiki/Windows-1252)),
225228
which limits what you can do with UTF-8-encoded files.
226229

227230

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.15.0
1+
0.16.0

initool.sml

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,29 @@
33
* License: MIT
44
*)
55

6-
type options = {ignoreCase: bool, passThrough: bool}
6+
exception Encoding of string
77

8-
fun idOptions (opts: options) : Id.options = {ignoreCase = #ignoreCase opts}
8+
val unsupportedEncoding = "unsupported encoding: "
9+
10+
fun checkWrongEncoding (lines: string list) =
11+
let
12+
val _ =
13+
case lines of
14+
[first] =>
15+
(case map Char.ord (String.explode first) of
16+
0x00 :: 0x00 :: 0xFE :: 0xFF :: _ =>
17+
raise Encoding (unsupportedEncoding ^ "UTF-32 BE")
18+
| 0xFF :: 0xFE :: 0x00 :: 0x00 :: _ =>
19+
raise Encoding (unsupportedEncoding ^ "UTF-32 LE")
20+
| 0xFE :: 0xFF :: _ =>
21+
raise Encoding (unsupportedEncoding ^ "UTF-16 BE")
22+
| 0xFF :: 0xFE :: _ =>
23+
raise Encoding (unsupportedEncoding ^ "UTF-16 LE")
24+
| _ => ())
25+
| _ => ()
26+
in
27+
lines
28+
end
929

1030
fun readLines (filename: string) : string list =
1131
let
@@ -44,7 +64,8 @@ datatype result = Output of string | FailureOutput of string | Error of string
4464

4565
fun processFileCustom quiet passThrough successFn filterFn filename =
4666
let
47-
val parsed = Ini.parse passThrough (readLines filename)
67+
val parsed =
68+
((Ini.parse passThrough) o checkWrongEncoding o readLines) filename
4869
val filtered = filterFn parsed
4970
val success = successFn (parsed, filtered)
5071
val output = if quiet then "" else Ini.stringify filtered
@@ -100,13 +121,17 @@ fun helpCommand [] = Output allUsage
100121
Error (invalidUsage ^ (formatArgs (cmd :: rest)) ^ "\n" ^ usage ^ cmd)
101122

102123
fun versionCommand [] =
103-
let val version = "0.15.0"
124+
let val version = "0.16.0"
104125
in Output (version ^ "\n")
105126
end
106127
| versionCommand [_] = versionCommand []
107128
| versionCommand (cmd :: rest) =
108129
Error (invalidUsage ^ (formatArgs (cmd :: rest)) ^ "\n" ^ usage ^ cmd)
109130

131+
type options = {ignoreCase: bool, passThrough: bool}
132+
133+
fun idOptions (opts: options) : Id.options = {ignoreCase = #ignoreCase opts}
134+
110135
fun getCommand (opts: options) [_, filename] =
111136
processFile (#passThrough opts) (fn _ => true) (fn x => x) filename
112137
| getCommand opts [_, filename, section] =
@@ -139,7 +164,7 @@ fun getCommand (opts: options) [_, filename] =
139164
val q = Ini.SelectProperty {section = section, key = key}
140165
val parsed =
141166
((Ini.select (idOptions opts) q) o (Ini.parse (#passThrough opts))
142-
o readLines) filename
167+
o checkWrongEncoding o readLines) filename
143168
val allItems = List.concat
144169
(List.map (fn {name = _, contents = xs} => xs) parsed)
145170
val values =
@@ -275,11 +300,16 @@ fun processArgs (opts: options) [] = helpCommand []
275300
| processArgs opts (cmd :: _) =
276301
Error (unknownCommand ^ (formatArgs [cmd]) ^ "\n" ^ availableCommands)
277302

303+
fun handleException (message: string) =
304+
exitWithError "" ("Error: " ^ message)
305+
278306
val args = CommandLine.arguments ()
279307

280308
val result =
281309
processArgs {ignoreCase = false, passThrough = false} args
282-
handle Ini.Tokenization (message) => exitWithError "" ("Error: " ^ message)
310+
handle
311+
Encoding message => handleException message
312+
| Ini.Tokenization message => handleException message
283313
val _ =
284314
case result of
285315
Output s => printFlush TextIO.stdOut s

0 commit comments

Comments
 (0)