imLcPrs.c revision 57f47464
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                 if (lcCompose == NULL)
325                     lcCompose = _XlcFileName(im->core.lcd, COMPOSE_FILE);
326                 if (lcCompose)
327                     l += strlen(lcCompose);
328   	         break;
329   	      case 'S':
330                 xlocaledir(dir, XLC_BUFSIZE);
331                 l += strlen(dir);
332   	         break;
333   	  }
334      } else {
335      	  l++;
336      }
337      i++;
338   }
339
340   j = ret = Xmalloc(l+1);
341   if (ret == NULL)
342      return ret;
343   i = name;
344   while (*i) {
345      if (*i == '%') {
346   	  i++;
347   	  switch (*i) {
348   	      case '%':
349                 *j++ = '%';
350   	         break;
351   	      case 'H':
352   	         if (home) {
353   	             strcpy(j, home);
354   	             j += strlen(home);
355   	         }
356   	         break;
357   	      case 'L':
358   	         if (lcCompose) {
359                    strcpy(j, lcCompose);
360                    j += strlen(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   Xfree(lcCompose);
375   return ret;
376}
377
378#ifndef MB_LEN_MAX
379#define MB_LEN_MAX 6
380#endif
381
382static int
383get_mb_string (Xim im, char *buf, KeySym ks)
384{
385    XPointer from, to;
386    int from_len, to_len, len;
387    XPointer args[1];
388    XlcCharSet charset;
389    char local_buf[MB_LEN_MAX];
390    unsigned int ucs;
391    ucs = KeySymToUcs4(ks);
392
393    from = (XPointer) &ucs;
394    to =   (XPointer) local_buf;
395    from_len = 1;
396    to_len = MB_LEN_MAX;
397    args[0] = (XPointer) &charset;
398    if (_XlcConvert(im->private.local.ucstoc_conv,
399                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
400         return 0;
401    }
402
403    from = (XPointer) local_buf;
404    to =   (XPointer) buf;
405    from_len = MB_LEN_MAX - to_len;
406    to_len = MB_LEN_MAX + 1;
407    args[0] = (XPointer) charset;
408    if (_XlcConvert(im->private.local.cstomb_conv,
409                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
410         return 0;
411    }
412    len = MB_LEN_MAX + 1 - to_len;
413    buf[len] = '\0';
414    return len;
415}
416
417#define AllMask (ShiftMask | LockMask | ControlMask | Mod1Mask)
418#define LOCAL_WC_BUFSIZE 128
419#define LOCAL_UTF8_BUFSIZE 256
420#define SEQUENCE_MAX	10
421
422static int
423parseline(
424    FILE *fp,
425    Xim   im,
426    char* tokenbuf)
427{
428    int token;
429    DTModifier modifier_mask;
430    DTModifier modifier;
431    DTModifier tmp;
432    KeySym keysym = NoSymbol;
433    DTIndex *top = &im->private.local.top;
434    DefTreeBase *b   = &im->private.local.base;
435    DTIndex t;
436    DefTree *p = NULL;
437    Bool exclam, tilde;
438    KeySym rhs_keysym = 0;
439    char *rhs_string_mb;
440    int l;
441    int lastch = 0;
442    char local_mb_buf[MB_LEN_MAX+1];
443    wchar_t local_wc_buf[LOCAL_WC_BUFSIZE], *rhs_string_wc;
444    char local_utf8_buf[LOCAL_UTF8_BUFSIZE], *rhs_string_utf8;
445
446    struct DefBuffer {
447	DTModifier modifier_mask;
448	DTModifier modifier;
449	KeySym keysym;
450    };
451
452    struct DefBuffer buf[SEQUENCE_MAX];
453    int i, n;
454
455    do {
456	token = nexttoken(fp, tokenbuf, &lastch);
457    } while (token == ENDOFLINE);
458
459    if (token == ENDOFFILE) {
460	return(-1);
461    }
462
463    n = 0;
464    do {
465    	if ((token == KEY) && (strcmp("include", tokenbuf) == 0)) {
466            char *filename;
467            FILE *infp;
468            token = nexttoken(fp, tokenbuf, &lastch);
469            if (token != KEY && token != STRING)
470                goto error;
471            if ((filename = TransFileName(im, tokenbuf)) == NULL)
472                goto error;
473            infp = _XFopenFile(filename, "r");
474                Xfree(filename);
475            if (infp == NULL)
476                goto error;
477            _XimParseStringFile(infp, im);
478            fclose(infp);
479            return (0);
480	} else if ((token == KEY) && (strcmp("None", tokenbuf) == 0)) {
481	    modifier = 0;
482	    modifier_mask = AllMask;
483	    token = nexttoken(fp, tokenbuf, &lastch);
484	} else {
485	    modifier_mask = modifier = 0;
486	    exclam = False;
487	    if (token == EXCLAM) {
488		exclam = True;
489		token = nexttoken(fp, tokenbuf, &lastch);
490	    }
491	    while (token == TILDE || token == KEY) {
492		tilde = False;
493		if (token == TILDE) {
494		    tilde = True;
495		    token = nexttoken(fp, tokenbuf, &lastch);
496		    if (token != KEY)
497			goto error;
498		}
499		tmp = modmask(tokenbuf);
500		if (!tmp) {
501		    goto error;
502		}
503		modifier_mask |= tmp;
504		if (tilde) {
505		    modifier &= ~tmp;
506		} else {
507		    modifier |= tmp;
508		}
509		token = nexttoken(fp, tokenbuf, &lastch);
510	    }
511	    if (exclam) {
512		modifier_mask = AllMask;
513	    }
514	}
515
516	if (token != LESS) {
517	    goto error;
518	}
519
520	token = nexttoken(fp, tokenbuf, &lastch);
521	if (token != KEY) {
522	    goto error;
523	}
524
525	token = nexttoken(fp, tokenbuf, &lastch);
526	if (token != GREATER) {
527	    goto error;
528	}
529
530	keysym = XStringToKeysym(tokenbuf);
531	if (keysym == NoSymbol) {
532	    goto error;
533	}
534
535	buf[n].keysym = keysym;
536	buf[n].modifier = modifier;
537	buf[n].modifier_mask = modifier_mask;
538	n++;
539	if( n >= SEQUENCE_MAX )
540	    goto error;
541	token = nexttoken(fp, tokenbuf, &lastch);
542    } while (token != COLON);
543
544    token = nexttoken(fp, tokenbuf, &lastch);
545    if (token == STRING) {
546	l = strlen(tokenbuf) + 1;
547	while (b->mbused + l > b->mbsize) {
548	    b->mbsize = b->mbsize ? b->mbsize * 1.5 : 1024;
549	    if (! (b->mb = Xrealloc (b->mb, b->mbsize)) )
550		goto error;
551	}
552	rhs_string_mb = &b->mb[b->mbused];
553	b->mbused    += l;
554	strcpy(rhs_string_mb, tokenbuf);
555	token = nexttoken(fp, tokenbuf, &lastch);
556	if (token == KEY) {
557	    rhs_keysym = XStringToKeysym(tokenbuf);
558	    if (rhs_keysym == NoSymbol) {
559		goto error;
560	    }
561	    token = nexttoken(fp, tokenbuf, &lastch);
562	}
563	if (token != ENDOFLINE && token != ENDOFFILE) {
564	    goto error;
565	}
566    } else if (token == KEY) {
567	rhs_keysym = XStringToKeysym(tokenbuf);
568	if (rhs_keysym == NoSymbol) {
569	    goto error;
570	}
571	token = nexttoken(fp, tokenbuf, &lastch);
572	if (token != ENDOFLINE && token != ENDOFFILE) {
573	    goto error;
574	}
575
576        l = get_mb_string(im, local_mb_buf, rhs_keysym);
577	while (b->mbused + l + 1 > b->mbsize) {
578	    b->mbsize = b->mbsize ? b->mbsize * 1.5 : 1024;
579	    if (! (b->mb = Xrealloc (b->mb, b->mbsize)) )
580		goto error;
581	}
582	rhs_string_mb = &b->mb[b->mbused];
583	b->mbused    += l + 1;
584        memcpy(rhs_string_mb, local_mb_buf, l);
585	rhs_string_mb[l] = '\0';
586    } else {
587	goto error;
588    }
589
590    l = _Xmbstowcs(local_wc_buf, rhs_string_mb, LOCAL_WC_BUFSIZE - 1);
591    if (l == LOCAL_WC_BUFSIZE - 1) {
592	local_wc_buf[l] = (wchar_t)'\0';
593    }
594    while (b->wcused + l + 1 > b->wcsize) {
595	b->wcsize = b->wcsize ? b->wcsize * 1.5 : 512;
596	if (! (b->wc = Xrealloc (b->wc, sizeof(wchar_t) * b->wcsize)) )
597	    goto error;
598    }
599    rhs_string_wc = &b->wc[b->wcused];
600    b->wcused    += l + 1;
601    memcpy((char *)rhs_string_wc, (char *)local_wc_buf, (l + 1) * sizeof(wchar_t) );
602
603    l = _Xmbstoutf8(local_utf8_buf, rhs_string_mb, LOCAL_UTF8_BUFSIZE - 1);
604    if (l == LOCAL_UTF8_BUFSIZE - 1) {
605	local_utf8_buf[l] = '\0';
606    }
607    while (b->utf8used + l + 1 > b->utf8size) {
608	b->utf8size = b->utf8size ? b->utf8size * 1.5 : 1024;
609	if (! (b->utf8 = Xrealloc (b->utf8, b->utf8size)) )
610	    goto error;
611    }
612    rhs_string_utf8 = &b->utf8[b->utf8used];
613    b->utf8used    += l + 1;
614    memcpy(rhs_string_utf8, local_utf8_buf, l + 1);
615
616    for (i = 0; i < n; i++) {
617	for (t = *top; t; t = b->tree[t].next) {
618	    if (buf[i].keysym        == b->tree[t].keysym &&
619		buf[i].modifier      == b->tree[t].modifier &&
620		buf[i].modifier_mask == b->tree[t].modifier_mask) {
621		break;
622	    }
623	}
624	if (t) {
625	    p = &b->tree[t];
626	    top = &p->succession;
627	} else {
628	    while (b->treeused >= b->treesize) {
629		DefTree *old     = b->tree;
630		int      oldsize = b->treesize;
631		b->treesize = b->treesize ? b->treesize * 1.5 : 256;
632		if (! (b->tree = Xrealloc (b->tree, sizeof(DefTree) * b->treesize)) )
633		    goto error;
634		if (top >= (DTIndex *) old && top < (DTIndex *) &old[oldsize])
635		    top = (DTIndex *) (((char *) top) + (((char *)b->tree)-(char *)old));
636	    }
637	    p = &b->tree[b->treeused];
638	    p->keysym        = buf[i].keysym;
639	    p->modifier      = buf[i].modifier;
640	    p->modifier_mask = buf[i].modifier_mask;
641	    p->succession    = 0;
642	    p->next          = *top;
643	    p->mb            = 0;
644	    p->wc            = 0;
645	    p->utf8          = 0;
646	    p->ks            = NoSymbol;
647	    *top = b->treeused;
648	    top = &p->succession;
649	    b->treeused++;
650	}
651    }
652
653    /* old entries no longer freed... */
654    p->mb   = rhs_string_mb   - b->mb;
655    p->wc   = rhs_string_wc   - b->wc;
656    p->utf8 = rhs_string_utf8 - b->utf8;
657    p->ks   = rhs_keysym;
658    return(n);
659error:
660    while (token != ENDOFLINE && token != ENDOFFILE) {
661	token = nexttoken(fp, tokenbuf, &lastch);
662    }
663    return(0);
664}
665
666void
667_XimParseStringFile(
668    FILE *fp,
669    Xim   im)
670{
671    char tb[8192];
672    char* tbp;
673    struct stat st;
674
675    if (fstat (fileno (fp), &st) != -1) {
676	unsigned long size = (unsigned long) st.st_size;
677	if (size <= sizeof tb) tbp = tb;
678	else tbp = malloc (size);
679
680	if (tbp != NULL) {
681	    while (parseline(fp, im, tbp) >= 0) {}
682	    if (tbp != tb) free (tbp);
683	}
684    }
685}
686