/*
* fhist - file history and comparison tools
* Copyright (C) 1998, 2002 Peter Miller;
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
* MANIFEST: functions to manipulate error_intls
*/
#include <ac/stdio.h>
#include <ac/limits.h>
#include <ac/string.h>
#include <ac/wchar.h>
#include <ac/wctype.h>
#include <error.h>
#include <error_intl.h>
#include <language.h>
#include <page.h>
#include <progname.h>
#include <quit.h>
#include <wstr.h>
/*
* NAME
* column_width - determine column width of a wide character
*
* SYNOPSIS
* int column_width(wchar_t);
*
* DESCRIPTION
* The column_width function is used to determine the column width
* if a wide character. This is particularly hard to do,
* especially if you have read the ISO C standard ammendments.
*
* WEASEL WORDS
* This is the phrase used by P.J. Plauger in his CUJ columns about
* standard C and C++, specially when the standard dances all
* around the issue, rather than actually solving anything. Take a
* squiz at these classic weasel words...
*
* In the original standard, ISO/IEC 9899:1990, 7.3 Character
* handling <ctype.h> reads as follows (3rd paragraph):
*
* The term <i>printing character</i> refers to a member of
* an implemntation defined set of characters, each of
* which occupies one printing position on a display
* device; the term <i>control character</i> refers to a
* member of an implementation defined set of characters
* that are not printing characters.
*
* The following 2 sections are from ISO/IEC 9899:1990/Amd. 1:1995 (E):
*
* 7.15.2 Wide-character classification utilities (2nd paragraph)
*
* The term <i>printing wide character</i> refers to a
* member of a locale-specific set of wide characters, each
* of which occupies at least one printing position on a
* display device; the term <i>control wide character</i>
* refers to a member of a locale-specific set of wide
* characters that are not printing wide characters.
*
* [ Notice how they weasel out by not-quite contradicting 7.3: a
* printing <i>char</i> is exactly one printing position wide, but
* a printing <i>wchar_t</i> is one or more printing positions
* wide. ]
*
* H.14 Column width
*
* The number of characters to be read or written can be
* specified in existing formatted i/o functions. On a
* traditional display device that displays characters with
* fixed pitch, the number of characters is directly
* proportional to the width occupied by the characters.
* So the display format can be specified through the field
* width and/or the precision.
*
* In formatted wide-character i/o functions, the field
* width and the precision specify the number of wide
* characters to be read or written. The number of wide
* characters is not always directly proportional to the
* width of their display. For example, with Japanese
* traditional display devices, a single-byte character
* such as an ASCII character has half the width of a Kanji
* character, even though each of them is treated as one
* wide character. To control the display format for wide
* characters, a set of formatted wide-character i/o
* functions were proposed whose metric was the column
* width instead of the character count.
*
* This proposal was supported only by Japan. Critics
* observed that the proposal was based on such traditional
* display devices with a fixed width of characters, while
* many modern display devices support a broad assortment
* of proportional pitch type faces. Hence, it was
* questioned whether the extra i/o functions in this
* proposal were really needed or were sufficiently
* general. Also considered were another set of functions
* that return the column width for any kind of display
* devices for a given wide-character string; but these
* seemed to be beyond the scope of the C language. Thus
* all proposals regarding column width were withdrawn.
*
* [ Notice how 7.15.2 specifically states that each printing
* character has a non-zero width measurable in <i>printing
* positions</i>. Why is this metric is unavailable to the
* C programmer? Presumably it is OK for an informational appendix
* to contradict the body of the standard. ]
*
* [ The section ends with a compliant-but-non-standard way a
* standard C library implementor may choose to do this. You can't
* reply on it being there, and you can't reply on the suggested
* semantics being used, so don't even bother having ./configure go
* look for it. ]
*
* SO FAKE IT
* Since there is no standard way to determine character width, we
* will have to fake it. Hopefully, locales that need it will
* define something useful. If you know of any, please let me
* know.
*/
static int
column_width(wchar_t wc)
{
#ifdef HAVE_ISWCTYPE
static int kanji_set;
static wctype_t kanji;
if (!kanji_set)
{
kanji = wctype("kanji");
kanji_set = 1;
}
if (kanji && iswctype(kanji, wc))
return 2;
#endif
return 1;
}
static int
wcs_column_width(wchar_t *wcs)
{
int result;
result = 0;
while (*wcs)
result += column_width(*wcs++);
return result;
}
/*
* NAME
* wrap - wrap s string over lines
*
* SYNOPSIS
* void wrap(wstring_ty *);
*
* DESCRIPTION
* The wrap function is used to print error messages onto stderr
* wrapping ling lines. Be very careful of multi-byte characters
* in international character sets.
*
* CAVEATS
* Line length is assumed to be 80 characters.
*/
static void
wrap(const wchar_t *s)
{
char *progname;
int page_width;
char tmp[(MAX_PAGE_WIDTH + 2) * MB_LEN_MAX];
int first_line;
char *tp;
int nbytes;
static int progname_width;
int midway;
/*
* flush any pending output,
* so the error message appears in a sensible place.
*/
if (fflush(stdout) || ferror(stdout))
nfatal_raw("standard output");
/*
* Ask the system how wide the terminal is.
* Don't use last column, many terminals are dumb.
*/
page_width = page_width_get() - 1;
midway = (page_width + 8) / 2;
/*
* Because it must be a legal UNIX file name, it is unlikely to
* be stupid - unprintable characters are hard to type, and most
* file systems don't allow high-bit-on characters in file
* names. Thus, assume progname is all legal characters.
*/
progname = progname_get();
if (!progname_width)
{
wstring_ty *ws;
ws = wstr_from_c(progname);
progname_width = wcs_column_width(ws->wstr_text);
wstr_free(ws);
}
/*
* the message is for a human, so
* use the human's locale
*/
language_human();
/*
* Emit the message a line at a time, wrapping as we go. The
* first line starts with the program name, subsequent lines are
* indented by a tab.
*/
first_line = 1;
while (*s)
{
const wchar_t *ep;
int ocol;
const wchar_t *break_space;
int break_space_col;
const wchar_t *break_punct;
int break_punct_col;
/*
* Work out how many characters fit on the line.
*/
if (first_line)
ocol = progname_width + 2;
else
ocol = 8;
wctomb(NULL, 0);
ep = s;
break_space = 0;
break_space_col = 0;
break_punct = 0;
break_punct_col = 0;
while (*ep)
{
char dummy[MB_LEN_MAX];
int cw;
wchar_t c;
/*
* Keep printing characters. Use a dummy
* character for unprintable sequences (which
* should not happen).
*/
c = *ep;
if (!iswprint(c))
c = '?';
nbytes = wctomb(dummy, c);
cw = column_width(c);
if (nbytes <= 0)
{
/*
* This should not happen! All
* unprintable characters should have
* been turned into C escapes inside the
* common/wstr.c file when converting from C
* string to wide strings.
*
* Replace invalid wide characters with
* a C escape.
*/
cw = 4;
nbytes = 4;
/*
* The wctomb state will be ``error'',
* so reset it and brave the worst. No
* need to reset the wctomb state, it is
* not broken.
*/
wctomb(NULL, 0);
}
/*
* Keep track of good places to break the line,
* but try to avoid runs of white space. There
* is a pathological case where the line is
* entirely composed of white space, but it does
* not happen often.
*/
if (c == ' ')
{
break_space = ep;
break_space_col = ocol;
while (break_space > s && break_space[-1] == ' ')
{
--break_space;
--break_space_col;
}
}
if (iswpunct(c) && ocol + cw <= page_width)
{
break_punct = ep + 1;
break_punct_col = ocol + cw;
}
/*
* if we have run out of room, break here
*/
if (ocol + cw > page_width)
break;
ocol += cw;
++ep;
}
/*
* see if there is a better place to break the line
*
* Break the line at space characters, otherwise break
* at punctuator characters. If it is possible to break
* on either a space or a punctuator, choose the space.
*
* However, if the space is in the left half of the
* line, things look very unbalanced, so break on a
* punctuator in that case.
*/
if (*ep && *ep != ' ')
{
if (break_space == s)
break_space = 0;
if
(
break_space
&&
break_punct
&&
break_space_col < midway
&&
break_punct_col >= midway
)
ep = break_punct;
else if (break_space)
ep = break_space;
else if (break_punct)
ep = break_punct;
}
/*
* print the line
*/
if (first_line)
{
strcpy(tmp, progname);
strcat(tmp, ": ");
}
else
strcpy(tmp, "\t");
tp = tmp + strlen(tmp);
/*
* Turn the input into a multi bytes chacacters.
*/
wctomb(NULL, 0);
while (s < ep)
{
wchar_t c;
/*
* Keep printing characters. Use a dummy
* character for unprintable sequences (which
* should not happen).
*/
c = *s++;
if (!iswprint(c))
c = '?';
nbytes = wctomb(tp, c);
if (nbytes <= 0)
{
/*
* This should not happen! All
* unprintable characters should have
* been turned into C escapes inside the
* wstring.c file when converting from C
* string to wide strings.
*
* Replace invalid wide characters with
* a C escape.
*/
nbytes = 4;
tp[0] = '\\';
tp[1] = '0' + ((c >> 6) & 7);
tp[2] = '0' + ((c >> 3) & 7);
tp[3] = '0' + ( c & 7);
/*
* The wctomb state will be ``error'',
* so reset it and brave the worst. No
* need to reset the wctomb state, it is
* not broken.
*/
wctomb(NULL, 0);
}
tp += nbytes;
}
/*
* Add a newline and end any outstanding shift state and
* add a NUL character.
*/
nbytes = wctomb(tp, (wchar_t)'\n');
if (nbytes > 0)
tp += nbytes;
nbytes = wctomb(tp, (wchar_t)0);
if (nbytes > 0)
tp += nbytes;
/*
* Emit the line to stderr. It is important to do this
* a whole line at a time, otherwise performance is
* terrible - stderr by default is character buffered.
*/
fputs(tmp, stderr);
if (ferror(stderr))
break;
/*
* skip leading spaces for subsequent lines
*/
while (*s == ' ')
++s;
first_line = 0;
}
/*
* done with humans
*/
language_C();
/*
* make sure nothing went wrong
*/
if (fflush(stderr) || ferror(stderr))
nfatal_raw("standard error");
}
void
error_intl(sub_context_ty *scp, const char *s)
{
wstring_ty *message;
int need_to_delete;
if (scp)
need_to_delete = 0;
else
{
scp = sub_context_new();
need_to_delete = 1;
}
message = subst_intl_wide(scp, s);
wrap(message->wstr_text);
wstr_free(message);
if (need_to_delete)
sub_context_delete(scp);
}
void
fatal_intl(sub_context_ty *scp, const char *s)
{
wstring_ty *message;
static const char *double_jeopardy;
/*
* Make sure that there isn't an infinite loop,
* if there is a problem with a substitution
* in an error message.
*/
if (double_jeopardy)
{
/*
* this error message can't be internationalized
*/
fatal_raw
(
"a fatal_intl error (\"%s\") happened while \
attempting to report an earlier fatal_intl error (\"%s\"). \
This is a probably bug.",
s,
double_jeopardy
);
}
double_jeopardy = s;
if (!scp)
scp = sub_context_new();
message = subst_intl_wide(scp, s);
wrap(message->wstr_text);
double_jeopardy = 0;
quit(1);
}
/*
* These are extra messages generated by various tools, but which must
* be translated all the same.
*/
#if 0
static void
bogus(void)
{
/* bison */
i18n("parse error");
i18n("parse error; also virtual memory exceeded");
i18n("parser stack overflow");
/* yacc */
i18n("syntax error");
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1