Consider this from today's current:
$ units -t ft in
$ echo "$(units -t ft in)"
When running 'units' from within $(), the terminal becomes unusable; note the staircas effect after the second invocation. Carriage returns do not work any longer, nor do control characters such as Ctrl+D. "reset" fixes the carriage return issue, but not everything: for example, backspace cannot work any longer.
I have tried this from both the "physical console" (in VMware), a remote session from OS X's Terminal.app, and from within tmux from such remote session. (Do not try on the physical console; getty gets confused once you log out.)
I somehow doubt this is a units(1) issue and suspect the problem is coming from libedit, but I'm filing this as a units(1) issue because that's how the problem can be reproduced.
(As a related issue, "units -t" should not need to initialize libedit at all -- but it's doing it. Removing that code path "hides" the problem.)
A commit references this bug:
Date: Sun Jun 28 16:43:08 UTC 2015
New revision: 284912
Only initialize libedit when necessary
The code path to support units conversions from the command line
need not initialize neither libedit nor the history. Therefore, only do
that when in interactive mode.
This hides the issue reported in PR bin/201167 whereby running commands
of the form 'echo "$(units ft in)"' would corrupt the terminal. The real
issue causing the corruption most likely still remains somewhere.
Differential Revision: D2935
Reviewed by: eadler
The specific issue described above has been fixed, but the problems with libedit (or whatever it is) "corrupting" the terminal still remain. I haven't debugged them further though, so keeping this bug open.
Just to make things clear, I could reproduce this from:
* The physical console (as exposed by VMware).
* A remote SSH session from OS X's Terminal.app (TERM=xterm).
* A tmux session within the remote session above.
All with LANG=C LC_ALL=C LC_COLLATE=C.
The corruption is because units does not shut down libedit via el_end(). The el_end() call is dead code, since the loop exits via exit(), not break.
Other issues: the (inhistory == 0) check is dead code since it has already crashed before that point; it should be moved directly after the history_init() call. Also, the history_end() call should probably be after the el_end() call, not before.