1/*
2
3Copyright 1988, 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include <stdio.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35
36#include <X11/X.h>
37#include <X11/Xlib.h>
38#include "Xlibint.h"
39#include "Xlcint.h"
40#include "XlcPubI.h"
41#include "Ximint.h"
42#include <X11/Xutil.h>
43#include <X11/Xmd.h>
44#define XK_LATIN1
45#define XK_PUBLISHING
46#include <X11/keysym.h>
47#include <X11/extensions/XKBproto.h>
48#include "XKBlibint.h"
49#include <X11/Xlocale.h>
50#include <ctype.h>
51#include <X11/Xos.h>
52
53static int
54_XkbHandleSpecialSym(KeySym keysym, char *buffer, int nbytes, int *extra_rtrn)
55{
56    /* try to convert to Latin-1, handling ctrl */
57    if (!(((keysym >= XK_BackSpace) && (keysym <= XK_Clear)) ||
58          (keysym == XK_Return) || (keysym == XK_Escape) ||
59          (keysym == XK_KP_Space) || (keysym == XK_KP_Tab) ||
60          (keysym == XK_KP_Enter) ||
61          ((keysym >= XK_KP_Multiply) && (keysym <= XK_KP_9)) ||
62          (keysym == XK_KP_Equal) || (keysym == XK_Delete)))
63        return 0;
64
65    if (nbytes < 1) {
66        if (extra_rtrn)
67            *extra_rtrn = 1;
68        return 0;
69    }
70    /* if X keysym, convert to ascii by grabbing low 7 bits */
71    if (keysym == XK_KP_Space)
72        buffer[0] = XK_space & 0x7F;            /* patch encoding botch */
73    else
74        buffer[0] = (char) (keysym & 0x7F);
75    return 1;
76}
77
78/*ARGSUSED*/
79static int
80_XkbKSToKnownSet(XPointer priv,
81                 KeySym keysym,
82                 char *buffer,
83                 int nbytes,
84                 int *extra_rtrn)
85{
86    char tbuf[8], *buf;
87
88    if (extra_rtrn)
89        *extra_rtrn = 0;
90
91    /* convert "dead" diacriticals for dumb applications */
92    if ((keysym & 0xffffff00) == 0xfe00) {
93        switch (keysym) {
94        case XK_dead_grave:            keysym = XK_grave; break;
95        case XK_dead_acute:            keysym = XK_acute; break;
96        case XK_dead_circumflex:       keysym = XK_asciicircum; break;
97        case XK_dead_tilde:            keysym = XK_asciitilde; break;
98        case XK_dead_macron:           keysym = XK_macron; break;
99        case XK_dead_breve:            keysym = XK_breve; break;
100        case XK_dead_abovedot:         keysym = XK_abovedot; break;
101        case XK_dead_diaeresis:        keysym = XK_diaeresis; break;
102        case XK_dead_abovering:        keysym = XK_degree; break;
103        case XK_dead_doubleacute:      keysym = XK_doubleacute; break;
104        case XK_dead_caron:            keysym = XK_caron; break;
105        case XK_dead_cedilla:          keysym = XK_cedilla; break;
106        case XK_dead_ogonek:           keysym = XK_ogonek; break;
107        case XK_dead_iota:             keysym = XK_Greek_iota; break;
108#ifdef XK_KATAKANA
109        case XK_dead_voiced_sound:     keysym = XK_voicedsound; break;
110        case XK_dead_semivoiced_sound: keysym = XK_semivoicedsound; break;
111#endif
112        }
113    }
114
115    if (nbytes < 1)
116        buf = tbuf;
117    else
118        buf = buffer;
119
120    if ((keysym & 0xffffff00) == 0xff00) {
121        return _XkbHandleSpecialSym(keysym, buf, nbytes, extra_rtrn);
122    }
123    return _XimGetCharCode(priv, keysym, (unsigned char *) buf, nbytes);
124}
125
126typedef struct _XkbToKS {
127    unsigned prefix;
128    char *map;
129} XkbToKS;
130
131/*ARGSUSED*/
132static KeySym
133_XkbKnownSetToKS(XPointer priv, char *buffer, int nbytes, Status *status)
134{
135    if (nbytes != 1)
136        return NoSymbol;
137    if (((buffer[0] & 0x80) == 0) && (buffer[0] >= 32))
138        return buffer[0];
139    else if ((buffer[0] & 0x7f) >= 32) {
140        XkbToKS *map = (XkbToKS *) priv;
141
142        if (map) {
143            if (map->map)
144                return map->prefix | map->map[buffer[0] & 0x7f];
145            else
146                return map->prefix | buffer[0];
147        }
148        return buffer[0];
149    }
150    return NoSymbol;
151}
152
153static KeySym
154__XkbDefaultToUpper(KeySym sym)
155{
156    KeySym lower, upper;
157
158    XConvertCase(sym, &lower, &upper);
159    return upper;
160}
161
162#ifdef XKB_EXTEND_LOOKUP_STRING
163static int
164Strcmp(char *str1, char *str2)
165{
166    char str[256];
167    char c, *s;
168
169    /*
170     * unchecked strings from the environment can end up here, so check
171     * the length before copying.
172     */
173    if (strlen(str1) >= sizeof(str))    /* almost certain it's a mismatch */
174        return 1;
175
176    for (s = str; (c = *str1++);) {
177        if (isupper(c))
178            c = tolower(c);
179        *s++ = c;
180    }
181    *s = '\0';
182    return (strcmp(str, str2));
183}
184#endif
185
186int
187_XkbGetConverters(const char *encoding_name, XkbConverters * cvt_rtrn)
188{
189    if (!cvt_rtrn)
190        return 0;
191
192    cvt_rtrn->KSToMB = _XkbKSToKnownSet;
193    cvt_rtrn->KSToMBPriv = _XimGetLocaleCode(encoding_name);
194    cvt_rtrn->MBToKS = _XkbKnownSetToKS;
195    cvt_rtrn->MBToKSPriv = NULL;
196    cvt_rtrn->KSToUpper = __XkbDefaultToUpper;
197    return 1;
198}
199
200/***====================================================================***/
201
202/*
203 * The function _XkbGetCharset seems to be missnamed as what it seems to
204 * be used for is to determine the encoding-name for the locale. ???
205 */
206
207#ifdef XKB_EXTEND_LOOKUP_STRING
208
209/*
210 * XKB_EXTEND_LOOKUP_STRING is not used by the SI. It is used by various
211 * X Consortium/X Project Team members, so we leave it in the source as
212 * an simplify integration by these companies.
213 */
214
215#define	CHARSET_FILE	"/usr/lib/X11/input/charsets"
216static char *_XkbKnownLanguages =
217    "c=ascii:da,de,en,es,fr,is,it,nl,no,pt,sv=iso8859-1:hu,pl,cs=iso8859-2:"
218    "eo=iso8859-3:sp=iso8859-5:ar,ara=iso8859-6:el=iso8859-7:he=iso8859-8:"
219    "tr=iso8859-9:lt,lv=iso8859-13:et,fi=iso8859-15:ru=koi8-r:uk=koi8-u:"
220    "th,th_TH,th_TH.iso8859-11=iso8859-11:th_TH.TIS620=tis620:hy=armscii-8:"
221    "vi=tcvn-5712:ka=georgian-academy:be,bg=microsoft-cp1251";
222
223char *
224_XkbGetCharset(void)
225{
226    /*
227     * PAGE USAGE TUNING: explicitly initialize to move these to data
228     * instead of bss
229     */
230    static char buf[100] = { 0 };
231    char lang[256];
232    char *start, *tmp, *end, *next, *set;
233    char *country, *charset;
234    char *locale;
235
236    tmp = getenv("_XKB_CHARSET");
237    if (tmp)
238        return tmp;
239    locale = setlocale(LC_CTYPE, NULL);
240
241    if (locale == NULL)
242        return NULL;
243
244    if (strlen(locale) >= sizeof(lang))
245        return NULL;
246
247    for (tmp = lang; *tmp = *locale++; tmp++) {
248        if (isupper(*tmp))
249            *tmp = tolower(*tmp);
250    }
251    country = strchr(lang, '_');
252    if (country) {
253        *country++ = '\0';
254        charset = strchr(country, '.');
255        if (charset)
256            *charset++ = '\0';
257        if (charset) {
258            strncpy(buf, charset, 99);
259            buf[99] = '\0';
260            return buf;
261        }
262    }
263    else {
264        charset = NULL;
265    }
266
267    if ((tmp = getenv("_XKB_LOCALE_CHARSETS")) != NULL) {
268        start = _XkbAlloc(strlen(tmp) + 1);
269        strcpy(start, tmp);
270        tmp = start;
271    }
272    else {
273        struct stat sbuf;
274        FILE *file;
275        char *cf = CHARSET_FILE;
276
277#ifndef S_ISREG
278# define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
279#endif
280
281        if ((stat(cf, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
282            (file = fopen(cf, "r"))) {
283            tmp = _XkbAlloc(sbuf.st_size + 1);
284            if (tmp != NULL) {
285                sbuf.st_size = (long) fread(tmp, 1, sbuf.st_size, file);
286                tmp[sbuf.st_size] = '\0';
287            }
288            fclose(file);
289        }
290    }
291
292    if (tmp == NULL) {
293        tmp = _XkbAlloc(strlen(_XkbKnownLanguages) + 1);
294        if (!tmp)
295            return NULL;
296        strcpy(tmp, _XkbKnownLanguages);
297    }
298    start = tmp;
299    do {
300        if ((set = strchr(tmp, '=')) == NULL)
301            break;
302        *set++ = '\0';
303        if ((next = strchr(set, ':')) != NULL)
304            *next++ = '\0';
305        while (tmp && *tmp) {
306            if ((end = strchr(tmp, ',')) != NULL)
307                *end++ = '\0';
308            if (Strcmp(tmp, lang) == 0) {
309                strncpy(buf, set, 100);
310                buf[99] = '\0';
311                Xfree(start);
312                return buf;
313            }
314            tmp = end;
315        }
316        tmp = next;
317    } while (tmp && *tmp);
318    Xfree(start);
319    return NULL;
320}
321#else
322char *
323_XkbGetCharset(void)
324{
325    char *tmp;
326    XLCd lcd;
327
328    tmp = getenv("_XKB_CHARSET");
329    if (tmp)
330        return tmp;
331
332    lcd = _XlcCurrentLC();
333    if (lcd)
334        return XLC_PUBLIC(lcd, encoding_name);
335
336    return NULL;
337}
338#endif
339