Commit 3de04937 authored by Thomas Roessler's avatar Thomas Roessler

Edmund Grimley Evans' UTF-8 patch.

parent ee09e5f4
......@@ -13,7 +13,12 @@ IMAP_SUBDIR = imap
IMAP_INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/imap
endif
SUBDIRS = m4 po intl doc contrib $(CHARMAP_SUBDIR) $(IMAP_SUBDIR)
if BUILD_ICONV
ICONV_SUBDIR = iconv
ICONV_INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/iconv
endif
SUBDIRS = m4 po intl doc contrib $(CHARMAP_SUBDIR) $(IMAP_SUBDIR) $(ICONV_SUBDIR)
if NEEDS_PGPEWRAP
bin_SCRIPTS = pgpewrap muttbug
......@@ -34,12 +39,13 @@ mutt_SOURCES = $(BUILT_SOURCES) \
rfc822.c rfc1524.c rfc2047.c rfc2231.c \
score.c send.c sendlib.c signal.c sort.c \
status.c system.c thread.c charset.c history.c lib.c \
muttlib.c editmsg.c
muttlib.c editmsg.c utf8.c mbyte.c wcwidth.c gettext.c
mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(MUTTLIBS) \
$(INTLLIBS)
$(INTLLIBS) $(LIBICONV)
mutt_DEPENDENCIES = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAPDEPS) $(INTLDEPS)
mutt_DEPENDENCIES = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAPDEPS) \
$(INTLDEPS) $(LIBICONVDEPS)
makedoc_SOURCES = makedoc.c
......@@ -49,7 +55,8 @@ DEFS=-DSHAREDIR=\"$(sharedir)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
-DBINDIR=\"$(bindir)\" -DHAVE_CONFIG_H=1
# top_srcdir is for building outside of the source tree
INCLUDES=-I$(top_srcdir) -I. $(IMAP_INCLUDES) -Iintl -I$(includedir)
INCLUDES=-I$(top_srcdir) -I. $(IMAP_INCLUDES) $(ICONV_INCLUDES) \
-Iintl -I$(includedir)
non_us_sources = pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1dgst.c \
gnupgparse.c sha.h sha_locl.h \
......
This diff is collapsed.
......@@ -19,35 +19,7 @@
#ifndef _CHARSET_H
#define _CHARSET_H
typedef int CHARSET_MAP[256];
typedef struct descr
{
char *symbol;
int repr;
}
CHARDESC;
typedef struct
{
char *charset;
char escape_char;
char comment_char;
short multbyte;
LIST *aliases;
}
CHARMAP;
typedef struct
{
size_t n_symb;
size_t u_symb;
short multbyte;
HASH *symb_to_repr;
CHARDESC **description;
}
CHARSET;
#include <iconv.h>
#define DECODER_BUFFSIZE 4096
......@@ -59,16 +31,14 @@ struct decoder_buff
typedef struct decoder
{
short src_is_utf8;
/*short src_is_utf8;*/
short just_take_id;
short forced;
/* used for utf-8 decoding */
CHARSET *chs;
char *outrepl;
/* conversion descriptor */
iconv_t cd;
/* used for 8-bit to 8-bit recoding */
CHARSET_MAP *chm;
/* the buffers */
struct decoder_buff in;
struct decoder_buff out;
......@@ -83,12 +53,16 @@ void mutt_decoder_pop_to_state (DECODER *, STATE *);
void mutt_free_decoder (DECODER **);
int mutt_decoder_push_one (DECODER *, char);
CHARSET *mutt_get_charset(const char *);
CHARSET_MAP *mutt_get_translation(const char *, const char *);
int mutt_display_string(char *, CHARSET_MAP *);
int mutt_is_utf8(const char *);
int mutt_recode_file (const char *, const char *, const char *);
unsigned char mutt_display_char(unsigned char, CHARSET_MAP *);
void mutt_decode_utf8_string(char *, CHARSET *);
int mutt_convert_string (char *, size_t, const char *, const char *);
size_t mutt_iconv (iconv_t, const char **, size_t *, char **, size_t *, const char **, const char *);
typedef void * FGETCONV;
FGETCONV *fgetconv_open (FILE *, const char *, const char *);
int fgetconv (FGETCONV *);
void fgetconv_close (FGETCONV *);
#endif /* _CHARSET_H */
......@@ -217,9 +217,17 @@ void ci_bounce_message (HEADER *h, int *redraw)
buf[0] = 0;
rfc822_write_address (buf, sizeof (buf), adr);
snprintf (prompt, (COLS > sizeof(prompt) ? sizeof(prompt) : COLS) - 13,
#define extra_space (15 + 7 + 2)
/*
* This is the printing width of "...? ([y=yes]/n=no): ?" plus 2
* for good measure. This is not ideal. FIXME.
*/
snprintf (prompt, sizeof (prompt),
(h ? _("Bounce message to %s") : _("Bounce messages to %s")), buf);
strcat(prompt, "...?");
mutt_format_string (prompt, sizeof (prompt),
0, COLS-extra_space, 0, 0,
prompt, sizeof (prompt));
strcat (prompt, "...?");
if (mutt_yesorno (prompt, 1) != 1)
{
rfc822_free_address (&adr);
......
......@@ -26,6 +26,7 @@
#include "mailbox.h"
#include "sort.h"
#include "charset.h"
#include "iconv.h"
#ifdef MIXMASTER
#include "remailer.h"
......@@ -67,7 +68,7 @@ enum
};
#define HDR_XOFFSET 10
#define TITLE_FMT "%10s"
#define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */
#define W (COLS - HDR_XOFFSET)
static char *Prompts[] =
......@@ -277,7 +278,8 @@ static void draw_envelope_addr (int line, ADDRESS *addr)
buf[0] = 0;
rfc822_write_address (buf, sizeof (buf), addr);
mvprintw (line, 0, TITLE_FMT "%-*.*s", Prompts[line - 1], W, W, buf);
mvprintw (line, 0, TITLE_FMT, Prompts[line - 1]);
mutt_paddstr (W, buf);
}
static void draw_envelope (HEADER *msg, char *fcc)
......@@ -286,10 +288,11 @@ static void draw_envelope (HEADER *msg, char *fcc)
draw_envelope_addr (HDR_TO, msg->env->to);
draw_envelope_addr (HDR_CC, msg->env->cc);
draw_envelope_addr (HDR_BCC, msg->env->bcc);
mvprintw (HDR_SUBJECT, 0, TITLE_FMT "%-*.*s", Prompts[HDR_SUBJECT - 1], W, W,
NONULL(msg->env->subject));
mvprintw (HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
mutt_paddstr (W, NONULL (msg->env->subject));
draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
mvprintw (HDR_FCC, 0, TITLE_FMT "%-*.*s", Prompts[HDR_FCC - 1], W, W, fcc);
mvprintw (HDR_FCC, 0, TITLE_FMT, Prompts[HDR_FCC - 1]);
mutt_paddstr (W, fcc);
......@@ -331,7 +334,8 @@ static int edit_address_list (int line, ADDRESS **addr)
/* redraw the expanded list so the user can see the result */
buf[0] = 0;
rfc822_write_address (buf, sizeof (buf), *addr);
mvprintw (line, HDR_XOFFSET, "%-*.*s", W, W, buf);
move (line, HDR_XOFFSET);
mutt_paddstr (W, buf);
return 0;
}
......@@ -385,6 +389,7 @@ static void update_idx (MUTTMENU *menu, ATTACHPTR **idx, short idxlen)
static int change_attachment_charset (BODY *b)
{
char buff[SHORT_STRING];
iconv_t cd;
if (!mutt_is_text_type (b->type, b->subtype))
{
......@@ -396,20 +401,14 @@ static int change_attachment_charset (BODY *b)
if (mutt_get_field (_("Enter character set: "), buff, sizeof(buff), 0) == -1)
return 0;
if (mutt_is_utf8(buff))
{
if (!b->noconv)
{
mutt_error (_("UTF-8 encoding attachments has not yet been implemented."));
return 0;
}
}
else if (mutt_get_charset (buff) == NULL)
if ((cd = iconv_open (buff, "us-ascii")) == (iconv_t)-1)
{
mutt_error (_("Character set %s is unknown."), buff);
return 0;
}
else
iconv_close (cd);
mutt_set_body_charset (b, buff);
return REDRAW_CURRENT;
......@@ -488,7 +487,7 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
move (HDR_SUBJECT, HDR_XOFFSET);
clrtoeol ();
if (msg->env->subject)
printw ("%-*.*s", W, W, msg->env->subject);
mutt_paddstr (W, msg->env->subject);
}
break;
case OP_COMPOSE_EDIT_REPLY_TO:
......@@ -500,7 +499,8 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
{
strfcpy (fcc, buf, _POSIX_PATH_MAX);
mutt_pretty_mailbox (fcc);
mvprintw (HDR_FCC, HDR_XOFFSET, "%-*.*s", W, W, fcc);
move (HDR_FCC, HDR_XOFFSET);
mutt_paddstr (W, fcc);
fccSet = 1;
}
MAYBE_REDRAW (menu->redraw);
......@@ -765,11 +765,6 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
mutt_error (_("Recoding only affects text attachments."));
break;
}
if (mutt_is_utf8 (mutt_get_parameter ("charset", CURRENT->parameter)))
{
mutt_error (_("We currently can't encode to utf-8."));
break;
}
CURRENT->noconv = !CURRENT->noconv;
if (CURRENT->noconv)
mutt_message (_("The current attachment won't be converted."));
......
......@@ -382,6 +382,44 @@ AC_ARG_WITH(sharedir, [ --with-sharedir=PATH Specify where to put arch in
sharedir=$mutt_cv_sharedir
AC_SUBST(sharedir)
AC_ARG_WITH(iconv, [ --with-iconv Use system's iconv], [
AC_CACHE_CHECK(for iconv, mutt_cv_func_iconv,
[ mutt_cv_func_iconv=no
mutt_cv_lib_iconv=no
AC_TRY_LINK([#include <stdlib.h>
#include <iconv.h>],
[iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);],
mutt_cv_func_iconv=yes)
if test "$mutt_cv_func_iconv" = no; then
mutt_save_LIBS="$LIBS"
LIBS="$LIBS -liconv"
AC_TRY_LINK([#include <stdlib.h>
#include <iconv.h>],
[iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);],
mutt_cv_lib_iconv=yes
mutt_cv_func_iconv=yes)
LIBS="$mutt_save_LIBS"
fi
])
if test "$mutt_cv_func_iconv" = no; then
AC_MSG_ERROR(No iconv)
fi
LIBICONV=
if test "$mutt_cv_lib_iconv" = yes; then
LIBICONV="-liconv"
fi
need_iconv=no
],
[ LIBICONV="-Liconv -liconv"
LIBICONVDEPS="\$(top_srcdir)/iconv/iconv.h iconv/libiconv.a"
need_iconv=yes
])
AM_CONDITIONAL(BUILD_ICONV, test "$need_iconv" = yes)
AC_SUBST(LIBICONV)
AC_SUBST(LIBICONVDEPS)
if test "$need_iconv" = yes ; then
mutt_cv_charmaps=/usr/share/i18n/charmaps
AC_ARG_WITH(charmaps, [ --with-charmaps=PATH Where to find character set definitions],
[if test x$withval != x -a $withval != yes ; then
......@@ -410,6 +448,8 @@ charmaps=$mutt_cv_charmaps
AC_SUBST(charmaps)
AM_CONDITIONAL(BUILD_CHARMAPS, test x$need_charmaps = xyes)
fi # /* need_iconv */
AC_ARG_WITH(docdir, [ --with-docdir=PATH Specify where to put the documentation],
[mutt_cv_docdir=$withval],
[ AC_CACHE_CHECK(where to put the documentation,
......@@ -614,5 +654,5 @@ AC_DEFINE_UNQUOTED(MUTTLOCALEDIR, "$MUTTLOCALEDIR")
AC_OUTPUT(Makefile intl/Makefile m4/Makefile dnl
po/Makefile.in doc/Makefile contrib/Makefile dnl
muttbug.sh dnl
charmaps/Makefile imap/Makefile dnl
charmaps/Makefile imap/Makefile iconv/Makefile dnl
Muttrc.head)
......@@ -20,6 +20,7 @@
#include "mutt_menu.h"
#include "mutt_curses.h"
#include "pager.h"
#include "mbyte.h"
#include <termios.h>
#include <sys/types.h>
......@@ -93,14 +94,15 @@ event_t mutt_getch (void)
int _mutt_get_field (/* const */ char *field, char *buf, size_t buflen, int complete, int multiple, char ***files, int *numfiles)
{
int ret;
int len = mutt_strlen (field); /* in case field==buffer */
int x, y;
do
{
CLEARLINE (LINES-1);
addstr (field);
mutt_refresh ();
ret = _mutt_enter_string ((unsigned char *) buf, buflen, LINES-1, len, complete, multiple, files, numfiles);
getyx (stdscr, y, x);
ret = _mutt_enter_string ((unsigned char *) buf, buflen, y, x, complete, multiple, files, numfiles);
}
while (ret == 1);
CLEARLINE (LINES-1);
......@@ -142,10 +144,23 @@ int mutt_yesorno (const char *msg, int def)
event_t ch;
unsigned char *yes = (unsigned char *) _("yes");
unsigned char *no = (unsigned char *) _("no");
char yes1 = 'y';
char no1 = 'n';
/*
* The keys are not localised, because none of the other
* keys in mutt are localised. Also, non-ASCII characters
* are unlikely to work at present ...
*/
CLEARLINE(LINES-1);
printw("%s ([%c]/%c): ", msg, def ? *yes : *no,
def ? *no : *yes);
if (*yes == yes1 && *no == no1) /* English, or not localised */
printw ("%s ([%c]/%c): ", msg, def ? yes1 : no1,
def ? no1 : yes1);
else
printw ("%s ([%c=%s]/%c=%s): ", msg,
def ? yes1 : no1, def ? yes : no,
def ? no1 : yes1, def ? no : yes);
FOREVER
{
mutt_refresh ();
......@@ -153,12 +168,12 @@ int mutt_yesorno (const char *msg, int def)
if (ch.ch == -1) return(-1);
if (CI_is_return (ch.ch))
break;
else if (tolower(ch.ch) == tolower(*yes))
else if (tolower (ch.ch) == tolower (yes1))
{
def = 1;
break;
}
else if (tolower(ch.ch) == tolower(*no))
else if (tolower (ch.ch) == tolower (no1))
{
def = 0;
break;
......@@ -190,16 +205,6 @@ void mutt_query_exit (void)
SigInt = 0;
}
static void clean_error_buf(void)
{
char *s;
for(s = Errorbuf; *s; s++)
{
if(!IsPrint(*s))
*s = '.';
}
}
void mutt_curses_error (const char *fmt, ...)
{
va_list ap;
......@@ -209,8 +214,8 @@ void mutt_curses_error (const char *fmt, ...)
va_end (ap);
dprint (1, (debugfile, "%s\n", Errorbuf));
Errorbuf[ (COLS < sizeof (Errorbuf) ? COLS : sizeof (Errorbuf)) - 2 ] = 0;
clean_error_buf();
mutt_format_string (Errorbuf, sizeof (Errorbuf),
0, COLS-2, 0, 0, Errorbuf, sizeof (Errorbuf));
if (!option (OPTKEEPQUIET))
{
......@@ -233,8 +238,8 @@ void mutt_message (const char *fmt, ...)
vsnprintf (Errorbuf, sizeof (Errorbuf), fmt, ap);
va_end (ap);
Errorbuf[ (COLS < sizeof (Errorbuf) ? COLS : sizeof (Errorbuf)) - 2 ] = 0;
clean_error_buf();
mutt_format_string (Errorbuf, sizeof (Errorbuf),
0, COLS-2, 0, 0, Errorbuf, sizeof (Errorbuf));
if (!option (OPTKEEPQUIET))
{
......@@ -455,3 +460,105 @@ int mutt_multi_choice (char *prompt, char *letters)
mutt_refresh ();
return choice;
}
/*
* addwch would be provided by an up-to-date curses library
*/
int mutt_addwch (wchar_t wc)
{
char buf[6]; /* FIXME */
int n;
n = mutt_wctomb (buf, wc);
if (n == -1)
return n;
else
return addnstr (buf, n);
}
/*
* This formats a string, a bit like
* snprintf (dest, destlen, "%-*.*s", min_width, max_width, s),
* except that the widths refer to the number of character cells
* when printed.
*/
void mutt_format_string (char *dest, size_t destlen,
int min_width, int max_width,
int right_justify, char pad_char,
const char *s, size_t n)
{
char *p;
wchar_t wc;
int w, k;
--destlen;
p = dest;
while ((k = mbtowc (&wc, s, n)))
{
if (k == -1 && n > 0)
{
k = 1;
wc = replacement_char ();
}
s += k, n -= k;
w = wc < M_TREE_MAX ? 1 : wcwidth (wc); /* hack */
if (w >= 0)
{
if (w > max_width || wctomb (0, wc) > destlen)
break;
min_width -= w;
max_width -= w;
p += (k = wctomb (p, wc));
destlen -= k;
}
}
k = (int)destlen < min_width ? destlen : min_width;
if (k <= 0)
*p = '\0';
else if (right_justify)
{
p[k] = '\0';
while (--p >= dest)
p[k] = *p;
while (--k >= 0)
dest[k] = pad_char;
}
else
{
while (--k >= 0)
*p++ = pad_char;
*p = '\0';
}
}
/*
* mutt_paddstr (n, s) is equivalent to
* mutt_format_string (bigbuf, big, n, n, 0, ' ', s, big), addstr (bigbuf)
*/
void mutt_paddstr (int n, const char *s)
{
wchar_t wc;
int k, w;
while ((k = mbtowc (&wc, s, -1)))
{
if (k == -1)
{
++s; /* skip ill-formed character */
continue;
}
if ((w = wcwidth (wc)) >= 0)
{
if (w > n)
break;
addnstr ((char *)s, k);
n -= w;
}
s += k;
}
while (n-- > 0)
addch (' ');
}
......@@ -521,7 +521,7 @@ int mutt_index_menu (void)
menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES-2);
SETCOLOR (MT_COLOR_STATUS);
printw ("%-*.*s", COLS, COLS, buf);
mutt_paddstr (COLS, buf);
SETCOLOR (MT_COLOR_NORMAL);
menu->redraw &= ~REDRAW_STATUS;
}
......
......@@ -124,8 +124,11 @@ int _mutt_enter_string (unsigned char *buf, size_t buflen, int y, int x,
j = begin;
}
move (y, x + j - begin);
for (; j < lastchar && j < begin + width; j++)
ADDCH (buf[j]);
{
int n = (lastchar < begin + width) ? lastchar : begin + width;
n = (n > j) ? n - j : 0;
addnstr ((char *)&buf[j], n);
}
clrtoeol ();
if (redraw != M_REDRAW_INIT)
move (y, x + curpos - begin);
......
#include <string.h>
#include "mutt.h"
#include "iconv.h"
#include "lib.h"
#include "charset.h"
/*
* One day, gettext will return strings in the appropriate
* encoding. In the meantime, we use this code to handle
* the conversion.
*/
struct gt_hash_elem
{
const char *key;
char *data;
struct gt_hash_elem *next;
};
#define gt_hash_size 127
static char *get_charset (const char *header)
{
/* FIXME: the comparison should at least be case-insensitive */
const char f[] = "\nContent-Type: text/plain; charset=";
char *charset, *i, *j;
charset = 0;
i = strstr (header, f);
if (i)
{
i += sizeof (f)-1;
for (j = i; *j >= 32; j++)
;
charset = safe_malloc (j-i+1);
memcpy (charset, i, j-i);
charset[j-i] = '\0';
}
return charset;
}
char *mutt_gettext (const char *message)
{
static struct gt_hash_elem **messages = 0;
static char *po_header = 0;
static char *po_charset = 0;
static char *message_charset = 0;
static char *outrepl = "?";
static iconv_t cd = (iconv_t)-1;
int change_cd = 0;
char *t, *orig;
char *header_msgid = "";
/* gettext ("") doesn't work due to __builtin_constant_p optimisation */
if ((t = gettext (header_msgid)) != po_header)
{
po_header = t;
t = get_charset (po_header);
if (t != po_charset &&
(!t || !po_charset || strcmp (t, po_charset)))
{
free (po_charset);
po_charset = t;
change_cd = 1;
}
else
free (t);
}
if (message_charset != Charset &&
(!message_charset || !Charset || strcmp (message_charset, Charset)))
{
free (message_charset);
if (Charset)
{
int n = strlen (Charset);
message_charset = safe_malloc (n+1);
memcpy (message_charset, Charset, n+1);
}
else
message_charset = 0;
outrepl = mutt_is_utf8 (message_charset) ? "\357\277\275" : "?";
change_cd = 1;
}
if (change_cd)
{
if (cd != (iconv_t)-1)
iconv_close (cd);
if (message_charset)
cd = iconv_open (message_charset, po_charset ? po_charset : "UTF-8");
else
cd = (iconv_t)-1;
if (messages)
{
int i;
struct gt_hash_elem *p, *pn;
for (i = 0; i < gt_hash_size; i++)
for (p = messages[i]; p; p = pn)
{
pn = p->next;
free (p);
}
free (messages);
messages = 0;
}
}
orig = gettext (message);
if (cd == (iconv_t)-1)
return orig;
else
{
struct gt_hash_elem *p;
int hash;
char *s, *t;
int n, nn;
const char *ib;
char *ob;
size_t ibl, obl;
if (!messages)
{
messages = safe_malloc (gt_hash_size * sizeof (*messages));
memset (messages, 0, gt_hash_size * sizeof (*messages));
}
hash = (long int)orig % gt_hash_size; /* not very clever */
for (p = messages[hash]; p && p->key != orig; p = p->next)
;
if (p)
return p->data;
n = strlen (orig);
nn = n + 1;
t = safe_malloc (nn);
ib = orig, ibl = n;
ob = t, obl = n;
for (;;)
{
mutt_iconv (cd, &ib, &ibl, &ob, &obl, 0, outrepl);
if (!ibl || obl > 256)
break;
s = t;
safe_realloc ((void **)&t, nn += n);
ob += t - s;
obl += n;
}
*ob = '\0';
n = strlen (t);
s = safe_malloc (n+1);
memcpy (s, t, n+1);
free (t);
p = safe_malloc (sizeof (struct gt_hash_elem));
p->key = orig;
p->data = s;
p->next = messages[hash];
messages[hash] = p;
return s;
}
}
......@@ -44,6 +44,7 @@
#include "mutt.h"
#include "pgp.h"
#include "charset.h"
#include "iconv.h"
/* for hexval */
#include "mime.h"
......@@ -67,12 +68,13 @@
/* decode the backslash-escaped user ids. */
static CHARSET *_chs;
static char *_chs = 0;
static void fix_uid (char *uid)
{
char *s, *d;
iconv_t cd;