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