imLcPrs.c revision 2d67cb4f
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#include <limits.h>
45#include "pathmax.h"
46
47#define XLC_BUFSIZE 256
48
49extern int _Xmbstowcs(
50    wchar_t	*wstr,
51    char	*str,
52    int		len
53);
54
55extern int _Xmbstoutf8(
56    char	*ustr,
57    const char	*str,
58    int		len
59);
60
61static void parsestringfile(FILE *fp, Xim im, int depth);
62
63/*
64 *	Parsing File Format:
65 *
66 *	FILE          ::= { [PRODUCTION] [COMMENT] "\n"}
67 *	PRODUCTION    ::= LHS ":" RHS [ COMMENT ]
68 *	COMMENT       ::= "#" {<any character except null or newline>}
69 *	LHS           ::= EVENT { EVENT }
70 *	EVENT         ::= [MODIFIER_LIST] "<" keysym ">"
71 *	MODIFIER_LIST ::= (["!"] {MODIFIER} ) | "None"
72 *	MODIFIER      ::= ["~"] MODIFIER_NAME
73 *	MODIFIER_NAME ::= ("Ctrl"|"Lock"|"Caps"|"Shift"|"Alt"|"Meta")
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 = NULL, *j;
314   size_t l = 0;
315
316   while (*i) {
317      if (*i == '%') {
318   	  i++;
319   	  switch (*i) {
320   	      case '%':
321                 l++;
322   	         break;
323   	      case 'H':
324                 if (home == NULL)
325                     home = getenv("HOME");
326                 if (home) {
327                     size_t Hsize = strlen(home);
328                     if (Hsize > PATH_MAX)
329                         /* your home directory length is ridiculous */
330                         goto end;
331                     l += Hsize;
332                 }
333   	         break;
334   	      case 'L':
335                 if (lcCompose == NULL)
336                     lcCompose = _XlcFileName(im->core.lcd, COMPOSE_FILE);
337                 if (lcCompose) {
338                     size_t Lsize = strlen(lcCompose);
339                     if (Lsize > PATH_MAX)
340                         /* your compose pathname length is ridiculous */
341                         goto end;
342                     l += Lsize;
343                 }
344   	         break;
345   	      case 'S':
346                 if (dir[0] == '\0')
347                     xlocaledir(dir, XLC_BUFSIZE);
348                 if (dir[0]) {
349                     size_t Ssize = strlen(dir);
350                     if (Ssize > PATH_MAX)
351                         /* your locale directory path length is ridiculous */
352                         goto end;
353                     l += Ssize;
354                 }
355   	         break;
356   	  }
357      } else {
358      	  l++;
359      }
360      i++;
361      if (l > PATH_MAX)
362          /* your expanded path length is ridiculous */
363          goto end;
364   }
365
366   j = ret = Xmalloc(l+1);
367   if (ret == NULL)
368      goto end;
369   i = name;
370   while (*i) {
371      if (*i == '%') {
372   	  i++;
373   	  switch (*i) {
374   	      case '%':
375                 *j++ = '%';
376   	         break;
377   	      case 'H':
378   	         if (home) {
379   	             strcpy(j, home);
380   	             j += strlen(home);
381   	         }
382   	         break;
383   	      case 'L':
384   	         if (lcCompose) {
385                    strcpy(j, lcCompose);
386                    j += strlen(lcCompose);
387                 }
388   	         break;
389   	      case 'S':
390                 strcpy(j, dir);
391                 j += strlen(dir);
392   	         break;
393   	  }
394          i++;
395      } else {
396      	  *j++ = *i++;
397      }
398   }
399   *j = '\0';
400end:
401   Xfree(lcCompose);
402   return ret;
403}
404
405#ifndef MB_LEN_MAX
406#define MB_LEN_MAX 6
407#endif
408
409static int
410get_mb_string (Xim im, char *buf, KeySym ks)
411{
412    XPointer from, to;
413    int from_len, to_len, len;
414    XPointer args[1];
415    XlcCharSet charset;
416    char local_buf[MB_LEN_MAX];
417    unsigned int ucs;
418    ucs = KeySymToUcs4(ks);
419
420    from = (XPointer) &ucs;
421    to =   (XPointer) local_buf;
422    from_len = 1;
423    to_len = MB_LEN_MAX;
424    args[0] = (XPointer) &charset;
425    if (_XlcConvert(im->private.local.ucstoc_conv,
426                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
427         return 0;
428    }
429
430    from = (XPointer) local_buf;
431    to =   (XPointer) buf;
432    from_len = MB_LEN_MAX - to_len;
433    to_len = MB_LEN_MAX + 1;
434    args[0] = (XPointer) charset;
435    if (_XlcConvert(im->private.local.cstomb_conv,
436                    &from, &from_len, &to, &to_len, args, 1 ) != 0) {
437         return 0;
438    }
439    len = MB_LEN_MAX + 1 - to_len;
440    buf[len] = '\0';
441    return len;
442}
443
444#define AllMask (ShiftMask | LockMask | ControlMask | Mod1Mask)
445#define LOCAL_WC_BUFSIZE 128
446#define LOCAL_UTF8_BUFSIZE 256
447#define SEQUENCE_MAX	10
448
449static int
450parseline(
451    FILE *fp,
452    Xim   im,
453    char* tokenbuf,
454    int   depth)
455{
456    int token;
457    DTModifier modifier_mask;
458    DTModifier modifier;
459    DTModifier tmp;
460    KeySym keysym = NoSymbol;
461    DTIndex *top = &im->private.local.top;
462    DefTreeBase *b   = &im->private.local.base;
463    DTIndex t;
464    DefTree *p = NULL;
465    Bool exclam, tilde;
466    KeySym rhs_keysym = 0;
467    char *rhs_string_mb;
468    int l;
469    int lastch = 0;
470    char local_mb_buf[MB_LEN_MAX+1];
471    wchar_t local_wc_buf[LOCAL_WC_BUFSIZE], *rhs_string_wc;
472    char local_utf8_buf[LOCAL_UTF8_BUFSIZE], *rhs_string_utf8;
473
474    struct DefBuffer {
475	DTModifier modifier_mask;
476	DTModifier modifier;
477	KeySym keysym;
478    };
479
480    struct DefBuffer buf[SEQUENCE_MAX];
481    int i, n;
482
483    do {
484	token = nexttoken(fp, tokenbuf, &lastch);
485    } while (token == ENDOFLINE);
486
487    if (token == ENDOFFILE) {
488	return(-1);
489    }
490
491    n = 0;
492    do {
493    	if ((token == KEY) && (strcmp("include", tokenbuf) == 0)) {
494            char *filename;
495            FILE *infp;
496            token = nexttoken(fp, tokenbuf, &lastch);
497            if (token != KEY && token != STRING)
498                goto error;
499            if (++depth > 100)
500                goto error;
501            if ((filename = TransFileName(im, tokenbuf)) == NULL)
502                goto error;
503            infp = _XFopenFile(filename, "r");
504            Xfree(filename);
505            if (infp == NULL)
506                goto error;
507            parsestringfile(infp, im, depth);
508            fclose(infp);
509            return (0);
510	} else if ((token == KEY) && (strcmp("None", tokenbuf) == 0)) {
511	    modifier = 0;
512	    modifier_mask = AllMask;
513	    token = nexttoken(fp, tokenbuf, &lastch);
514	} else {
515	    modifier_mask = modifier = 0;
516	    exclam = False;
517	    if (token == EXCLAM) {
518		exclam = True;
519		token = nexttoken(fp, tokenbuf, &lastch);
520	    }
521	    while (token == TILDE || token == KEY) {
522		tilde = False;
523		if (token == TILDE) {
524		    tilde = True;
525		    token = nexttoken(fp, tokenbuf, &lastch);
526		    if (token != KEY)
527			goto error;
528		}
529		tmp = modmask(tokenbuf);
530		if (!tmp) {
531		    goto error;
532		}
533		modifier_mask |= tmp;
534		if (tilde) {
535		    modifier &= ~tmp;
536		} else {
537		    modifier |= tmp;
538		}
539		token = nexttoken(fp, tokenbuf, &lastch);
540	    }
541	    if (exclam) {
542		modifier_mask = AllMask;
543	    }
544	}
545
546	if (token != LESS) {
547	    goto error;
548	}
549
550	token = nexttoken(fp, tokenbuf, &lastch);
551	if (token != KEY) {
552	    goto error;
553	}
554
555	token = nexttoken(fp, tokenbuf, &lastch);
556	if (token != GREATER) {
557	    goto error;
558	}
559
560	keysym = XStringToKeysym(tokenbuf);
561	if (keysym == NoSymbol) {
562	    goto error;
563	}
564
565	buf[n].keysym = keysym;
566	buf[n].modifier = modifier;
567	buf[n].modifier_mask = modifier_mask;
568	n++;
569	if( n >= SEQUENCE_MAX )
570	    goto error;
571	token = nexttoken(fp, tokenbuf, &lastch);
572    } while (token != COLON);
573
574    token = nexttoken(fp, tokenbuf, &lastch);
575    if (token == STRING) {
576	l = strlen(tokenbuf) + 1;
577	while (b->mbused + l > b->mbsize) {
578	    DTCharIndex newsize = b->mbsize ? b->mbsize * 1.5 : 1024;
579	    char *newmb = Xrealloc (b->mb, newsize);
580	    if (newmb == NULL)
581		goto error;
582	    b->mb = newmb;
583	    b->mbsize = newsize;
584	}
585	rhs_string_mb = &b->mb[b->mbused];
586	b->mbused    += l;
587	strcpy(rhs_string_mb, tokenbuf);
588	token = nexttoken(fp, tokenbuf, &lastch);
589	if (token == KEY) {
590	    rhs_keysym = XStringToKeysym(tokenbuf);
591	    if (rhs_keysym == NoSymbol) {
592		goto error;
593	    }
594	    token = nexttoken(fp, tokenbuf, &lastch);
595	}
596	if (token != ENDOFLINE && token != ENDOFFILE) {
597	    goto error;
598	}
599    } else if (token == KEY) {
600	rhs_keysym = XStringToKeysym(tokenbuf);
601	if (rhs_keysym == NoSymbol) {
602	    goto error;
603	}
604	token = nexttoken(fp, tokenbuf, &lastch);
605	if (token != ENDOFLINE && token != ENDOFFILE) {
606	    goto error;
607	}
608
609        l = get_mb_string(im, local_mb_buf, rhs_keysym);
610	while (b->mbused + l + 1 > b->mbsize) {
611	    DTCharIndex newsize = b->mbsize ? b->mbsize * 1.5 : 1024;
612	    char *newmb = Xrealloc (b->mb, newsize);
613	    if (newmb == NULL)
614		goto error;
615	    b->mb = newmb;
616	    b->mbsize = newsize;
617	}
618	rhs_string_mb = &b->mb[b->mbused];
619	b->mbused    += l + 1;
620        memcpy(rhs_string_mb, local_mb_buf, l);
621	rhs_string_mb[l] = '\0';
622    } else {
623	goto error;
624    }
625
626    l = _Xmbstowcs(local_wc_buf, rhs_string_mb, LOCAL_WC_BUFSIZE - 1);
627    if (l == LOCAL_WC_BUFSIZE - 1) {
628	local_wc_buf[l] = (wchar_t)'\0';
629    }
630    while (b->wcused + l + 1 > b->wcsize) {
631	DTCharIndex newsize = b->wcsize ? b->wcsize * 1.5 : 512;
632	wchar_t *newwc = Xrealloc (b->wc, sizeof(wchar_t) * newsize);
633	if (newwc == NULL)
634	    goto error;
635	b->wc = newwc;
636	b->wcsize = newsize;
637    }
638    rhs_string_wc = &b->wc[b->wcused];
639    b->wcused    += l + 1;
640    memcpy((char *)rhs_string_wc, (char *)local_wc_buf, (l + 1) * sizeof(wchar_t) );
641
642    l = _Xmbstoutf8(local_utf8_buf, rhs_string_mb, LOCAL_UTF8_BUFSIZE - 1);
643    if (l == LOCAL_UTF8_BUFSIZE - 1) {
644	local_utf8_buf[l] = '\0';
645    }
646    while (b->utf8used + l + 1 > b->utf8size) {
647	DTCharIndex newsize = b->utf8size ? b->utf8size * 1.5 : 1024;
648	char *newutf8 = Xrealloc (b->utf8, newsize);
649	if (newutf8 == NULL)
650	    goto error;
651	b->utf8 = newutf8;
652	b->utf8size = newsize;
653    }
654    rhs_string_utf8 = &b->utf8[b->utf8used];
655    b->utf8used    += l + 1;
656    memcpy(rhs_string_utf8, local_utf8_buf, l + 1);
657
658    for (i = 0; i < n; i++) {
659	for (t = *top; t; t = b->tree[t].next) {
660	    if (buf[i].keysym        == b->tree[t].keysym &&
661		buf[i].modifier      == b->tree[t].modifier &&
662		buf[i].modifier_mask == b->tree[t].modifier_mask) {
663		break;
664	    }
665	}
666	if (t) {
667	    p = &b->tree[t];
668	    top = &p->succession;
669	} else {
670	    while (b->treeused >= b->treesize) {
671		DefTree *old     = b->tree;
672		int      oldsize = b->treesize;
673		int      newsize = b->treesize ? b->treesize * 1.5 : 256;
674		DefTree *new     = Xrealloc (b->tree, sizeof(DefTree) * newsize);
675		if (new == NULL)
676		    goto error;
677		b->tree = new;
678		b->treesize = newsize;
679		if (top >= (DTIndex *) old && top < (DTIndex *) &old[oldsize])
680		    top = (DTIndex *) (((char *) top) + (((char *)b->tree)-(char *)old));
681	    }
682	    p = &b->tree[b->treeused];
683	    p->keysym        = buf[i].keysym;
684	    p->modifier      = buf[i].modifier;
685	    p->modifier_mask = buf[i].modifier_mask;
686	    p->succession    = 0;
687	    p->next          = *top;
688	    p->mb            = 0;
689	    p->wc            = 0;
690	    p->utf8          = 0;
691	    p->ks            = NoSymbol;
692	    *top = b->treeused;
693	    top = &p->succession;
694	    b->treeused++;
695	}
696    }
697
698    /* old entries no longer freed... */
699    p->mb   = rhs_string_mb   - b->mb;
700    p->wc   = rhs_string_wc   - b->wc;
701    p->utf8 = rhs_string_utf8 - b->utf8;
702    p->ks   = rhs_keysym;
703    return(n);
704error:
705    while (token != ENDOFLINE && token != ENDOFFILE) {
706	token = nexttoken(fp, tokenbuf, &lastch);
707    }
708    return(0);
709}
710
711void
712_XimParseStringFile(
713    FILE *fp,
714    Xim   im)
715{
716    parsestringfile(fp, im, 0);
717}
718
719static void
720parsestringfile(
721    FILE *fp,
722    Xim   im,
723    int   depth)
724{
725    char tb[8192];
726    char* tbp;
727    struct stat st;
728
729    if (fstat (fileno (fp), &st) != -1) {
730	unsigned long size = (unsigned long) st.st_size;
731	if (st.st_size >= INT_MAX)
732	    return;
733	if (size <= sizeof tb) tbp = tb;
734	else tbp = malloc (size);
735
736	if (tbp != NULL) {
737	    while (parseline(fp, im, tbp, depth) >= 0) {}
738	    if (tbp != tb) free (tbp);
739	}
740    }
741}
742