imLcPrs.c revision b4ee4795
1/******************************************************************
2
3              Copyright 1992 by Oki Technosystems Laboratory, Inc.
4              Copyright 1992 by Fuji Xerox Co., Ltd.
5
6Permission to use, copy, modify, distribute, and sell this software
7and its documentation for any purpose is hereby granted without fee,
8provided that the above copyright notice appear in all copies and
9that both that copyright notice and this permission notice appear
10in supporting documentation, and that the name of Oki Technosystems
11Laboratory and Fuji Xerox not be used in advertising or publicity
12pertaining to distribution of the software without specific, written
13prior permission.
14Oki Technosystems Laboratory and Fuji Xerox make no representations
15about the suitability of this software for any purpose.  It is provided
16"as is" without express or implied warranty.
17
18OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES
19WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
20MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS
21LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR
22CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
23OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
24OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
25OR PERFORMANCE OF THIS SOFTWARE.
26
27  Author: Yasuhiro Kawai	Oki Technosystems Laboratory
28  Author: Kazunori Nishihara	Fuji Xerox
29
30******************************************************************/
31
32
33#ifdef HAVE_CONFIG_H
34#include <config.h>
35#endif
36#include <X11/Xlib.h>
37#include <X11/Xmd.h>
38#include <X11/Xos.h>
39#include "Xlibint.h"
40#include "Xlcint.h"
41#include "Ximint.h"
42#include <sys/stat.h>
43#include <stdio.h>
44
45#define XLC_BUFSIZE 256
46
47extern void xlocaledir(
48    char *buf,
49    int buf_len
50);
51
52extern int _Xmbstowcs(
53    wchar_t	*wstr,
54    char	*str,
55    int		len
56);
57
58extern int _Xmbstoutf8(
59    char	*ustr,
60    const char	*str,
61    int		len
62);
63
64/*
65 *	Parsing File Format:
66 *
67 *	FILE          ::= { [PRODUCTION] [COMMENT] "\n"}
68 *	PRODUCTION    ::= LHS ":" RHS [ COMMENT ]
69 *	COMMENT       ::= "#" {<any character except null or newline>}
70 *	LHS           ::= EVENT { EVENT }
71 *	EVENT         ::= [MODIFIER_LIST] "<" keysym ">"
72 *	MODIFIER_LIST ::= ("!" {MODIFIER} ) | "None"
73 *	MODIFIER      ::= ["~"] modifier_name
74 *	RHS           ::= ( STRING | keysym | STRING keysym )
75 *	STRING        ::= '"' { CHAR } '"'
76 *	CHAR          ::= GRAPHIC_CHAR | ESCAPED_CHAR
77 *	GRAPHIC_CHAR  ::= locale (codeset) dependent code
78 *	ESCAPED_CHAR  ::= ('\\' | '\"' | OCTAL | HEX )
79 *	OCTAL         ::= '\' OCTAL_CHAR [OCTAL_CHAR [OCTAL_CHAR]]
80 *	OCTAL_CHAR    ::= (0|1|2|3|4|5|6|7)
81 *	HEX           ::= '\' (x|X) HEX_CHAR [HEX_CHAR]]
82 *	HEX_CHAR      ::= (0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|a|b|c|d|e|f)
83 *
84 */
85
86static int
87nextch(
88    FILE *fp,
89    int *lastch)
90{
91    int c;
92
93    if (*lastch != 0) {
94	c = *lastch;
95	*lastch = 0;
96    } else {
97	c = getc(fp);
98	if (c == '\\') {
99	    c = getc(fp);
100	    if (c == '\n') {
101		c = getc(fp);
102	    } else {
103		ungetc(c, fp);
104		c = '\\';
105	    }
106	}
107    }
108    return(c);
109}
110
111static void
112putbackch(
113    int c,
114    int *lastch)
115{
116    *lastch = c;
117}
118
119#define ENDOFFILE 0
120#define ENDOFLINE 1
121#define COLON 2
122#define LESS 3
123#define GREATER 4
124#define EXCLAM 5
125#define TILDE 6
126#define STRING 7
127#define KEY 8
128#define ERROR 9
129
130#ifndef isalnum
131#define isalnum(c)      \
132    (('0' <= (c) && (c) <= '9')  || \
133     ('A' <= (c) && (c) <= 'Z')  || \
134     ('a' <= (c) && (c) <= 'z'))
135#endif
136
137static int
138nexttoken(
139    FILE *fp,
140    char *tokenbuf,
141    int *lastch)
142{
143    int c;
144    int token;
145    char *p;
146    int i, j;
147
148    while ((c = nextch(fp, lastch)) == ' ' || c == '\t') {
149    }
150    switch (c) {
151      case EOF:
152	token = ENDOFFILE;
153	break;
154      case '\n':
155	token = ENDOFLINE;
156	break;
157      case '<':
158	token = LESS;
159	break;
160      case '>':
161	token = GREATER;
162	break;
163      case ':':
164	token = COLON;
165	break;
166      case '!':
167	token = EXCLAM;
168	break;
169      case '~':
170	token = TILDE;
171	break;
172      case '"':
173	p = tokenbuf;
174	while ((c = nextch(fp, lastch)) != '"') {
175	    if (c == '\n' || c == EOF) {
176		putbackch(c, lastch);
177		token = ERROR;
178		goto string_error;
179	    } else if (c == '\\') {
180		c = nextch(fp, lastch);
181		switch (c) {
182		  case '\\':
183		  case '"':
184		    *p++ = c;
185		    break;
186		  case 'n':
187		    *p++ = '\n';
188		    break;
189		  case 'r':
190		    *p++ = '\r';
191		    break;
192		  case 't':
193		    *p++ = '\t';
194		    break;
195		  case '0':
196		  case '1':
197		  case '2':
198		  case '3':
199		  case '4':
200		  case '5':
201		  case '6':
202		  case '7':
203		    i = c - '0';
204		    c = nextch(fp, lastch);
205		    for (j = 0; j < 2 && c >= '0' && c <= '7'; j++) {
206			i <<= 3;
207			i += c - '0';
208			c = nextch(fp, lastch);
209		    }
210		    putbackch(c, lastch);
211		    *p++ = (char)i;
212		    break;
213		  case 'X':
214		  case 'x':
215		    i = 0;
216		    for (j = 0; j < 2; j++) {
217			c = nextch(fp, lastch);
218 			i <<= 4;
219			if (c >= '0' && c <= '9') {
220			    i += c - '0';
221			} else if (c >= 'A' && c <= 'F') {
222			    i += c - 'A' + 10;
223			} else if (c >= 'a' && c <= 'f') {
224			    i += c - 'a' + 10;
225			} else {
226			    putbackch(c, lastch);
227			    i >>= 4;
228			    break;
229		        }
230		    }
231		    if (j == 0) {
232		        token = ERROR;
233		        goto string_error;
234		    }
235		    *p++ = (char)i;
236		    break;
237		  case EOF:
238		    putbackch(c, lastch);
239		    token = ERROR;
240		    goto string_error;
241		  default:
242		    *p++ = c;
243		    break;
244		}
245	    } else {
246		*p++ = c;
247	    }
248	}
249	*p = '\0';
250	token = STRING;
251	break;
252      case '#':
253	while ((c = nextch(fp, lastch)) != '\n' && c != EOF) {
254	}
255	if (c == '\n') {
256	    token = ENDOFLINE;
257	} else {
258	    token = ENDOFFILE;
259	}
260	break;
261      default:
262	if (isalnum(c) || c == '_' || c == '-') {
263	    p = tokenbuf;
264	    *p++ = c;
265	    c = nextch(fp, lastch);
266	    while (isalnum(c) || c == '_' || c == '-') {
267		*p++ = c;
268		c = nextch(fp, lastch);
269	    }
270	    *p = '\0';
271	    putbackch(c, lastch);
272	    token = KEY;
273	} else {
274	    token = ERROR;
275	}
276	break;
277    }
278string_error:
279    return(token);
280}
281
282static long
283modmask(
284    char *name)
285{
286    struct _modtbl {
287	const char name[6];
288	long mask;
289    };
290
291    static const struct _modtbl tbl[] = {
292	{ "Ctrl",	ControlMask	},
293        { "Lock",	LockMask	},
294        { "Caps",	LockMask	},
295        { "Shift",	ShiftMask	},
296        { "Alt",	Mod1Mask	},
297        { "Meta",	Mod1Mask	}};
298
299    int i, num_entries = sizeof (tbl) / sizeof (tbl[0]);
300
301    for (i = 0; i < num_entries; i++)
302        if (!strcmp (name, tbl[i].name))
303            return tbl[i].mask;
304
305    return 0;
306}
307
308static char*
309TransFileName(Xim im, char *name)
310{
311   char *home = NULL, *lcCompose = NULL;
312   char dir[XLC_BUFSIZE];
313   char *i = name, *ret, *j;
314   int l = 0;
315
316   while (*i) {
317      if (*i == '%') {
318   	  i++;
319   	  switch (*i) {
320   	      case '%':
321                 l++;
322   	         break;
323   	      case 'H':
324   	         home = getenv("HOME");
325   	         if (home)
326                     l += strlen(home);
327   	         break;
328   	      case 'L':
329                 lcCompose = _XlcFileName(im->core.lcd, COMPOSE_FILE);
330                 if (lcCompose)
331                     l += strlen(lcCompose);
332   	         break;
333   	      case 'S':
334                 xlocaledir(dir, XLC_BUFSIZE);
335                 l += strlen(dir);
336   	         break;
337   	  }
338      } else {
339      	  l++;
340      }
341      i++;
342   }
343
344   j = ret = Xmalloc(l+1);
345   if (ret == NULL)
346      return ret;
347   i = name;
348   while (*i) {
349      if (*i == '%') {
350   	  i++;
351   	  switch (*i) {
352   	      case '%':
353                 *j++ = '%';
354   	         break;
355   	      case 'H':
356   	         if (home) {
357   	             strcpy(j, home);
358   	             j += strlen(home);
359   	         }
360   	         break;
361   	      case 'L':
362   	         if (lcCompose) {
363                    strcpy(j, lcCompose);
364                    j += strlen(lcCompose);
365                    Xfree(lcCompose);
366                 }
367   	         break;
368   	      case 'S':
369                 strcpy(j, dir);
370                 j += strlen(dir);
371   	         break;
372   	  }
373          i++;
374      } else {
375      	  *j++ = *i++;
376      }
377   }
378   *j = '\0';
379   return ret;
380}
381
382#ifndef MB_LEN_MAX
383#define MB_LEN_MAX 6
384#endif
385
386static int
387get_mb_string (Xim im, char *buf, KeySym ks)
388{
389    XPointer from, to;
390    int from_len, to_len, len;
391    XPointer args[1];
392    XlcCharSet charset;
393    char local_buf[MB_LEN_MAX];
394    unsigned int ucs;
395    ucs = KeySymToUcs4(ks);
396
397    from = (XPointer) &ucs;
398    to =   (XPointer) local_buf;
399    from_len = 1;
400    to_len = MB_LEN_MAX;
401    args[0] = (XPointer) &charset;
402    if (_XlcConvert(im->private.local.ucstoc_conv,
403                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
404         return 0;
405    }
406
407    from = (XPointer) local_buf;
408    to =   (XPointer) buf;
409    from_len = MB_LEN_MAX - to_len;
410    to_len = MB_LEN_MAX + 1;
411    args[0] = (XPointer) charset;
412    if (_XlcConvert(im->private.local.cstomb_conv,
413                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
414         return 0;
415    }
416    len = MB_LEN_MAX + 1 - to_len;
417    buf[len] = '\0';
418    return len;
419}
420
421#define AllMask (ShiftMask | LockMask | ControlMask | Mod1Mask)
422#define LOCAL_WC_BUFSIZE 128
423#define LOCAL_UTF8_BUFSIZE 256
424#define SEQUENCE_MAX	10
425
426static int
427parseline(
428    FILE *fp,
429    Xim   im,
430    char* tokenbuf)
431{
432    int token;
433    DTModifier modifier_mask;
434    DTModifier modifier;
435    DTModifier tmp;
436    KeySym keysym = NoSymbol;
437    DTIndex *top = &im->private.local.top;
438    DefTreeBase *b   = &im->private.local.base;
439    DTIndex t;
440    DefTree *p = NULL;
441    Bool exclam, tilde;
442    KeySym rhs_keysym = 0;
443    char *rhs_string_mb;
444    int l;
445    int lastch = 0;
446    char local_mb_buf[MB_LEN_MAX+1];
447    wchar_t local_wc_buf[LOCAL_WC_BUFSIZE], *rhs_string_wc;
448    char local_utf8_buf[LOCAL_UTF8_BUFSIZE], *rhs_string_utf8;
449
450    struct DefBuffer {
451	DTModifier modifier_mask;
452	DTModifier modifier;
453	KeySym keysym;
454    };
455
456    struct DefBuffer buf[SEQUENCE_MAX];
457    int i, n;
458
459    do {
460	token = nexttoken(fp, tokenbuf, &lastch);
461    } while (token == ENDOFLINE);
462
463    if (token == ENDOFFILE) {
464	return(-1);
465    }
466
467    n = 0;
468    do {
469    	if ((token == KEY) && (strcmp("include", tokenbuf) == 0)) {
470            char *filename;
471            FILE *infp;
472            token = nexttoken(fp, tokenbuf, &lastch);
473            if (token != KEY && token != STRING)
474                goto error;
475            if ((filename = TransFileName(im, tokenbuf)) == NULL)
476                goto error;
477            infp = _XFopenFile(filename, "r");
478                Xfree(filename);
479            if (infp == NULL)
480                goto error;
481            _XimParseStringFile(infp, im);
482            fclose(infp);
483            return (0);
484	} else if ((token == KEY) && (strcmp("None", tokenbuf) == 0)) {
485	    modifier = 0;
486	    modifier_mask = AllMask;
487	    token = nexttoken(fp, tokenbuf, &lastch);
488	} else {
489	    modifier_mask = modifier = 0;
490	    exclam = False;
491	    if (token == EXCLAM) {
492		exclam = True;
493		token = nexttoken(fp, tokenbuf, &lastch);
494	    }
495	    while (token == TILDE || token == KEY) {
496		tilde = False;
497		if (token == TILDE) {
498		    tilde = True;
499		    token = nexttoken(fp, tokenbuf, &lastch);
500		    if (token != KEY)
501			goto error;
502		}
503		tmp = modmask(tokenbuf);
504		if (!tmp) {
505		    goto error;
506		}
507		modifier_mask |= tmp;
508		if (tilde) {
509		    modifier &= ~tmp;
510		} else {
511		    modifier |= tmp;
512		}
513		token = nexttoken(fp, tokenbuf, &lastch);
514	    }
515	    if (exclam) {
516		modifier_mask = AllMask;
517	    }
518	}
519
520	if (token != LESS) {
521	    goto error;
522	}
523
524	token = nexttoken(fp, tokenbuf, &lastch);
525	if (token != KEY) {
526	    goto error;
527	}
528
529	token = nexttoken(fp, tokenbuf, &lastch);
530	if (token != GREATER) {
531	    goto error;
532	}
533
534	keysym = XStringToKeysym(tokenbuf);
535	if (keysym == NoSymbol) {
536	    goto error;
537	}
538
539	buf[n].keysym = keysym;
540	buf[n].modifier = modifier;
541	buf[n].modifier_mask = modifier_mask;
542	n++;
543	if( n >= SEQUENCE_MAX )
544	    goto error;
545	token = nexttoken(fp, tokenbuf, &lastch);
546    } while (token != COLON);
547
548    token = nexttoken(fp, tokenbuf, &lastch);
549    if (token == STRING) {
550	l = strlen(tokenbuf) + 1;
551	while (b->mbused + l > b->mbsize) {
552	    b->mbsize = b->mbsize ? b->mbsize * 1.5 : 1024;
553	    if (! (b->mb = Xrealloc (b->mb, b->mbsize)) )
554		goto error;
555	}
556	rhs_string_mb = &b->mb[b->mbused];
557	b->mbused    += l;
558	strcpy(rhs_string_mb, tokenbuf);
559	token = nexttoken(fp, tokenbuf, &lastch);
560	if (token == KEY) {
561	    rhs_keysym = XStringToKeysym(tokenbuf);
562	    if (rhs_keysym == NoSymbol) {
563		goto error;
564	    }
565	    token = nexttoken(fp, tokenbuf, &lastch);
566	}
567	if (token != ENDOFLINE && token != ENDOFFILE) {
568	    goto error;
569	}
570    } else if (token == KEY) {
571	rhs_keysym = XStringToKeysym(tokenbuf);
572	if (rhs_keysym == NoSymbol) {
573	    goto error;
574	}
575	token = nexttoken(fp, tokenbuf, &lastch);
576	if (token != ENDOFLINE && token != ENDOFFILE) {
577	    goto error;
578	}
579
580        l = get_mb_string(im, local_mb_buf, rhs_keysym);
581	while (b->mbused + l + 1 > b->mbsize) {
582	    b->mbsize = b->mbsize ? b->mbsize * 1.5 : 1024;
583	    if (! (b->mb = Xrealloc (b->mb, b->mbsize)) )
584		goto error;
585	}
586	rhs_string_mb = &b->mb[b->mbused];
587	b->mbused    += l + 1;
588        memcpy(rhs_string_mb, local_mb_buf, l);
589	rhs_string_mb[l] = '\0';
590    } else {
591	goto error;
592    }
593
594    l = _Xmbstowcs(local_wc_buf, rhs_string_mb, LOCAL_WC_BUFSIZE - 1);
595    if (l == LOCAL_WC_BUFSIZE - 1) {
596	local_wc_buf[l] = (wchar_t)'\0';
597    }
598    while (b->wcused + l + 1 > b->wcsize) {
599	b->wcsize = b->wcsize ? b->wcsize * 1.5 : 512;
600	if (! (b->wc = Xrealloc (b->wc, sizeof(wchar_t) * b->wcsize)) )
601	    goto error;
602    }
603    rhs_string_wc = &b->wc[b->wcused];
604    b->wcused    += l + 1;
605    memcpy((char *)rhs_string_wc, (char *)local_wc_buf, (l + 1) * sizeof(wchar_t) );
606
607    l = _Xmbstoutf8(local_utf8_buf, rhs_string_mb, LOCAL_UTF8_BUFSIZE - 1);
608    if (l == LOCAL_UTF8_BUFSIZE - 1) {
609	local_utf8_buf[l] = '\0';
610    }
611    while (b->utf8used + l + 1 > b->utf8size) {
612	b->utf8size = b->utf8size ? b->utf8size * 1.5 : 1024;
613	if (! (b->utf8 = Xrealloc (b->utf8, b->utf8size)) )
614	    goto error;
615    }
616    rhs_string_utf8 = &b->utf8[b->utf8used];
617    b->utf8used    += l + 1;
618    memcpy(rhs_string_utf8, local_utf8_buf, l + 1);
619
620    for (i = 0; i < n; i++) {
621	for (t = *top; t; t = b->tree[t].next) {
622	    if (buf[i].keysym        == b->tree[t].keysym &&
623		buf[i].modifier      == b->tree[t].modifier &&
624		buf[i].modifier_mask == b->tree[t].modifier_mask) {
625		break;
626	    }
627	}
628	if (t) {
629	    p = &b->tree[t];
630	    top = &p->succession;
631	} else {
632	    while (b->treeused >= b->treesize) {
633		DefTree *old     = b->tree;
634		int      oldsize = b->treesize;
635		b->treesize = b->treesize ? b->treesize * 1.5 : 256;
636		if (! (b->tree = Xrealloc (b->tree, sizeof(DefTree) * b->treesize)) )
637		    goto error;
638		if (top >= (DTIndex *) old && top < (DTIndex *) &old[oldsize])
639		    top = (DTIndex *) (((char *) top) + (((char *)b->tree)-(char *)old));
640	    }
641	    p = &b->tree[b->treeused];
642	    p->keysym        = buf[i].keysym;
643	    p->modifier      = buf[i].modifier;
644	    p->modifier_mask = buf[i].modifier_mask;
645	    p->succession    = 0;
646	    p->next          = *top;
647	    p->mb            = 0;
648	    p->wc            = 0;
649	    p->utf8          = 0;
650	    p->ks            = NoSymbol;
651	    *top = b->treeused;
652	    top = &p->succession;
653	    b->treeused++;
654	}
655    }
656
657    /* old entries no longer freed... */
658    p->mb   = rhs_string_mb   - b->mb;
659    p->wc   = rhs_string_wc   - b->wc;
660    p->utf8 = rhs_string_utf8 - b->utf8;
661    p->ks   = rhs_keysym;
662    return(n);
663error:
664    while (token != ENDOFLINE && token != ENDOFFILE) {
665	token = nexttoken(fp, tokenbuf, &lastch);
666    }
667    return(0);
668}
669
670void
671_XimParseStringFile(
672    FILE *fp,
673    Xim   im)
674{
675    char tb[8192];
676    char* tbp;
677    struct stat st;
678
679    if (fstat (fileno (fp), &st) != -1) {
680	unsigned long size = (unsigned long) st.st_size;
681	if (size <= sizeof tb) tbp = tb;
682	else tbp = malloc (size);
683
684	if (tbp != NULL) {
685	    while (parseline(fp, im, tbp) >= 0) {}
686	    if (tbp != tb) free (tbp);
687	}
688    }
689}
690