Treat batch stdout/stderr like standard display

Calls like (print FOO) could generate improperly encoded or
hard-to-read output if FOO contains characters outside the system
locale.  Fix this by treating batch stdout and stderr like
interactive standard display, when it comes to transliterating and
encoding characters (Bug#20545).
* doc/emacs/mule.texi (Communication Coding):
* doc/lispref/display.texi (Active Display Table):
* doc/lispref/nonascii.texi (Locales):
* etc/NEWS:
* src/coding.c (syms_of_coding):
* src/dispnew.c (syms_of_display):
Document this.
* src/print.c: Include disptab.h.
(printchar_to_stream): New function, with much of the guts of the
old Fexternal_debugging_output, except this one also uses the
standard display table.
(printchar, strout, Fexternal_debugging_output): Use it.
This commit is contained in:
Paul Eggert 2015-05-31 20:04:05 -07:00
parent 21d13bce38
commit 467af178f5
7 changed files with 90 additions and 36 deletions

View file

@ -1164,7 +1164,9 @@ current language environment.
to use when encoding and decoding system strings such as system error
messages and @code{format-time-string} formats and time stamps. That
coding system is also used for decoding non-@acronym{ASCII} keyboard
input on the X Window System. You should choose a coding system that is compatible
input on the X Window System and for encoding text sent to the
standard output and error streams when in batch mode. You should
choose a coding system that is compatible
with the underlying system's text representation, which is normally
specified by one of the environment variables @env{LC_ALL},
@env{LC_CTYPE}, and @env{LANG}. (The first one, in the order

View file

@ -6522,8 +6522,9 @@ no buffer display table.
@defvar standard-display-table
The value of this variable is the standard display table, which is
used when Emacs is displaying a buffer in a window with neither a
window display table nor a buffer display table defined. Its default
is @code{nil}.
window display table nor a buffer display table defined, or when Emacs
is outputting text to the standard output or error streams. Its
default is @code{nil}.
@end defvar
The @file{disp-table} library defines several functions for changing

View file

@ -1959,6 +1959,7 @@ how Emacs interacts with these features.
@cindex keyboard input decoding on X
This variable specifies the coding system to use for decoding system
error messages and---on X Window system only---keyboard input, for
sending batch output to the standard output and error streams, for
encoding the format argument to @code{format-time-string}, and for
decoding the return value of @code{format-time-string}.
@end defvar

View file

@ -947,6 +947,12 @@ integers.
** New function `set-binary-mode' allows to switch a standard stream
of the Emacs process to binary I/O mode.
** In locales that cannot display curved quotes, ASCII approximations
are installed in standard-display-table.
** Standard output and error streams now transliterate characters via
standard-display-table, and encode output using locale-coding-system.
** Miscellaneous name change
For consistency with the usual Emacs spelling, the Lisp variable

View file

@ -11121,7 +11121,8 @@ See also the function `find-operation-coding-system'. */);
DEFVAR_LISP ("locale-coding-system", Vlocale_coding_system,
doc: /* Coding system to use with system messages.
Also used for decoding keyboard input on X Window system. */);
Also used for decoding keyboard input on X Window system, and for
encoding standard output and error streams. */);
Vlocale_coding_system = Qnil;
/* The eol mnemonics are reset in startup.el system-dependently. */

View file

@ -6245,6 +6245,7 @@ Each element can be:
DEFVAR_LISP ("standard-display-table", Vstandard_display_table,
doc: /* Display table to use for buffers that specify none.
It is also used for standard output and error streams.
See `buffer-display-table' for more information. */);
Vstandard_display_table = Qnil;

View file

@ -31,6 +31,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "window.h"
#include "process.h"
#include "dispextern.h"
#include "disptab.h"
#include "termchar.h"
#include "intervals.h"
#include "blockinput.h"
@ -195,6 +196,61 @@ print_unwind (Lisp_Object saved_text)
memcpy (print_buffer, SDATA (saved_text), SCHARS (saved_text));
}
/* Print character CH to the stdio stream STREAM. */
static void
printchar_to_stream (unsigned int ch, FILE *stream)
{
Lisp_Object dv IF_LINT (= Qnil);
ptrdiff_t i = 0, n = 1;
if (CHAR_VALID_P (ch) && DISP_TABLE_P (Vstandard_display_table))
{
dv = DISP_CHAR_VECTOR (XCHAR_TABLE (Vstandard_display_table), ch);
if (VECTORP (dv))
{
n = ASIZE (dv);
goto next_char;
}
}
while (true)
{
if (ASCII_CHAR_P (ch))
{
putc (ch, stream);
#ifdef WINDOWSNT
/* Send the output to a debugger (nothing happens if there
isn't one). */
if (print_output_debug_flag && stream == stderr)
OutputDebugString ((char []) {ch, '\0'});
#endif
}
else
{
unsigned char mbstr[MAX_MULTIBYTE_LENGTH];
int len = CHAR_STRING (ch, mbstr);
Lisp_Object encoded_ch =
ENCODE_SYSTEM (make_multibyte_string ((char *) mbstr, 1, len));
fwrite (SSDATA (encoded_ch), 1, SBYTES (encoded_ch), stream);
#ifdef WINDOWSNT
if (print_output_debug_flag && stream == stderr)
OutputDebugString (SSDATA (encoded_ch));
#endif
}
i++;
next_char:
for (; i < n; i++)
if (CHARACTERP (AREF (dv, i)))
break;
if (! (i < n))
break;
ch = XFASTINT (AREF (dv, i));
}
}
/* Print character CH using method FUN. FUN nil means print to
print_buffer. FUN t means print to echo area or stdout if
@ -226,7 +282,10 @@ printchar (unsigned int ch, Lisp_Object fun)
else if (noninteractive)
{
printchar_stdout_last = ch;
fwrite (str, 1, len, stdout);
if (DISP_TABLE_P (Vstandard_display_table))
printchar_to_stream (ch, stdout);
else
fwrite (str, 1, len, stdout);
noninteractive_need_newline = 1;
}
else
@ -267,7 +326,19 @@ strout (const char *ptr, ptrdiff_t size, ptrdiff_t size_byte,
}
else if (noninteractive && EQ (printcharfun, Qt))
{
fwrite (ptr, 1, size_byte, stdout);
if (DISP_TABLE_P (Vstandard_display_table))
{
int len;
for (ptrdiff_t i = 0; i < size_byte; i += len)
{
int ch = STRING_CHAR_AND_LENGTH ((const unsigned char *) ptr + i,
len);
printchar_to_stream (ch, stdout);
}
}
else
fwrite (ptr, 1, size_byte, stdout);
noninteractive_need_newline = 1;
}
else if (EQ (printcharfun, Qt))
@ -688,37 +759,8 @@ You can call print while debugging emacs, and pass it this function
to make it write to the debugging output. */)
(Lisp_Object character)
{
unsigned int ch;
CHECK_NUMBER (character);
ch = XINT (character);
if (ASCII_CHAR_P (ch))
{
putc (ch, stderr);
#ifdef WINDOWSNT
/* Send the output to a debugger (nothing happens if there isn't
one). */
if (print_output_debug_flag)
{
char buf[2] = {(char) XINT (character), '\0'};
OutputDebugString (buf);
}
#endif
}
else
{
unsigned char mbstr[MAX_MULTIBYTE_LENGTH];
ptrdiff_t len = CHAR_STRING (ch, mbstr);
Lisp_Object encoded_ch =
ENCODE_SYSTEM (make_multibyte_string ((char *) mbstr, 1, len));
fwrite (SSDATA (encoded_ch), SBYTES (encoded_ch), 1, stderr);
#ifdef WINDOWSNT
if (print_output_debug_flag)
OutputDebugString (SSDATA (encoded_ch));
#endif
}
printchar_to_stream (XINT (character), stderr);
return character;
}