calendar: use iconv to respect the output encoding

calendar(1) can have input in various encoding, specifying
LANG=<locale_name> to enable calendar(1) to determine which one to use.

The problem is the content of the calendar itself is exposed as is making it
unreadable in many cases. For example french calendar which is encoded
ISO8859-1 is rendered badly in a fr_FR.UTF-8 environment.

Using iconv allows to solve this issue.
This will also allow to keep only 1 encoding in base for those files without
breaking user existing setup

Reported by:	many
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D19221
This commit is contained in:
Baptiste Daroussin 2019-02-20 06:40:52 +00:00
parent 748f247a44
commit adedf5ee31
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=344340
5 changed files with 143 additions and 15 deletions

View File

@ -13,6 +13,10 @@ INTER= de_AT.ISO_8859-15 de_DE.ISO8859-1 fr_FR.ISO8859-1 \
DE_LINKS= de_DE.ISO8859-15
FR_LINKS= fr_FR.ISO8859-15
.if ${MK_ICONV} == "yes"
CFLAGS+= -DWITH_ICONV
.endif
FILESGROUPS+= CALS
CALS= calendars/calendar.all \
calendars/calendar.australia \

View File

@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <errno.h>
#include <locale.h>
#include <langinfo.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
@ -66,6 +67,9 @@ static char *DEBUG = NULL;
static time_t f_time = 0;
double UTCOffset = UTCOFFSET_NOTSET;
int EastLongitude = LONGITUDE_NOTSET;
#ifdef WITH_ICONV
const char *outputEncoding;
#endif
static void usage(void) __dead2;
@ -80,6 +84,12 @@ main(int argc, char *argv[])
struct tm tp1, tp2;
(void)setlocale(LC_ALL, "");
#ifdef WITH_ICONV
/* save the information about the encoding used in the terminal */
outputEncoding = strdup(nl_langinfo(CODESET));
if (outputEncoding == NULL)
errx(1, "cannot allocate memory");
#endif
while ((ch = getopt(argc, argv, "-A:aB:D:dF:f:l:t:U:W:?")) != -1)
switch (ch) {

View File

@ -59,6 +59,9 @@ extern struct fixs neaster, npaskha, ncny, nfullmoon, nnewmoon;
extern struct fixs nmarequinox, nsepequinox, njunsolstice, ndecsolstice;
extern double UTCOffset;
extern int EastLongitude;
#ifdef WITH_ICONV
extern const char *outputEncoding;
#endif
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
@ -197,3 +200,7 @@ void fpom(int year, double utcoffset, double *ffms, double *fnms);
void equinoxsolstice(int year, double UTCoffset, int *equinoxdays, int *solsticedays);
void fequinoxsolstice(int year, double UTCoffset, double *equinoxdays, double *solsticedays);
int calculatesunlongitude30(int year, int degreeGMToffset, int *ichinesemonths);
#ifdef WITH_ICONV
void set_new_encoding(void);
#endif

View File

@ -35,10 +35,120 @@ __FBSDID("$FreeBSD$");
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WITH_ICONV
#include <iconv.h>
#include <errno.h>
#include <langinfo.h>
static iconv_t conv = (iconv_t)-1;
static char *currentEncoding = NULL;
#endif
#include "pathnames.h"
#include "calendar.h"
#ifdef WITH_ICONV
void
set_new_encoding(void)
{
const char *newenc;
newenc = nl_langinfo(CODESET);
if (currentEncoding == NULL) {
currentEncoding = strdup(newenc);
if (currentEncoding == NULL)
errx(1, "set_new_encoding: cannot allocate memory");
return;
}
if (strcmp(currentEncoding, newenc) == 0)
return;
free(currentEncoding);
currentEncoding = strdup(newenc);
if (currentEncoding == NULL)
errx(1, "set_new_encoding: cannot allocate memory");
if (conv != (iconv_t) -1) {
iconv_close(conv);
conv = (iconv_t) -1;
}
}
#endif
static char *
convert(char *input)
{
char *output;
#ifdef WITH_ICONV
size_t inleft, outleft, converted = 0;
char *outbuf, *tmp;
char *inbuf;
size_t outlen;
if (currentEncoding == NULL) {
output = strdup(input);
if (output == NULL)
errx(1, "convert: cannot allocate memory");
return (output);
}
if (conv == (iconv_t)-1) {
conv = iconv_open(outputEncoding, currentEncoding);
if (conv == (iconv_t)-1) {
if (errno == EINVAL)
errx(1, "Conversion is not supported");
else
err(1, "Initialization failure");
}
}
inleft = strlen(input);
inbuf = input;
outlen = inleft;
if ((output = malloc(outlen + 1)) == NULL)
errx(1, "convert: cannot allocate memory");
for (;;) {
errno = 0;
outbuf = output + converted;
outleft = outlen - converted;
converted = iconv(conv, (char **) &inbuf, &inleft, &outbuf, &outleft);
if (converted != (size_t) -1 || errno == EINVAL) {
/* finished or invalid multibyte, so truncate and ignore */
break;
}
if (errno != E2BIG) {
free(output);
err(1, "convert");
}
converted = outbuf - output;
outlen += inleft * 2;
if ((tmp = realloc(output, outlen + 1)) == NULL) {
free(output);
errx(1, "convert: cannot allocate memory");
}
output = tmp;
outbuf = output + converted;
}
/* flush the iconv conversion */
iconv(conv, NULL, NULL, &outbuf, &outleft);
/* null terminate the string */
*outbuf = '\0';
#else
output = strdup(input);
if (output == NULL)
errx(1, "convert: cannot allocate memory");
#endif
return (output);
}
struct event *
event_add(int year, int month, int day, char *date, int var, char *txt,
char *extra)
@ -58,15 +168,15 @@ event_add(int year, int month, int day, char *date, int var, char *txt,
e->month = month;
e->day = day;
e->var = var;
e->date = strdup(date);
e->date = convert(date);
if (e->date == NULL)
errx(1, "event_add: cannot allocate memory");
e->text = strdup(txt);
e->text = convert(txt);
if (e->text == NULL)
errx(1, "event_add: cannot allocate memory");
e->extra = NULL;
if (extra != NULL && extra[0] != '\0')
e->extra = strdup(extra);
e->extra = convert(extra);
addtodate(e, year, month, day);
return (e);
}
@ -74,23 +184,17 @@ event_add(int year, int month, int day, char *date, int var, char *txt,
void
event_continue(struct event *e, char *txt)
{
char *text;
char *oldtext, *text;
/*
* Adding text to the event:
* - Save a copy of the old text (unknown length, so strdup())
* - Allocate enough space for old text + \n + new text + 0
* - Store the old text + \n + new text
* - Destroy the saved copy.
*/
text = strdup(e->text);
if (text == NULL)
text = convert(txt);
oldtext = e->text;
if (oldtext == NULL)
errx(1, "event_continue: cannot allocate memory");
free(e->text);
asprintf(&e->text, "%s\n%s", text, txt);
asprintf(&e->text, "%s\n%s", oldtext, text);
if (e->text == NULL)
errx(1, "event_continue: cannot allocate memory");
free(oldtext);
free(text);
return;

View File

@ -294,6 +294,9 @@ cal_parse(FILE *in, FILE *out)
if (strncmp(buf, "LANG=", 5) == 0) {
(void)setlocale(LC_ALL, buf + 5);
d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
#ifdef WITH_ICONV
set_new_encoding();
#endif
setnnames();
continue;
}