Commit 0575858a 0575858a61c4ffcdb4fad7bae6853992e3d43913 by Sergey Poznyakoff

Rewritten output routines using FriBidi. Arabic

subject lines look almost OK, except that all characters are
displayed in isolated variant.
1 parent 8206377e
Showing 1 changed file with 307 additions and 162 deletions
......@@ -42,6 +42,10 @@
#include <mbswidth.h>
#include <xalloc.h>
#ifdef HAVE_FRIBIDI_FRIBIDI_H
# include <fribidi/fribidi.h>
#endif
#include <mailutils/address.h>
#include <mailutils/argp.h>
#include <mailutils/attribute.h>
......@@ -80,6 +84,7 @@ static int select_attribute;
static int selected;
static int action (observer_t, size_t);
void init_output (size_t s);
const char *program_version = "frm (" PACKAGE_STRING ")";
static char doc[] = N_("GNU frm -- display From: lines");
......@@ -182,6 +187,36 @@ decode_attr (char *arg)
/* Get the number of columns on the screen
First try an ioctl() call, not all shells set the COLUMNS environ.
If ioctl does not succeed on stdout, try it on /dev/tty, as we
may work via a pipe.
This function was taken from mail/util.c. It should probably reside
in the library */
int
util_getcols (void)
{
struct winsize ws;
ws.ws_col = ws.ws_row = 0;
if (ioctl (1, TIOCGWINSZ, (char *) &ws) < 0)
{
int fd = open ("/dev/tty", O_RDWR);
ioctl (fd, TIOCGWINSZ, (char *) &ws);
close (fd);
}
if (ws.ws_row == 0)
{
const char *columns = getenv ("COLUMNS");
if (columns)
ws.ws_col = strtol (columns, NULL, 10);
}
return ws.ws_col;
}
static struct argp_option options[] = {
{"debug", 'd', NULL, 0, N_("Enable debugging output"), 0},
{"field", 'f', N_("NAME"), 0, N_("Header field to display"), 0},
......@@ -195,6 +230,237 @@ static struct argp_option options[] = {
{0, 0, 0, 0}
};
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
switch (key)
{
case 'd':
dbug++;
break;
case 'f':
show_field = arg;
align = 0;
break;
case 'l':
show_to = 1;
break;
case 'n':
show_number = 1;
break;
case 'Q':
/* Very silent. */
be_quiet += 2;
break;
case 'q':
be_quiet++;
show_query = 1;
break;
case 'S':
show_summary = 1;
break;
case 's':
select_attribute = decode_attr (arg);
break;
case 't':
align = 1;
break;
case ARGP_KEY_FINI:
{
size_t s;
if (align && (s = util_getcols ()))
init_output (s);
else
init_output (0);
}
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = {
options,
parse_opt,
N_("[URL ...]"),
doc,
NULL,
NULL, NULL
};
static const char *frm_argp_capa[] = {
"common",
"license",
"mailbox",
#ifdef WITH_TLS
"tls",
#endif
NULL
};
/* Charset magic */
static char *output_charset = NULL;
const char *
get_charset ()
{
char *tmp;
if (!output_charset)
{
char locale[32];
memset (locale, 0, sizeof (locale));
/* Try to deduce the charset from LC_ALL or LANG variables */
tmp = getenv ("LC_ALL");
if (!tmp)
tmp = getenv ("LANG");
if (tmp)
{
char *sp = NULL;
char *lang;
char *terr;
strncpy (locale, tmp, sizeof (locale) - 1);
lang = strtok_r (locale, "_", &sp);
terr = strtok_r (NULL, ".", &sp);
output_charset = strtok_r (NULL, "@", &sp);
if (output_charset)
output_charset = xstrdup (output_charset);
else
output_charset = mu_charset_lookup (lang, terr);
if (!output_charset)
output_charset = "ASCII";
}
}
return output_charset;
}
/* BIDI support (will be moved to lib when it's ready) */
#ifdef HAVE_LIBFRIBIDI
static int fb_charset_num = -1;
FriBidiChar *logical;
char *outstring;
size_t logical_size;
void
alloc_logical (size_t size)
{
logical = xmalloc (size * sizeof (logical[0]));
logical_size = size;
outstring = xmalloc (size);
}
void
puts_bidi (char *string)
{
if (fb_charset_num == -1)
{
fb_charset_num = fribidi_parse_charset (get_charset ());
if (fb_charset_num && dbug)
mu_error (_("fribidi failed to recognize charset `%s'"),
get_charset ());
}
if (fb_charset_num == 0)
puts (string);
else
{
FriBidiStrIndex len;
FriBidiCharType base = FRIBIDI_TYPE_ON;
fribidi_boolean log2vis;
static FriBidiChar *visual;
static size_t visual_size;
len = fribidi_charset_to_unicode (fb_charset_num,
string, strlen (string),
logical);
if (len + 1 > visual_size)
{
visual_size = len + 1;
visual = xrealloc (visual, visual_size * sizeof *visual);
}
/* Create a bidi string. */
log2vis = fribidi_log2vis (logical, len, &base,
/* output */
visual, NULL, NULL, NULL);
if (log2vis)
{
FriBidiStrIndex idx, st;
FriBidiStrIndex new_len;
for (idx = 0; idx < len;)
{
FriBidiStrIndex wid, inlen;
wid = 3 * logical_size;
st = idx;
if (fb_charset_num != FRIBIDI_CHARSET_CAP_RTL)
{
while (wid > 0 && idx < len)
wid -= fribidi_wcwidth (visual[idx++]);
}
else
{
while (wid > 0 && idx < len)
{
wid--;
idx++;
}
}
if (wid < 0 && idx > st + 1)
idx--;
inlen = idx - st;
new_len = fribidi_unicode_to_charset (fb_charset_num,
visual + st, inlen,
outstring);
printf ("%s", outstring);
}
putchar ('\n');
}
else
{
/* Print the string as is */
puts (string);
}
}
}
#else
# define alloc_logical(s)
# define puts_bidi puts
#endif
/* Output functions */
/* Number of columns in output:
Maximum 4 message number, to, from, subject -ln
......@@ -220,23 +486,24 @@ print_line ()
{
if (linebuf)
{
puts (linebuf);
puts_bidi (linebuf);
linebuf[0] = 0;
linepos = 0;
curcol = nextstart = 0;
curfield = 0;
}
else
putchar ('\n');
curfield = 0;
}
void
format_field_simple (const char *fmt, ...)
{
va_list ap;
if (curfield++)
putchar ('\t');
va_start (ap, fmt);
vprintf (fmt, ap);
putchar (' ');
va_end (ap);
}
......@@ -253,7 +520,7 @@ format_field_align (const char *fmt, ...)
{
if (curfield == numfields - 1)
{
puts (linebuf);
puts_bidi (linebuf);
linepos = 0;
printf ("%*s", nextstart, "");
}
......@@ -298,137 +565,47 @@ format_field_align (const char *fmt, ...)
nextstart += fieldwidth[curfield++];
}
/*
* Get the number of columns on the screen
* First try an ioctl() call not all shells set the COLUMNS environ.
* This function was taken from mail/util.c.
*/
int
util_getcols (void)
void
init_output (size_t s)
{
struct winsize ws;
ws.ws_col = ws.ws_row = 0;
if (ioctl (1, TIOCGWINSZ, (char *) &ws) < 0)
{
int fd = open ("/dev/tty", O_RDWR);
ioctl (fd, TIOCGWINSZ, (char *) &ws);
close (fd);
}
if (ws.ws_row == 0)
{
const char *columns = getenv ("COLUMNS");
if (columns)
ws.ws_col = strtol (columns, NULL, 10);
}
return ws.ws_col;
}
int i;
size_t width = 0;
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
switch (key)
if (s == 0)
{
case 'd':
dbug++;
break;
case 'f':
show_field = arg;
align = 0;
break;
case 'l':
show_to = 1;
break;
case 'n':
show_number = 1;
break;
case 'Q':
/* Very silent. */
be_quiet += 2;
break;
case 'q':
be_quiet++;
show_query = 1;
break;
case 'S':
show_summary = 1;
break;
case 's':
select_attribute = decode_attr (arg);
break;
case 't':
align = 1;
break;
case ARGP_KEY_FINI:
if (align && (linemax = util_getcols ()))
{
int i;
size_t width = 0;
format_field = format_field_align;
/* Allocate the line buffer */
linemax = linemax * MB_LEN_MAX + 1;
linebuf = xmalloc (linemax);
/* Set up column widths */
if (show_number)
fieldwidth[numfields++] = 5;
if (show_to)
fieldwidth[numfields++] = 20;
if (show_field)
fieldwidth[numfields++] = 0;
else
{
fieldwidth[numfields++] = 20;
fieldwidth[numfields++] = 0;
}
format_field = format_field_simple;
return;
}
format_field = format_field_align;
for (i = 0; i < numfields; i++)
width += fieldwidth[i];
/* Allocate the line buffer */
linemax = s * MB_LEN_MAX + 1;
linebuf = xmalloc (linemax);
alloc_logical (s);
fieldwidth[numfields-1] = util_getcols () - width;
}
else
format_field = format_field_simple;
break;
default:
return ARGP_ERR_UNKNOWN;
/* Set up column widths */
if (show_number)
fieldwidth[numfields++] = 5;
if (show_to)
fieldwidth[numfields++] = 20;
if (show_field)
fieldwidth[numfields++] = 0;
else
{
fieldwidth[numfields++] = 20;
fieldwidth[numfields++] = 0;
}
return 0;
for (i = 0; i < numfields; i++)
width += fieldwidth[i];
fieldwidth[numfields-1] = util_getcols () - width;
}
static struct argp argp = {
options,
parse_opt,
N_("[URL ...]"),
doc,
NULL,
NULL, NULL
};
static const char *frm_argp_capa[] = {
"common",
"license",
"mailbox",
#ifdef WITH_TLS
"tls",
#endif
NULL
};
/*
FIXME: Generalize this function and move it
to `mailbox/locale.c'. Do the same with the one
......@@ -439,39 +616,7 @@ rfc2047_decode_wrapper (char *buf, size_t buflen)
{
int rc;
char *tmp;
static char *charset = NULL;
if (!charset)
{
char locale[32];
memset (locale, 0, sizeof (locale));
/* Try to deduce the charset from LC_ALL or LANG variables */
tmp = getenv ("LC_ALL");
if (!tmp)
tmp = getenv ("LANG");
if (tmp)
{
char *sp = NULL;
char *lang;
char *terr;
strncpy (locale, tmp, sizeof (locale) - 1);
lang = strtok_r (locale, "_", &sp);
terr = strtok_r (NULL, ".", &sp);
charset = strtok_r (NULL, "@", &sp);
if (!charset)
charset = mu_charset_lookup (lang, terr);
if (!charset)
charset = "ASCII";
}
}
const char *charset = get_charset ();
if (strcmp (charset, "ASCII") == 0)
return strdup (buf);
......@@ -566,7 +711,7 @@ action (observer_t o, size_t type)
break;
if (show_number)
format_field ("%4lu: ", (u_long) counter.index);
format_field ("%4lu:", (u_long) counter.index);
if (show_to)
{
......@@ -575,7 +720,7 @@ action (observer_t o, size_t type)
if (status == 0)
{
format_field ("(%s) ", hto);
format_field ("(%s)", hto);
free (hto);
}
else
......