Skip to content

Save history independently of the locale encoding - #1277

Open
ihamzatarar wants to merge 1 commit into
ruby:masterfrom
ihamzatarar:save-history-independently-of-locale-encoding
Open

ihamzatarar wants to merge 1 commit into
ruby:masterfrom
ihamzatarar:save-history-independently-of-locale-encoding

Conversation

@ihamzatarar

Copy link
Copy Markdown

Fixes #1023.

Under a locale that cannot represent what was typed, IRB aborts on exit and the session's history is thrown away:

$ LANG=en_US.ASCII irb
irb(main):001> puts 'héllo'
héllo
irb(main):002> exit
.../lib/irb/history.rb:68:in 'IO#puts': U+00E9 from UTF-8 to US-ASCII (Encoding::UndefinedConversionError)

save_history opens the file with encoding: IRB.conf[:LC_MESSAGES]&.encoding, which resolves to US-ASCII here, so f.puts(hist) has to transcode and raises on the first character the locale has no room for. The two rescues below it cover Errno::ENOENT, Errno::EACCES and Errno::EPERM, so this one escapes. save_history is called from the ensure block in IRB.run, which is why the whole session goes rather than just the write.

l.scrub on the line above does not help: it repairs invalid bytes, and these bytes are perfectly valid UTF-8. There is simply no US-ASCII character to transcode them to.

This writes the entries in the encoding they already carry, so nothing has to be converted and nothing is lost. The history file is IRB's own file, so there is no external consumer whose expectations the byte encoding has to match.

Scope, since it is worth being explicit about what this does not touch. load_history has the mirror problem one line up, at File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}"): an existing UTF-8 history file read back under an ASCII locale comes through with its characters mangled. That path is covered today by test_history_different_encodings, which asserts the mangling, so changing it would mean deciding that the current behaviour is wrong as well. Reading is lossy but does not raise, and the crash and the data loss on exit come entirely from the write. I have kept this change to that and am happy to follow up on the read side separately if you would like it changed.

The new test puts a non-ASCII entry in the history with LC_MESSAGES set to en_US.ASCII and asserts the save does not raise and the entry survives. On master it fails with Encoding::UndefinedConversionError: U+00E9 from UTF-8 to US-ASCII.

The full suite goes from 402 tests and 2483 assertions to 403 and 2484, with the same single pre-existing failure either way (test_use_tracer_enabled_when_gem_is_available, which needs the tracer gem).

Opening the history file with the locale's encoding makes f.puts raise
Encoding::UndefinedConversionError for any entry holding a character the
locale cannot represent. The rescues around it only cover Errno, so the
exception escapes save_history, which runs from the ensure block in
IRB.run, and IRB aborts on exit with the whole session discarded.

Write the entries in the encoding they already carry instead.

Fixes ruby#1023

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UndefinedConversionError when LANG and default encoding are different

1 participant