1/************************************************************
2 Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc.
3
4 Permission to use, copy, modify, and distribute this
5 software and its documentation for any purpose and without
6 fee is hereby granted, provided that the above copyright
7 notice appear in all copies and that both that copyright
8 notice and this permission notice appear in supporting
9 documentation, and that the name of Silicon Graphics not be
10 used in advertising or publicity pertaining to distribution
11 of the software without specific prior written permission.
12 Silicon Graphics makes no representation about the suitability
13 of this software for any purpose. It is provided "as is"
14 without any express or implied warranty.
15
16 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23 THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
25 ********************************************************/
26
27#include "xkbcomp.h"
28#include "misc.h"
29#include "alias.h"
30#include "keycodes.h"
31
32#include <X11/extensions/XKBgeom.h>
33
34static void
35HandleCollision(AliasInfo * old, AliasInfo * new)
36{
37    if (strncmp(new->real, old->real, XkbKeyNameLength) == 0)
38    {
39        if (((new->def.fileID == old->def.fileID) && (warningLevel > 0)) ||
40            (warningLevel > 9))
41        {
42            WARN("Alias of %s for %s declared more than once\n",
43                  XkbKeyNameText(new->alias, XkbMessage),
44                  XkbKeyNameText(new->real, XkbMessage));
45            ACTION("First definition ignored\n");
46        }
47    }
48    else
49    {
50        char *use, *ignore;
51        if (new->def.merge == MergeAugment)
52        {
53            use = old->real;
54            ignore = new->real;
55        }
56        else
57        {
58            use = new->real;
59            ignore = old->real;
60        }
61        if (((old->def.fileID == new->def.fileID) && (warningLevel > 0)) ||
62            (warningLevel > 9))
63        {
64            WARN("Multiple definitions for alias %s\n",
65                  XkbKeyNameText(old->alias, XkbMessage));
66            ACTION("Using %s, ignoring %s\n",
67                    XkbKeyNameText(use, XkbMessage),
68                    XkbKeyNameText(ignore, XkbMessage));
69        }
70        if (use != old->real)
71            memcpy(old->real, use, XkbKeyNameLength);
72    }
73    old->def.fileID = new->def.fileID;
74    old->def.merge = new->def.merge;
75    return;
76}
77
78static void
79InitAliasInfo(AliasInfo *info, unsigned merge, unsigned file_id,
80              const char *alias, const char *real)
81{
82    bzero(info, sizeof(AliasInfo));
83    info->def.merge = merge;
84    info->def.fileID = file_id;
85    strncpy(info->alias, alias, XkbKeyNameLength);
86    strncpy(info->real, real, XkbKeyNameLength);
87    return;
88}
89
90int
91HandleAliasDef(const KeyAliasDef *def,
92               unsigned merge, unsigned file_id, AliasInfo **info_in)
93{
94    AliasInfo *info;
95
96    for (info = *info_in; info != NULL; info = (AliasInfo *) info->def.next)
97    {
98        if (strncmp(info->alias, def->alias, XkbKeyNameLength) == 0)
99        {
100            AliasInfo new;
101            InitAliasInfo(&new, merge, file_id, def->alias, def->real);
102            HandleCollision(info, &new);
103            return True;
104        }
105    }
106    info = calloc(1, sizeof(AliasInfo));
107    if (info == NULL)
108    {
109        WSGO("Allocation failure in HandleAliasDef\n");
110        return False;
111    }
112    info->def.fileID = file_id;
113    info->def.merge = merge;
114    info->def.next = (CommonInfo *) * info_in;
115    memcpy(info->alias, def->alias, XkbKeyNameLength);
116    memcpy(info->real, def->real, XkbKeyNameLength);
117    *info_in = (AliasInfo *) AddCommonInfo(&(*info_in)->def, &info->def);
118    return True;
119}
120
121void
122ClearAliases(AliasInfo ** info_in)
123{
124    if ((info_in) && (*info_in))
125        ClearCommonInfo(&(*info_in)->def);
126    return;
127}
128
129Bool
130MergeAliases(AliasInfo ** into, AliasInfo ** merge, unsigned how_merge)
131{
132    AliasInfo *tmp;
133    KeyAliasDef def;
134
135    if ((*merge) == NULL)
136        return True;
137    if ((*into) == NULL)
138    {
139        *into = *merge;
140        *merge = NULL;
141        return True;
142    }
143    bzero(&def, sizeof(KeyAliasDef));
144    for (tmp = *merge; tmp != NULL; tmp = (AliasInfo *) tmp->def.next)
145    {
146        if (how_merge == MergeDefault)
147            def.merge = tmp->def.merge;
148        else
149            def.merge = how_merge;
150        memcpy(def.alias, tmp->alias, XkbKeyNameLength);
151        memcpy(def.real, tmp->real, XkbKeyNameLength);
152        if (!HandleAliasDef(&def, def.merge, tmp->def.fileID, into))
153            return False;
154    }
155    return True;
156}
157
158int
159ApplyAliases(XkbDescPtr xkb, Bool toGeom, AliasInfo ** info_in)
160{
161    int i;
162    XkbKeyAliasPtr old, a;
163    AliasInfo *info;
164    int nNew, nOld;
165    Status status;
166
167    if (*info_in == NULL)
168        return True;
169    if (toGeom)
170    {
171        nOld = (xkb->geom ? xkb->geom->num_key_aliases : 0);
172        old = (xkb->geom ? xkb->geom->key_aliases : NULL);
173    }
174    else
175    {
176        nOld = (xkb->names ? xkb->names->num_key_aliases : 0);
177        old = (xkb->names ? xkb->names->key_aliases : NULL);
178    }
179    for (nNew = 0, info = *info_in; info != NULL;
180         info = (AliasInfo *) info->def.next)
181    {
182        unsigned long lname;
183        unsigned int kc;
184
185        lname = KeyNameToLong(info->real);
186        if (!FindNamedKey(xkb, lname, &kc, False, CreateKeyNames(xkb), 0))
187        {
188            if (warningLevel > 4)
189            {
190                WARN("Attempt to alias %s to non-existent key %s\n",
191                      XkbKeyNameText(info->alias, XkbMessage),
192                      XkbKeyNameText(info->real, XkbMessage));
193                ACTION("Ignored\n");
194            }
195            info->alias[0] = '\0';
196            continue;
197        }
198        lname = KeyNameToLong(info->alias);
199        if (FindNamedKey(xkb, lname, &kc, False, False, 0))
200        {
201            if (warningLevel > 4)
202            {
203                WARN("Attempt to create alias with the name of a real key\n");
204                ACTION("Alias \"%s = %s\" ignored\n",
205                        XkbKeyNameText(info->alias, XkbMessage),
206                        XkbKeyNameText(info->real, XkbMessage));
207            }
208            info->alias[0] = '\0';
209            continue;
210        }
211        nNew++;
212        if (old)
213        {
214            for (i = 0, a = old; i < nOld; i++, a++)
215            {
216                if (strncmp(a->alias, info->alias, XkbKeyNameLength) == 0)
217                {
218                    AliasInfo oldai;
219                    InitAliasInfo(&oldai, MergeAugment, 0, a->alias, a->real);
220                    HandleCollision(&oldai, info);
221                    memcpy(oldai.real, a->real, XkbKeyNameLength);
222                    info->alias[0] = '\0';
223                    nNew--;
224                    break;
225                }
226            }
227        }
228    }
229    if (nNew == 0)
230    {
231        ClearCommonInfo(&(*info_in)->def);
232        *info_in = NULL;
233        return True;
234    }
235    status = Success;
236    if (toGeom)
237    {
238        if (!xkb->geom)
239        {
240            XkbGeometrySizesRec sizes = {
241                .which = XkbGeomKeyAliasesMask,
242                .num_key_aliases = nOld + nNew
243            };
244            status = XkbAllocGeometry(xkb, &sizes);
245        }
246        else
247        {
248            status = XkbAllocGeomKeyAliases(xkb->geom, nOld + nNew);
249        }
250        if (xkb->geom)
251            old = xkb->geom->key_aliases;
252    }
253    else
254    {
255        status = XkbAllocNames(xkb, XkbKeyAliasesMask, 0, nOld + nNew);
256        if (xkb->names)
257            old = xkb->names->key_aliases;
258    }
259    if (status != Success)
260    {
261        WSGO("Allocation failure in ApplyAliases\n");
262        return False;
263    }
264    if (toGeom)
265        a = &xkb->geom->key_aliases[nOld];
266    else
267        a = &xkb->names->key_aliases[nOld];
268    for (info = *info_in; info != NULL; info = (AliasInfo *) info->def.next)
269    {
270        if (info->alias[0] != '\0')
271        {
272            strncpy(a->alias, info->alias, XkbKeyNameLength);
273            strncpy(a->real, info->real, XkbKeyNameLength);
274            a++;
275        }
276    }
277#ifdef DEBUG
278    if ((a - old) != (nOld + nNew))
279    {
280        WSGO("Expected %d aliases total but created %d\n", nOld + nNew,
281              (int)(a - old));
282    }
283#endif
284    if (toGeom)
285        xkb->geom->num_key_aliases += nNew;
286    ClearCommonInfo(&(*info_in)->def);
287    *info_in = NULL;
288    return True;
289}
290