#include <X11/Xlib.h>
#include <X11/Xmd.h>
#include <X11/Xos.h>
#include "Xlibint.h"
#include "Xlcint.h"
#include "Ximint.h"
#include <sys/stat.h>
#include <stdio.h>
extern int _Xmbstowcs(
wchar_t *wstr,
char *str,
int len
);
extern int _Xmbstoutf8(
char *ustr,
const char *str,
int len
);
static int
nextch(
FILE *fp,
int *lastch)
{
int c;
if (*lastch != 0) {
c = *lastch;
*lastch = 0;
} else {
c = getc(fp);
if (c == '\\') {
c = getc(fp);
if (c == '\n') {
c = getc(fp);
} else {
ungetc(c, fp);
c = '\\';
}
}
}
return(c);
}
static void
putbackch(
int c,
int *lastch)
{
*lastch = c;
}
#define ENDOFFILE 0
#define ENDOFLINE 1
#define COLON 2
#define LESS 3
#define GREATER 4
#define EXCLAM 5
#define TILDE 6
#define STRING 7
#define KEY 8
#define ERROR 9
#ifndef isalnum
#define isalnum(c) \
(('0' <= (c) && (c) <= '9') || \
('A' <= (c) && (c) <= 'Z') || \
('a' <= (c) && (c) <= 'z'))
#endif
static int
nexttoken(
FILE *fp,
char *tokenbuf,
int *lastch)
{
int c;
int token;
char *p;
int i, j;
while ((c = nextch(fp, lastch)) == ' ' || c == '\t') {
}
switch (c) {
case EOF:
token = ENDOFFILE;
break;
case '\n':
token = ENDOFLINE;
break;
case '<':
token = LESS;
break;
case '>':
token = GREATER;
break;
case ':':
token = COLON;
break;
case '!':
token = EXCLAM;
break;
case '~':
token = TILDE;
break;
case '"':
p = tokenbuf;
while ((c = nextch(fp, lastch)) != '"') {
if (c == '\n' || c == EOF) {
putbackch(c, lastch);
token = ERROR;
goto string_error;
} else if (c == '\\') {
c = nextch(fp, lastch);
switch (c) {
case '\\':
case '"':
*p++ = c;
break;
case 'n':
*p++ = '\n';
break;
case 'r':
*p++ = '\r';
break;
case 't':
*p++ = '\t';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
i = c - '0';
c = nextch(fp, lastch);
for (j = 0; j < 2 && c >= '0' && c <= '7'; j++) {
i <<= 3;
i += c - '0';
c = nextch(fp, lastch);
}
putbackch(c, lastch);
*p++ = (char)i;
break;
case 'X':
case 'x':
i = 0;
for (j = 0; j < 2; j++) {
c = nextch(fp, lastch);
i <<= 4;
if (c >= '0' && c <= '9') {
i += c - '0';
} else if (c >= 'A' && c <= 'F') {
i += c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
i += c - 'a' + 10;
} else {
putbackch(c, lastch);
i >>= 4;
break;
}
}
if (j == 0) {
token = ERROR;
goto string_error;
}
*p++ = (char)i;
break;
case EOF:
putbackch(c, lastch);
token = ERROR;
goto string_error;
default:
*p++ = c;
break;
}
} else {
*p++ = c;
}
}
*p = '\0';
token = STRING;
break;
case '#':
while ((c = nextch(fp, lastch)) != '\n' && c != EOF) {
}
if (c == '\n') {
token = ENDOFLINE;
} else {
token = ENDOFFILE;
}
break;
default:
if (isalnum(c) || c == '_' || c == '-') {
p = tokenbuf;
*p++ = c;
c = nextch(fp, lastch);
while (isalnum(c) || c == '_' || c == '-') {
*p++ = c;
c = nextch(fp, lastch);
}
*p = '\0';
putbackch(c, lastch);
token = KEY;
} else {
token = ERROR;
}
break;
}
string_error:
return(token);
}
static long
modmask(
char *name)
{
long mask;
struct _modtbl {
char *name;
long mask;
};
struct _modtbl *p;
static struct _modtbl tbl[] = {
{ "Ctrl", ControlMask },
{ "Lock", LockMask },
{ "Caps", LockMask },
{ "Shift", ShiftMask },
{ "Alt", Mod1Mask },
{ "Meta", Mod1Mask },
{ NULL, 0 }};
p = tbl;
mask = 0;
for (p = tbl; p->name != NULL; p++) {
if (strcmp(name, p->name) == 0) {
mask = p->mask;
break;
}
}
return(mask);
}
static char*
TransFileName(Xim im, char *name)
{
char *home = NULL, *lcCompose = NULL;
char *i = name, *ret, *j;
int l = 0;
while (*i) {
if (*i == '%') {
i++;
switch (*i) {
case '%':
l++;
break;
case 'H':
home = getenv("HOME");
if (home)
l += strlen(home);
break;
case 'L':
lcCompose = _XlcFileName(im->core.lcd, COMPOSE_FILE);
if (lcCompose)
l += strlen(lcCompose);
break;
}
} else {
l++;
}
i++;
}
j = ret = Xmalloc(l+1);
if (ret == NULL)
return ret;
i = name;
while (*i) {
if (*i == '%') {
i++;
switch (*i) {
case '%':
*j++ = '%';
break;
case 'H':
if (home) {
strcpy(j, home);
j += strlen(home);
}
break;
case 'L':
if (lcCompose) {
strcpy(j, lcCompose);
j += strlen(lcCompose);
Xfree(lcCompose);
}
break;
}
i++;
} else {
*j++ = *i++;
}
}
*j = '\0';
return ret;
}
#ifndef MB_LEN_MAX
#define MB_LEN_MAX 6
#endif
static int
get_mb_string (Xim im, char *buf, KeySym ks)
{
XPointer from, to;
int from_len, to_len, len;
XPointer args[1];
XlcCharSet charset;
char local_buf[MB_LEN_MAX];
unsigned int ucs;
ucs = KeySymToUcs4(ks);
from = (XPointer) &ucs;
to = (XPointer) local_buf;
from_len = 1;
to_len = MB_LEN_MAX;
args[0] = (XPointer) &charset;
if (_XlcConvert(im->private.local.ucstoc_conv,
&from, &from_len, &to, &to_len, args, 1 ) != 0) {
return 0;
}
from = (XPointer) local_buf;
to = (XPointer) buf;
from_len = MB_LEN_MAX - to_len;
to_len = MB_LEN_MAX + 1;
args[0] = (XPointer) charset;
if (_XlcConvert(im->private.local.cstomb_conv,
&from, &from_len, &to, &to_len, args, 1 ) != 0) {
return 0;
}
len = MB_LEN_MAX + 1 - to_len;
buf[len] = '\0';
return len;
}
#define AllMask (ShiftMask | LockMask | ControlMask | Mod1Mask)
#define LOCAL_WC_BUFSIZE 128
#define LOCAL_UTF8_BUFSIZE 256
#define SEQUENCE_MAX 10
static int
parseline(
FILE *fp,
Xim im,
char* tokenbuf)
{
int token;
unsigned modifier_mask;
unsigned modifier;
unsigned tmp;
KeySym keysym = NoSymbol;
DefTree **top = &im->private.local.top;
DefTree *p = NULL;
Bool exclam, tilde;
KeySym rhs_keysym = 0;
char *rhs_string_mb;
int l;
int lastch = 0;
char local_mb_buf[MB_LEN_MAX+1];
wchar_t local_wc_buf[LOCAL_WC_BUFSIZE], *rhs_string_wc;
char local_utf8_buf[LOCAL_UTF8_BUFSIZE], *rhs_string_utf8;
struct DefBuffer {
unsigned modifier_mask;
unsigned modifier;
KeySym keysym;
};
struct DefBuffer buf[SEQUENCE_MAX];
int i, n;
do {
token = nexttoken(fp, tokenbuf, &lastch);
} while (token == ENDOFLINE);
if (token == ENDOFFILE) {
return(-1);
}
n = 0;
do {
if ((token == KEY) && (strcmp("include", tokenbuf) == 0)) {
char *filename;
FILE *infp;
token = nexttoken(fp, tokenbuf, &lastch);
if (token != KEY && token != STRING)
goto error;
if ((filename = TransFileName(im, tokenbuf)) == NULL)
goto error;
infp = _XFopenFile(filename, "r");
Xfree(filename);
if (infp == NULL)
goto error;
_XimParseStringFile(infp, im);
return (0);
} else if ((token == KEY) && (strcmp("None", tokenbuf) == 0)) {
modifier = 0;
modifier_mask = AllMask;
token = nexttoken(fp, tokenbuf, &lastch);
} else {
modifier_mask = modifier = 0;
exclam = False;
if (token == EXCLAM) {
exclam = True;
token = nexttoken(fp, tokenbuf, &lastch);
}
while (token == TILDE || token == KEY) {
tilde = False;
if (token == TILDE) {
tilde = True;
token = nexttoken(fp, tokenbuf, &lastch);
if (token != KEY)
goto error;
}
tmp = modmask(tokenbuf);
if (!tmp) {
goto error;
}
modifier_mask |= tmp;
if (tilde) {
modifier &= ~tmp;
} else {
modifier |= tmp;
}
token = nexttoken(fp, tokenbuf, &lastch);
}
if (exclam) {
modifier_mask = AllMask;
}
}
if (token != LESS) {
goto error;
}
token = nexttoken(fp, tokenbuf, &lastch);
if (token != KEY) {
goto error;
}
token = nexttoken(fp, tokenbuf, &lastch);
if (token != GREATER) {
goto error;
}
keysym = XStringToKeysym(tokenbuf);
if (keysym == NoSymbol) {
goto error;
}
buf[n].keysym = keysym;
buf[n].modifier = modifier;
buf[n].modifier_mask = modifier_mask;
n++;
if( n >= SEQUENCE_MAX )
goto error;
token = nexttoken(fp, tokenbuf, &lastch);
} while (token != COLON);
token = nexttoken(fp, tokenbuf, &lastch);
if (token == STRING) {
if( (rhs_string_mb = Xmalloc(strlen(tokenbuf) + 1)) == NULL )
goto error;
strcpy(rhs_string_mb, tokenbuf);
token = nexttoken(fp, tokenbuf, &lastch);
if (token == KEY) {
rhs_keysym = XStringToKeysym(tokenbuf);
if (rhs_keysym == NoSymbol) {
Xfree(rhs_string_mb);
goto error;
}
token = nexttoken(fp, tokenbuf, &lastch);
}
if (token != ENDOFLINE && token != ENDOFFILE) {
Xfree(rhs_string_mb);
goto error;
}
} else if (token == KEY) {
rhs_keysym = XStringToKeysym(tokenbuf);
if (rhs_keysym == NoSymbol) {
goto error;
}
token = nexttoken(fp, tokenbuf, &lastch);
if (token != ENDOFLINE && token != ENDOFFILE) {
goto error;
}
l = get_mb_string(im, local_mb_buf, rhs_keysym);
if (l == 0) {
rhs_string_mb = Xmalloc(1);
} else {
rhs_string_mb = Xmalloc(l + 1);
}
if( rhs_string_mb == NULL ) {
goto error;
}
memcpy(rhs_string_mb, local_mb_buf, l);
rhs_string_mb[l] = '\0';
} else {
goto error;
}
l = _Xmbstowcs(local_wc_buf, rhs_string_mb, LOCAL_WC_BUFSIZE - 1);
if (l == LOCAL_WC_BUFSIZE - 1) {
local_wc_buf[l] = (wchar_t)'\0';
}
if( (rhs_string_wc = (wchar_t *)Xmalloc((l + 1) * sizeof(wchar_t))) == NULL ) {
Xfree( rhs_string_mb );
return( 0 );
}
memcpy((char *)rhs_string_wc, (char *)local_wc_buf, (l + 1) * sizeof(wchar_t) );
l = _Xmbstoutf8(local_utf8_buf, rhs_string_mb, LOCAL_UTF8_BUFSIZE - 1);
if (l == LOCAL_UTF8_BUFSIZE - 1) {
local_wc_buf[l] = '\0';
}
if( (rhs_string_utf8 = (char *)Xmalloc(l + 1)) == NULL ) {
Xfree( rhs_string_wc );
Xfree( rhs_string_mb );
return( 0 );
}
memcpy(rhs_string_utf8, local_utf8_buf, l + 1);
for (i = 0; i < n; i++) {
for (p = *top; p; p = p->next) {
if (buf[i].keysym == p->keysym &&
buf[i].modifier == p->modifier &&
buf[i].modifier_mask == p->modifier_mask) {
break;
}
}
if (p) {
top = &p->succession;
} else {
if( (p = (DefTree*)Xmalloc(sizeof(DefTree))) == NULL ) {
Xfree( rhs_string_mb );
goto error;
}
p->keysym = buf[i].keysym;
p->modifier = buf[i].modifier;
p->modifier_mask = buf[i].modifier_mask;
p->succession = NULL;
p->next = *top;
p->mb = NULL;
p->wc = NULL;
p->utf8 = NULL;
p->ks = NoSymbol;
*top = p;
top = &p->succession;
}
}
if( p->mb != NULL )
Xfree( p->mb );
p->mb = rhs_string_mb;
if( p->wc != NULL )
Xfree( p->wc );
p->wc = rhs_string_wc;
if( p->utf8 != NULL )
Xfree( p->utf8 );
p->utf8 = rhs_string_utf8;
p->ks = rhs_keysym;
return(n);
error:
while (token != ENDOFLINE && token != ENDOFFILE) {
token = nexttoken(fp, tokenbuf, &lastch);
}
return(0);
}
void
_XimParseStringFile(
FILE *fp,
Xim im)
{
char tb[8192];
char* tbp;
struct stat st;
if (fstat (fileno (fp), &st) != -1) {
unsigned long size = (unsigned long) st.st_size;
if (size <= sizeof tb) tbp = tb;
else tbp = malloc (size);
if (tbp != NULL) {
while (parseline(fp, im, tbp) >= 0) {}
if (tbp != tb) free (tbp);
}
}
}