#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <locale.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "runetype.h"
#include "rune_local.h"
#define SAFE_ADD(x, y) \
do { \
if ((x) > SIZE_MAX - (y)) \
return NULL; \
(x) += (y); \
} while (0);
static int readrange(_RuneLocale *, _RuneRange *, uint32_t, void *, FILE *);
static void _freeentry(_RuneRange *);
static int
readrange(_RuneLocale *rl, _RuneRange *rr, uint32_t nranges, void *lastp,
FILE *fp)
{
uint32_t i;
_RuneEntry *re;
_FileRuneEntry fre;
re = (_RuneEntry *)rl->rl_variable;
rr->rr_nranges = nranges;
if (rr->rr_nranges == 0) {
rr->rr_rune_ranges = NULL;
return 0;
}
rr->rr_rune_ranges = re;
for (i = 0; i < rr->rr_nranges; i++) {
if ((void *)re >= lastp)
return -1;
if (fread(&fre, sizeof(fre), 1, fp) != 1)
return -1;
re->re_min = ntohl((uint32_t)fre.fre_min);
re->re_max = ntohl((uint32_t)fre.fre_max);
re->re_map = ntohl((uint32_t)fre.fre_map);
re++;
}
rl->rl_variable = re;
return 0;
}
static int
readentry(_RuneRange *rr, FILE *fp)
{
_RuneEntry *re;
size_t l, i, j;
int error;
re = rr->rr_rune_ranges;
for (i = 0; i < rr->rr_nranges; i++) {
if (re[i].re_map != 0) {
re[i].re_rune_types = NULL;
continue;
}
if (re[i].re_max < re[i].re_min) {
error = EINVAL;
goto fail;
}
l = re[i].re_max - re[i].re_min + 1;
re[i].re_rune_types = calloc(l, sizeof(_RuneType));
if (!re[i].re_rune_types) {
error = ENOMEM;
goto fail;
}
if (fread(re[i].re_rune_types, sizeof(_RuneType), l, fp) != l)
goto fail2;
for (j = 0; j < l; j++)
re[i].re_rune_types[j] = ntohl(re[i].re_rune_types[j]);
}
return 0;
fail:
for (j = 0; j < i; j++) {
free(re[j].re_rune_types);
re[j].re_rune_types = NULL;
}
return error;
fail2:
for (j = 0; j <= i; j++) {
free(re[j].re_rune_types);
re[j].re_rune_types = NULL;
}
return errno;
}
static int
find_codeset(_RuneLocale *rl)
{
char *top, *codeset, *tail, *ep;
if (rl->rl_variable == NULL)
return 0;
ep = (char *)rl->rl_variable;
ep += rl->rl_variable_len;
rl->rl_codeset = NULL;
if (!(top = strstr(rl->rl_variable, _RUNE_CODESET)))
return 0;
tail = strpbrk(top, " \t");
codeset = top + sizeof(_RUNE_CODESET) - 1;
if (tail) {
*top = *tail;
*tail = '\0';
rl->rl_codeset = strdup(codeset);
strlcpy(top + 1, tail + 1, (unsigned)(ep - (top + 1)));
} else {
*top = '\0';
rl->rl_codeset = strdup(codeset);
}
return (rl->rl_codeset == NULL);
}
void
_freeentry(_RuneRange *rr)
{
_RuneEntry *re;
uint32_t i;
re = rr->rr_rune_ranges;
for (i = 0; i < rr->rr_nranges; i++) {
free(re[i].re_rune_types);
re[i].re_rune_types = NULL;
}
}
_RuneLocale *
_Read_RuneMagi(FILE *fp)
{
_FileRuneLocale frl;
char *hostdata;
size_t hostdatalen;
void *lastp;
_RuneLocale *rl;
struct stat sb;
int x;
uint32_t runetype_nranges, maplower_nranges, mapupper_nranges, var_len;
if (fstat(fileno(fp), &sb) == -1)
return NULL;
if (sb.st_size < sizeof(_FileRuneLocale))
return NULL;
rewind(fp);
if (fread(&frl, sizeof(frl), 1, fp) != 1)
return NULL;
if (memcmp(frl.frl_magic, _RUNE_MAGIC_1, sizeof(frl.frl_magic)))
return NULL;
runetype_nranges = ntohl(frl.frl_runetype_ext.frr_nranges);
maplower_nranges = ntohl(frl.frl_maplower_ext.frr_nranges);
mapupper_nranges = ntohl(frl.frl_mapupper_ext.frr_nranges);
var_len = ntohl((uint32_t)frl.frl_variable_len);
#if SIZE_MAX <= UINT32_MAX
if (runetype_nranges > SIZE_MAX / sizeof(_RuneEntry) ||
maplower_nranges > SIZE_MAX / sizeof(_RuneEntry) ||
mapupper_nranges > SIZE_MAX / sizeof(_RuneEntry))
return NULL;
#endif
if (var_len > INT32_MAX)
return NULL;
hostdatalen = sizeof(*rl);
SAFE_ADD(hostdatalen, var_len);
SAFE_ADD(hostdatalen, runetype_nranges * sizeof(_RuneEntry));
SAFE_ADD(hostdatalen, maplower_nranges * sizeof(_RuneEntry));
SAFE_ADD(hostdatalen, mapupper_nranges * sizeof(_RuneEntry));
if ((hostdata = calloc(hostdatalen, 1)) == NULL)
return NULL;
lastp = hostdata + hostdatalen;
rl = (_RuneLocale *)hostdata;
rl->rl_variable = rl + 1;
rl->rl_variable_len = ntohl((uint32_t)frl.frl_variable_len);
for (x = 0; x < _CACHED_RUNES; ++x) {
rl->rl_runetype[x] = ntohl(frl.frl_runetype[x]);
rl->rl_maplower[x] = ntohl((uint32_t)frl.frl_maplower[x]);
rl->rl_mapupper[x] = ntohl((uint32_t)frl.frl_mapupper[x]);
}
if (readrange(rl, &rl->rl_runetype_ext, runetype_nranges, lastp, fp) ||
readrange(rl, &rl->rl_maplower_ext, maplower_nranges, lastp, fp) ||
readrange(rl, &rl->rl_mapupper_ext, mapupper_nranges, lastp, fp))
goto err;
if (readentry(&rl->rl_runetype_ext, fp) != 0)
goto err;
if ((uint8_t *)rl->rl_variable + rl->rl_variable_len >
(uint8_t *)lastp)
goto rune_err;
if (rl->rl_variable_len == 0)
rl->rl_variable = NULL;
else if (fread(rl->rl_variable, rl->rl_variable_len, 1, fp) != 1)
goto rune_err;
if (find_codeset(rl))
goto rune_err;
if (ftello(fp) != sb.st_size)
goto rune_err;
return(rl);
rune_err:
_freeentry(&rl->rl_runetype_ext);
err:
free(hostdata);
return NULL;
}