imLcPrs.c revision 1ab64890
1/* $Xorg: imLcPrs.c,v 1.3 2000/08/17 19:45:14 cpqbld Exp $ */
2/******************************************************************
3
4              Copyright 1992 by Oki Technosystems Laboratory, Inc.
5              Copyright 1992 by Fuji Xerox Co., Ltd.
6
7Permission to use, copy, modify, distribute, and sell this software
8and its documentation for any purpose is hereby granted without fee,
9provided that the above copyright notice appear in all copies and
10that both that copyright notice and this permission notice appear
11in supporting documentation, and that the name of Oki Technosystems
12Laboratory and Fuji Xerox not be used in advertising or publicity
13pertaining to distribution of the software without specific, written
14prior permission.
15Oki Technosystems Laboratory and Fuji Xerox make no representations
16about the suitability of this software for any purpose.  It is provided
17"as is" without express or implied warranty.
18
19OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES
20WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
21MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS
22LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR
23CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
24OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
25OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
26OR PERFORMANCE OF THIS SOFTWARE.
27
28  Author: Yasuhiro Kawai	Oki Technosystems Laboratory
29  Author: Kazunori Nishihara	Fuji Xerox
30
31******************************************************************/
32
33/* $XFree86: xc/lib/X11/imLcPrs.c,v 1.10 2003/09/06 14:06:32 pascal Exp $ */
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif
38#include <X11/Xlib.h>
39#include <X11/Xmd.h>
40#include <X11/Xos.h>
41#include "Xlibint.h"
42#include "Xlcint.h"
43#include "Ximint.h"
44#include <sys/stat.h>
45#include <stdio.h>
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 *i = name, *ret, *j;
308   int l = 0;
309
310   while (*i) {
311      if (*i == '%') {
312   	  i++;
313   	  switch (*i) {
314   	      case '%':
315                 l++;
316   	         break;
317   	      case 'H':
318   	         home = getenv("HOME");
319   	         if (home)
320                     l += strlen(home);
321   	         break;
322   	      case 'L':
323                 lcCompose = _XlcFileName(im->core.lcd, COMPOSE_FILE);
324                 if (lcCompose)
325                     l += strlen(lcCompose);
326   	         break;
327   	  }
328      } else {
329      	  l++;
330      }
331      i++;
332   }
333
334   j = ret = Xmalloc(l+1);
335   if (ret == NULL)
336      return ret;
337   i = name;
338   while (*i) {
339      if (*i == '%') {
340   	  i++;
341   	  switch (*i) {
342   	      case '%':
343                 *j++ = '%';
344   	         break;
345   	      case 'H':
346   	         if (home) {
347   	             strcpy(j, home);
348   	             j += strlen(home);
349   	         }
350   	         break;
351   	      case 'L':
352   	         if (lcCompose) {
353                    strcpy(j, lcCompose);
354                    j += strlen(lcCompose);
355                    Xfree(lcCompose);
356                 }
357   	         break;
358   	  }
359          i++;
360      } else {
361      	  *j++ = *i++;
362      }
363   }
364   *j = '\0';
365   return ret;
366}
367
368#ifndef MB_LEN_MAX
369#define MB_LEN_MAX 6
370#endif
371
372static int
373get_mb_string (Xim im, char *buf, KeySym ks)
374{
375    XPointer from, to;
376    int from_len, to_len, len;
377    XPointer args[1];
378    XlcCharSet charset;
379    char local_buf[MB_LEN_MAX];
380    unsigned int ucs;
381    ucs = KeySymToUcs4(ks);
382
383    from = (XPointer) &ucs;
384    to =   (XPointer) local_buf;
385    from_len = 1;
386    to_len = MB_LEN_MAX;
387    args[0] = (XPointer) &charset;
388    if (_XlcConvert(im->private.local.ucstoc_conv,
389                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
390         return 0;
391    }
392
393    from = (XPointer) local_buf;
394    to =   (XPointer) buf;
395    from_len = MB_LEN_MAX - to_len;
396    to_len = MB_LEN_MAX + 1;
397    args[0] = (XPointer) charset;
398    if (_XlcConvert(im->private.local.cstomb_conv,
399                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
400         return 0;
401    }
402    len = MB_LEN_MAX + 1 - to_len;
403    buf[len] = '\0';
404    return len;
405}
406
407#define AllMask (ShiftMask | LockMask | ControlMask | Mod1Mask)
408#define LOCAL_WC_BUFSIZE 128
409#define LOCAL_UTF8_BUFSIZE 256
410#define SEQUENCE_MAX	10
411
412static int
413parseline(
414    FILE *fp,
415    Xim   im,
416    char* tokenbuf)
417{
418    int token;
419    DTModifier modifier_mask;
420    DTModifier modifier;
421    DTModifier tmp;
422    KeySym keysym = NoSymbol;
423    DTIndex *top = &im->private.local.top;
424    DefTreeBase *b   = &im->private.local.base;
425    DTIndex t;
426    DefTree *p = NULL;
427    Bool exclam, tilde;
428    KeySym rhs_keysym = 0;
429    char *rhs_string_mb;
430    int l;
431    int lastch = 0;
432    char local_mb_buf[MB_LEN_MAX+1];
433    wchar_t local_wc_buf[LOCAL_WC_BUFSIZE], *rhs_string_wc;
434    char local_utf8_buf[LOCAL_UTF8_BUFSIZE], *rhs_string_utf8;
435
436    struct DefBuffer {
437	DTModifier modifier_mask;
438	DTModifier modifier;
439	KeySym keysym;
440    };
441
442    struct DefBuffer buf[SEQUENCE_MAX];
443    int i, n;
444
445    do {
446	token = nexttoken(fp, tokenbuf, &lastch);
447    } while (token == ENDOFLINE);
448
449    if (token == ENDOFFILE) {
450	return(-1);
451    }
452
453    n = 0;
454    do {
455    	if ((token == KEY) && (strcmp("include", tokenbuf) == 0)) {
456            char *filename;
457            FILE *infp;
458            token = nexttoken(fp, tokenbuf, &lastch);
459            if (token != KEY && token != STRING)
460                goto error;
461            if ((filename = TransFileName(im, tokenbuf)) == NULL)
462                goto error;
463            infp = _XFopenFile(filename, "r");
464                Xfree(filename);
465            if (infp == NULL)
466                goto error;
467            _XimParseStringFile(infp, im);
468            fclose(infp);
469            return (0);
470	} else if ((token == KEY) && (strcmp("None", tokenbuf) == 0)) {
471	    modifier = 0;
472	    modifier_mask = AllMask;
473	    token = nexttoken(fp, tokenbuf, &lastch);
474	} else {
475	    modifier_mask = modifier = 0;
476	    exclam = False;
477	    if (token == EXCLAM) {
478		exclam = True;
479		token = nexttoken(fp, tokenbuf, &lastch);
480	    }
481	    while (token == TILDE || token == KEY) {
482		tilde = False;
483		if (token == TILDE) {
484		    tilde = True;
485		    token = nexttoken(fp, tokenbuf, &lastch);
486		    if (token != KEY)
487			goto error;
488		}
489		tmp = modmask(tokenbuf);
490		if (!tmp) {
491		    goto error;
492		}
493		modifier_mask |= tmp;
494		if (tilde) {
495		    modifier &= ~tmp;
496		} else {
497		    modifier |= tmp;
498		}
499		token = nexttoken(fp, tokenbuf, &lastch);
500	    }
501	    if (exclam) {
502		modifier_mask = AllMask;
503	    }
504	}
505
506	if (token != LESS) {
507	    goto error;
508	}
509
510	token = nexttoken(fp, tokenbuf, &lastch);
511	if (token != KEY) {
512	    goto error;
513	}
514
515	token = nexttoken(fp, tokenbuf, &lastch);
516	if (token != GREATER) {
517	    goto error;
518	}
519
520	keysym = XStringToKeysym(tokenbuf);
521	if (keysym == NoSymbol) {
522	    goto error;
523	}
524
525	buf[n].keysym = keysym;
526	buf[n].modifier = modifier;
527	buf[n].modifier_mask = modifier_mask;
528	n++;
529	if( n >= SEQUENCE_MAX )
530	    goto error;
531	token = nexttoken(fp, tokenbuf, &lastch);
532    } while (token != COLON);
533
534    token = nexttoken(fp, tokenbuf, &lastch);
535    if (token == STRING) {
536	l = strlen(tokenbuf) + 1;
537	while (b->mbused + l > b->mbsize) {
538	    b->mbsize = b->mbsize ? b->mbsize * 1.5 : 1024;
539	    if (! (b->mb = Xrealloc (b->mb, b->mbsize)) )
540		goto error;
541	}
542	rhs_string_mb = &b->mb[b->mbused];
543	b->mbused    += l;
544	strcpy(rhs_string_mb, tokenbuf);
545	token = nexttoken(fp, tokenbuf, &lastch);
546	if (token == KEY) {
547	    rhs_keysym = XStringToKeysym(tokenbuf);
548	    if (rhs_keysym == NoSymbol) {
549		goto error;
550	    }
551	    token = nexttoken(fp, tokenbuf, &lastch);
552	}
553	if (token != ENDOFLINE && token != ENDOFFILE) {
554	    goto error;
555	}
556    } else if (token == KEY) {
557	rhs_keysym = XStringToKeysym(tokenbuf);
558	if (rhs_keysym == NoSymbol) {
559	    goto error;
560	}
561	token = nexttoken(fp, tokenbuf, &lastch);
562	if (token != ENDOFLINE && token != ENDOFFILE) {
563	    goto error;
564	}
565
566        l = get_mb_string(im, local_mb_buf, rhs_keysym);
567	while (b->mbused + l + 1 > b->mbsize) {
568	    b->mbsize = b->mbsize ? b->mbsize * 1.5 : 1024;
569	    if (! (b->mb = Xrealloc (b->mb, b->mbsize)) )
570		goto error;
571	}
572	rhs_string_mb = &b->mb[b->mbused];
573	b->mbused    += l + 1;
574        memcpy(rhs_string_mb, local_mb_buf, l);
575	rhs_string_mb[l] = '\0';
576    } else {
577	goto error;
578    }
579
580    l = _Xmbstowcs(local_wc_buf, rhs_string_mb, LOCAL_WC_BUFSIZE - 1);
581    if (l == LOCAL_WC_BUFSIZE - 1) {
582	local_wc_buf[l] = (wchar_t)'\0';
583    }
584    while (b->wcused + l + 1 > b->wcsize) {
585	b->wcsize = b->wcsize ? b->wcsize * 1.5 : 512;
586	if (! (b->wc = Xrealloc (b->wc, sizeof(wchar_t) * b->wcsize)) )
587	    goto error;
588    }
589    rhs_string_wc = &b->wc[b->wcused];
590    b->wcused    += l + 1;
591    memcpy((char *)rhs_string_wc, (char *)local_wc_buf, (l + 1) * sizeof(wchar_t) );
592
593    l = _Xmbstoutf8(local_utf8_buf, rhs_string_mb, LOCAL_UTF8_BUFSIZE - 1);
594    if (l == LOCAL_UTF8_BUFSIZE - 1) {
595	local_wc_buf[l] = '\0';
596    }
597    while (b->utf8used + l + 1 > b->utf8size) {
598	b->utf8size = b->utf8size ? b->utf8size * 1.5 : 1024;
599	if (! (b->utf8 = Xrealloc (b->utf8, b->utf8size)) )
600	    goto error;
601    }
602    rhs_string_utf8 = &b->utf8[b->utf8used];
603    b->utf8used    += l + 1;
604    memcpy(rhs_string_utf8, local_utf8_buf, l + 1);
605
606    for (i = 0; i < n; i++) {
607	for (t = *top; t; t = b->tree[t].next) {
608	    if (buf[i].keysym        == b->tree[t].keysym &&
609		buf[i].modifier      == b->tree[t].modifier &&
610		buf[i].modifier_mask == b->tree[t].modifier_mask) {
611		break;
612	    }
613	}
614	if (t) {
615	    p = &b->tree[t];
616	    top = &p->succession;
617	} else {
618	    while (b->treeused >= b->treesize) {
619		DefTree *old     = b->tree;
620		int      oldsize = b->treesize;
621		b->treesize = b->treesize ? b->treesize * 1.5 : 256;
622		if (! (b->tree = Xrealloc (b->tree, sizeof(DefTree) * b->treesize)) )
623		    goto error;
624		if (top >= (DTIndex *) old && top < (DTIndex *) &old[oldsize])
625		    top = (DTIndex *) (((char *) top) + (((char *)b->tree)-(char *)old));
626	    }
627	    p = &b->tree[b->treeused];
628	    p->keysym        = buf[i].keysym;
629	    p->modifier      = buf[i].modifier;
630	    p->modifier_mask = buf[i].modifier_mask;
631	    p->succession    = 0;
632	    p->next          = *top;
633	    p->mb            = 0;
634	    p->wc            = 0;
635	    p->utf8          = 0;
636	    p->ks            = NoSymbol;
637	    *top = b->treeused;
638	    top = &p->succession;
639	    b->treeused++;
640	}
641    }
642
643    /* old entries no longer freed... */
644    p->mb   = rhs_string_mb   - b->mb;
645    p->wc   = rhs_string_wc   - b->wc;
646    p->utf8 = rhs_string_utf8 - b->utf8;
647    p->ks   = rhs_keysym;
648    return(n);
649error:
650    while (token != ENDOFLINE && token != ENDOFFILE) {
651	token = nexttoken(fp, tokenbuf, &lastch);
652    }
653    return(0);
654}
655
656void
657_XimParseStringFile(
658    FILE *fp,
659    Xim   im)
660{
661    char tb[8192];
662    char* tbp;
663    struct stat st;
664
665    if (fstat (fileno (fp), &st) != -1) {
666	unsigned long size = (unsigned long) st.st_size;
667	if (size <= sizeof tb) tbp = tb;
668	else tbp = malloc (size);
669
670	if (tbp != NULL) {
671	    while (parseline(fp, im, tbp) >= 0) {}
672	    if (tbp != tb) free (tbp);
673	}
674    }
675}
676