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