2009-03-27 07:55:32 -06:00
|
|
|
/************************************************************
|
|
|
|
Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and distribute this
|
|
|
|
software and its documentation for any purpose and without
|
|
|
|
fee is hereby granted, provided that the above copyright
|
|
|
|
notice appear in all copies and that both that copyright
|
|
|
|
notice and this permission notice appear in supporting
|
2009-04-04 10:19:51 -06:00
|
|
|
documentation, and that the name of Silicon Graphics not be
|
|
|
|
used in advertising or publicity pertaining to distribution
|
2009-03-27 07:55:32 -06:00
|
|
|
of the software without specific prior written permission.
|
2009-04-04 10:19:51 -06:00
|
|
|
Silicon Graphics makes no representation about the suitability
|
2009-03-27 07:55:32 -06:00
|
|
|
of this software for any purpose. It is provided "as is"
|
|
|
|
without any express or implied warranty.
|
2009-04-04 10:19:51 -06:00
|
|
|
|
|
|
|
SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
|
|
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
2009-03-27 07:55:32 -06:00
|
|
|
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
2009-04-04 10:19:51 -06:00
|
|
|
GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
|
|
|
|
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
|
|
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
2009-03-27 07:55:32 -06:00
|
|
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
|
|
THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
|
|
********************************************************/
|
|
|
|
|
|
|
|
#include "xkbcomp.h"
|
2009-04-08 08:46:25 -06:00
|
|
|
#include "xkballoc.h"
|
|
|
|
#include "xkbmisc.h"
|
2009-03-27 07:55:32 -06:00
|
|
|
#include "tokens.h"
|
|
|
|
#include "expr.h"
|
|
|
|
#include "vmod.h"
|
|
|
|
#include "action.h"
|
|
|
|
#include "misc.h"
|
|
|
|
|
|
|
|
typedef struct _PreserveInfo
|
|
|
|
{
|
|
|
|
CommonInfo defs;
|
|
|
|
short matchingMapIndex;
|
|
|
|
unsigned char indexMods;
|
|
|
|
unsigned char preMods;
|
|
|
|
unsigned short indexVMods;
|
|
|
|
unsigned short preVMods;
|
|
|
|
} PreserveInfo;
|
|
|
|
|
|
|
|
#define _KT_Name (1<<0)
|
|
|
|
#define _KT_Mask (1<<1)
|
|
|
|
#define _KT_Map (1<<2)
|
|
|
|
#define _KT_Preserve (1<<3)
|
|
|
|
#define _KT_LevelNames (1<<4)
|
|
|
|
|
|
|
|
typedef struct _KeyTypeInfo
|
|
|
|
{
|
|
|
|
CommonInfo defs;
|
2010-06-28 04:50:12 -06:00
|
|
|
uint32_t name;
|
2009-03-27 07:55:32 -06:00
|
|
|
int fileID;
|
|
|
|
unsigned mask;
|
|
|
|
unsigned vmask;
|
|
|
|
Bool groupInfo;
|
|
|
|
int numLevels;
|
|
|
|
int nEntries;
|
|
|
|
int szEntries;
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryPtr entries;
|
2009-03-27 07:55:32 -06:00
|
|
|
PreserveInfo *preserve;
|
|
|
|
int szNames;
|
2010-06-28 04:50:12 -06:00
|
|
|
uint32_t *lvlNames;
|
2009-03-27 07:55:32 -06:00
|
|
|
} KeyTypeInfo;
|
|
|
|
|
|
|
|
typedef struct _KeyTypesInfo
|
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
int errorCount;
|
|
|
|
int fileID;
|
|
|
|
unsigned stdPresent;
|
|
|
|
int nTypes;
|
|
|
|
KeyTypeInfo *types;
|
|
|
|
KeyTypeInfo dflt;
|
|
|
|
VModInfo vmods;
|
|
|
|
} KeyTypesInfo;
|
|
|
|
|
2010-06-28 04:50:12 -06:00
|
|
|
uint32_t tok_ONE_LEVEL;
|
|
|
|
uint32_t tok_TWO_LEVEL;
|
|
|
|
uint32_t tok_ALPHABETIC;
|
|
|
|
uint32_t tok_KEYPAD;
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
/***====================================================================***/
|
|
|
|
|
2009-03-28 15:09:36 -06:00
|
|
|
#define ReportTypeShouldBeArray(t, f) \
|
|
|
|
ReportShouldBeArray("key type", (f), TypeTxt(t))
|
|
|
|
#define ReportTypeBadType(t, f, w) \
|
|
|
|
ReportBadType("key type", (f), TypeTxt(t), (w))
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
/***====================================================================***/
|
|
|
|
|
2009-03-27 21:01:32 -06:00
|
|
|
extern Bool AddMapEntry(XkbcDescPtr /* xkb */ ,
|
2009-03-27 07:55:32 -06:00
|
|
|
KeyTypeInfo * /* type */ ,
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryPtr /* new */ ,
|
2009-03-27 07:55:32 -06:00
|
|
|
Bool /* clobber */ ,
|
|
|
|
Bool /* report */
|
|
|
|
);
|
|
|
|
|
2009-03-27 21:01:32 -06:00
|
|
|
extern Bool AddPreserve(XkbcDescPtr /* xkb */ ,
|
2009-03-27 07:55:32 -06:00
|
|
|
KeyTypeInfo * /* type */ ,
|
|
|
|
PreserveInfo * /* new */ ,
|
|
|
|
Bool /* clobber */ ,
|
|
|
|
Bool /* report */
|
|
|
|
);
|
|
|
|
|
|
|
|
extern Bool AddLevelName(KeyTypeInfo * /* type */ ,
|
|
|
|
unsigned /* level */ ,
|
2010-06-28 04:50:12 -06:00
|
|
|
uint32_t /* name */ ,
|
2009-03-27 07:55:32 -06:00
|
|
|
Bool /* clobber */ ,
|
|
|
|
Bool /* report */
|
|
|
|
);
|
|
|
|
|
2009-03-28 15:09:36 -06:00
|
|
|
#define MapEntryTxt(x, e) \
|
2009-03-28 16:12:30 -06:00
|
|
|
XkbcVModMaskText((x), (e)->mods.real_mods, (e)->mods.vmods)
|
2009-03-28 15:09:36 -06:00
|
|
|
#define PreserveIndexTxt(x, p) \
|
2009-03-28 16:12:30 -06:00
|
|
|
XkbcVModMaskText((x), (p)->indexMods, (p)->indexVMods)
|
2009-03-28 15:09:36 -06:00
|
|
|
#define PreserveTxt(x, p) \
|
2009-03-28 16:12:30 -06:00
|
|
|
XkbcVModMaskText((x), (p)->preMods, (p)->preVMods)
|
2009-03-28 15:09:36 -06:00
|
|
|
#define TypeTxt(t) \
|
|
|
|
XkbcAtomText((t)->name)
|
|
|
|
#define TypeMaskTxt(t, x) \
|
2009-03-28 16:12:30 -06:00
|
|
|
XkbcVModMaskText((x), (t)->mask, (t)->vmask)
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
/***====================================================================***/
|
|
|
|
|
|
|
|
static void
|
2009-03-27 21:01:32 -06:00
|
|
|
InitKeyTypesInfo(KeyTypesInfo * info, XkbcDescPtr xkb, KeyTypesInfo * from)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
2009-03-28 07:56:26 -06:00
|
|
|
tok_ONE_LEVEL = XkbcInternAtom("ONE_LEVEL", False);
|
|
|
|
tok_TWO_LEVEL = XkbcInternAtom("TWO_LEVEL", False);
|
|
|
|
tok_ALPHABETIC = XkbcInternAtom("ALPHABETIC", False);
|
|
|
|
tok_KEYPAD = XkbcInternAtom("KEYPAD", False);
|
2009-04-05 21:27:35 -06:00
|
|
|
info->name = strdup("default");
|
2009-03-27 07:55:32 -06:00
|
|
|
info->errorCount = 0;
|
|
|
|
info->stdPresent = 0;
|
|
|
|
info->nTypes = 0;
|
|
|
|
info->types = NULL;
|
|
|
|
info->dflt.defs.defined = 0;
|
|
|
|
info->dflt.defs.fileID = 0;
|
|
|
|
info->dflt.defs.merge = MergeOverride;
|
|
|
|
info->dflt.defs.next = NULL;
|
|
|
|
info->dflt.name = None;
|
|
|
|
info->dflt.mask = 0;
|
|
|
|
info->dflt.vmask = 0;
|
|
|
|
info->dflt.groupInfo = False;
|
|
|
|
info->dflt.numLevels = 1;
|
|
|
|
info->dflt.nEntries = info->dflt.szEntries = 0;
|
|
|
|
info->dflt.entries = NULL;
|
|
|
|
info->dflt.szNames = 0;
|
|
|
|
info->dflt.lvlNames = NULL;
|
|
|
|
info->dflt.preserve = NULL;
|
|
|
|
InitVModInfo(&info->vmods, xkb);
|
|
|
|
if (from != NULL)
|
|
|
|
{
|
|
|
|
info->dflt = from->dflt;
|
|
|
|
if (from->dflt.entries)
|
|
|
|
{
|
|
|
|
info->dflt.entries = uTypedCalloc(from->dflt.szEntries,
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryRec);
|
2009-03-27 07:55:32 -06:00
|
|
|
if (info->dflt.entries)
|
|
|
|
{
|
2010-06-23 09:25:10 -06:00
|
|
|
unsigned sz = from->dflt.nEntries * sizeof(XkbcKTMapEntryRec);
|
2009-03-27 07:55:32 -06:00
|
|
|
memcpy(info->dflt.entries, from->dflt.entries, sz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (from->dflt.lvlNames)
|
|
|
|
{
|
2010-06-28 04:50:12 -06:00
|
|
|
info->dflt.lvlNames = uTypedCalloc(from->dflt.szNames, uint32_t);
|
2009-03-27 07:55:32 -06:00
|
|
|
if (info->dflt.lvlNames)
|
|
|
|
{
|
2010-06-28 04:50:12 -06:00
|
|
|
register unsigned sz = from->dflt.szNames * sizeof(uint32_t);
|
2009-03-27 07:55:32 -06:00
|
|
|
memcpy(info->dflt.lvlNames, from->dflt.lvlNames, sz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (from->dflt.preserve)
|
|
|
|
{
|
|
|
|
PreserveInfo *old, *new, *last;
|
|
|
|
last = NULL;
|
|
|
|
old = from->dflt.preserve;
|
|
|
|
for (; old; old = (PreserveInfo *) old->defs.next)
|
|
|
|
{
|
|
|
|
new = uTypedAlloc(PreserveInfo);
|
|
|
|
if (!new)
|
|
|
|
return;
|
|
|
|
*new = *old;
|
|
|
|
new->defs.next = NULL;
|
|
|
|
if (last)
|
|
|
|
last->defs.next = (CommonInfo *) new;
|
|
|
|
else
|
|
|
|
info->dflt.preserve = new;
|
|
|
|
last = new;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
FreeKeyTypeInfo(KeyTypeInfo * type)
|
|
|
|
{
|
|
|
|
if (type->entries != NULL)
|
|
|
|
{
|
2010-06-28 04:58:01 -06:00
|
|
|
free(type->entries);
|
2009-03-27 07:55:32 -06:00
|
|
|
type->entries = NULL;
|
|
|
|
}
|
|
|
|
if (type->lvlNames != NULL)
|
|
|
|
{
|
2010-06-28 04:58:01 -06:00
|
|
|
free(type->lvlNames);
|
2009-03-27 07:55:32 -06:00
|
|
|
type->lvlNames = NULL;
|
|
|
|
}
|
|
|
|
if (type->preserve != NULL)
|
|
|
|
{
|
|
|
|
ClearCommonInfo(&type->preserve->defs);
|
|
|
|
type->preserve = NULL;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
FreeKeyTypesInfo(KeyTypesInfo * info)
|
|
|
|
{
|
|
|
|
if (info->name)
|
2010-06-28 04:58:01 -06:00
|
|
|
free(info->name);
|
2009-03-27 07:55:32 -06:00
|
|
|
info->name = NULL;
|
|
|
|
if (info->types)
|
|
|
|
{
|
|
|
|
register KeyTypeInfo *type;
|
|
|
|
for (type = info->types; type; type = (KeyTypeInfo *) type->defs.next)
|
|
|
|
{
|
|
|
|
FreeKeyTypeInfo(type);
|
|
|
|
}
|
|
|
|
info->types = (KeyTypeInfo *) ClearCommonInfo(&info->types->defs);
|
|
|
|
}
|
|
|
|
FreeKeyTypeInfo(&info->dflt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static KeyTypeInfo *
|
|
|
|
NextKeyType(KeyTypesInfo * info)
|
|
|
|
{
|
|
|
|
KeyTypeInfo *type;
|
|
|
|
|
|
|
|
type = uTypedAlloc(KeyTypeInfo);
|
|
|
|
if (type != NULL)
|
|
|
|
{
|
|
|
|
bzero(type, sizeof(KeyTypeInfo));
|
|
|
|
type->defs.fileID = info->fileID;
|
|
|
|
info->types = (KeyTypeInfo *) AddCommonInfo(&info->types->defs,
|
|
|
|
(CommonInfo *) type);
|
|
|
|
info->nTypes++;
|
|
|
|
}
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
static KeyTypeInfo *
|
|
|
|
FindMatchingKeyType(KeyTypesInfo * info, KeyTypeInfo * new)
|
|
|
|
{
|
|
|
|
KeyTypeInfo *old;
|
|
|
|
|
|
|
|
for (old = info->types; old; old = (KeyTypeInfo *) old->defs.next)
|
|
|
|
{
|
|
|
|
if (old->name == new->name)
|
|
|
|
return old;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
ReportTypeBadWidth(const char *type, int has, int needs)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Key type \"%s\" has %d levels, must have %d\n", type, has, needs);
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Illegal type definition ignored\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
2009-03-27 21:01:32 -06:00
|
|
|
AddKeyType(XkbcDescPtr xkb, KeyTypesInfo * info, KeyTypeInfo * new)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
KeyTypeInfo *old;
|
|
|
|
|
|
|
|
if (new->name == tok_ONE_LEVEL)
|
|
|
|
{
|
|
|
|
if (new->numLevels > 1)
|
|
|
|
return ReportTypeBadWidth("ONE_LEVEL", new->numLevels, 1);
|
|
|
|
info->stdPresent |= XkbOneLevelMask;
|
|
|
|
}
|
|
|
|
else if (new->name == tok_TWO_LEVEL)
|
|
|
|
{
|
|
|
|
if (new->numLevels > 2)
|
|
|
|
return ReportTypeBadWidth("TWO_LEVEL", new->numLevels, 2);
|
|
|
|
else if (new->numLevels < 2)
|
|
|
|
new->numLevels = 2;
|
|
|
|
info->stdPresent |= XkbTwoLevelMask;
|
|
|
|
}
|
|
|
|
else if (new->name == tok_ALPHABETIC)
|
|
|
|
{
|
|
|
|
if (new->numLevels > 2)
|
|
|
|
return ReportTypeBadWidth("ALPHABETIC", new->numLevels, 2);
|
|
|
|
else if (new->numLevels < 2)
|
|
|
|
new->numLevels = 2;
|
|
|
|
info->stdPresent |= XkbAlphabeticMask;
|
|
|
|
}
|
|
|
|
else if (new->name == tok_KEYPAD)
|
|
|
|
{
|
|
|
|
if (new->numLevels > 2)
|
|
|
|
return ReportTypeBadWidth("KEYPAD", new->numLevels, 2);
|
|
|
|
else if (new->numLevels < 2)
|
|
|
|
new->numLevels = 2;
|
|
|
|
info->stdPresent |= XkbKeypadMask;
|
|
|
|
}
|
|
|
|
|
|
|
|
old = FindMatchingKeyType(info, new);
|
|
|
|
if (old != NULL)
|
|
|
|
{
|
|
|
|
Bool report;
|
|
|
|
if ((new->defs.merge == MergeReplace)
|
|
|
|
|| (new->defs.merge == MergeOverride))
|
|
|
|
{
|
|
|
|
KeyTypeInfo *next = (KeyTypeInfo *) old->defs.next;
|
|
|
|
if (((old->defs.fileID == new->defs.fileID)
|
|
|
|
&& (warningLevel > 0)) || (warningLevel > 9))
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Multiple definitions of the %s key type\n",
|
2010-06-15 08:25:40 -06:00
|
|
|
XkbcAtomText(new->name));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Earlier definition ignored\n");
|
|
|
|
}
|
|
|
|
FreeKeyTypeInfo(old);
|
|
|
|
*old = *new;
|
|
|
|
new->szEntries = new->nEntries = 0;
|
|
|
|
new->entries = NULL;
|
|
|
|
new->preserve = NULL;
|
|
|
|
new->lvlNames = NULL;
|
|
|
|
old->defs.next = &next->defs;
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
report = (old->defs.fileID == new->defs.fileID) && (warningLevel > 0);
|
|
|
|
if (report)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Multiple definitions of the %s key type\n",
|
2010-06-15 08:25:40 -06:00
|
|
|
XkbcAtomText(new->name));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Later definition ignored\n");
|
|
|
|
}
|
|
|
|
FreeKeyTypeInfo(new);
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
old = NextKeyType(info);
|
|
|
|
if (old == NULL)
|
|
|
|
return False;
|
|
|
|
*old = *new;
|
|
|
|
old->defs.next = NULL;
|
|
|
|
new->nEntries = new->szEntries = 0;
|
|
|
|
new->entries = NULL;
|
|
|
|
new->szNames = 0;
|
|
|
|
new->lvlNames = NULL;
|
|
|
|
new->preserve = NULL;
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***====================================================================***/
|
|
|
|
|
|
|
|
static void
|
|
|
|
MergeIncludedKeyTypes(KeyTypesInfo * into,
|
2009-03-27 21:01:32 -06:00
|
|
|
KeyTypesInfo * from, unsigned merge, XkbcDescPtr xkb)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
KeyTypeInfo *type;
|
|
|
|
|
|
|
|
if (from->errorCount > 0)
|
|
|
|
{
|
|
|
|
into->errorCount += from->errorCount;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (into->name == NULL)
|
|
|
|
{
|
|
|
|
into->name = from->name;
|
|
|
|
from->name = NULL;
|
|
|
|
}
|
|
|
|
for (type = from->types; type; type = (KeyTypeInfo *) type->defs.next)
|
|
|
|
{
|
|
|
|
if (merge != MergeDefault)
|
|
|
|
type->defs.merge = merge;
|
|
|
|
if (!AddKeyType(xkb, into, type))
|
|
|
|
into->errorCount++;
|
|
|
|
}
|
|
|
|
into->stdPresent |= from->stdPresent;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef void (*FileHandler) (XkbFile * /* file */ ,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr /* xkb */ ,
|
2009-03-27 07:55:32 -06:00
|
|
|
unsigned /* merge */ ,
|
|
|
|
KeyTypesInfo * /* included */
|
|
|
|
);
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
HandleIncludeKeyTypes(IncludeStmt * stmt,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr xkb, KeyTypesInfo * info, FileHandler hndlr)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
unsigned newMerge;
|
|
|
|
XkbFile *rtrn;
|
|
|
|
KeyTypesInfo included;
|
|
|
|
Bool haveSelf;
|
|
|
|
|
|
|
|
haveSelf = False;
|
|
|
|
if ((stmt->file == NULL) && (stmt->map == NULL))
|
|
|
|
{
|
|
|
|
haveSelf = True;
|
|
|
|
included = *info;
|
|
|
|
bzero(info, sizeof(KeyTypesInfo));
|
|
|
|
}
|
|
|
|
else if (ProcessIncludeFile(stmt, XkmTypesIndex, &rtrn, &newMerge))
|
|
|
|
{
|
|
|
|
InitKeyTypesInfo(&included, xkb, info);
|
|
|
|
included.fileID = included.dflt.defs.fileID = rtrn->id;
|
|
|
|
included.dflt.defs.merge = newMerge;
|
|
|
|
|
|
|
|
(*hndlr) (rtrn, xkb, newMerge, &included);
|
|
|
|
if (stmt->stmt != NULL)
|
|
|
|
{
|
|
|
|
if (included.name != NULL)
|
2010-06-28 04:58:01 -06:00
|
|
|
free(included.name);
|
2009-03-27 07:55:32 -06:00
|
|
|
included.name = stmt->stmt;
|
|
|
|
stmt->stmt = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info->errorCount += 10;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
if ((stmt->next != NULL) && (included.errorCount < 1))
|
|
|
|
{
|
|
|
|
IncludeStmt *next;
|
|
|
|
unsigned op;
|
|
|
|
KeyTypesInfo next_incl;
|
|
|
|
|
|
|
|
for (next = stmt->next; next != NULL; next = next->next)
|
|
|
|
{
|
|
|
|
if ((next->file == NULL) && (next->map == NULL))
|
|
|
|
{
|
|
|
|
haveSelf = True;
|
|
|
|
MergeIncludedKeyTypes(&included, info, next->merge, xkb);
|
|
|
|
FreeKeyTypesInfo(info);
|
|
|
|
}
|
|
|
|
else if (ProcessIncludeFile(next, XkmTypesIndex, &rtrn, &op))
|
|
|
|
{
|
|
|
|
InitKeyTypesInfo(&next_incl, xkb, &included);
|
|
|
|
next_incl.fileID = next_incl.dflt.defs.fileID = rtrn->id;
|
|
|
|
next_incl.dflt.defs.merge = op;
|
|
|
|
(*hndlr) (rtrn, xkb, op, &next_incl);
|
|
|
|
MergeIncludedKeyTypes(&included, &next_incl, op, xkb);
|
|
|
|
FreeKeyTypesInfo(&next_incl);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info->errorCount += 10;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (haveSelf)
|
|
|
|
*info = included;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MergeIncludedKeyTypes(info, &included, newMerge, xkb);
|
|
|
|
FreeKeyTypesInfo(&included);
|
|
|
|
}
|
|
|
|
return (info->errorCount == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***====================================================================***/
|
|
|
|
|
2010-06-23 09:25:10 -06:00
|
|
|
static XkbcKTMapEntryPtr
|
2009-03-27 07:55:32 -06:00
|
|
|
FindMatchingMapEntry(KeyTypeInfo * type, unsigned mask, unsigned vmask)
|
|
|
|
{
|
|
|
|
register int i;
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryPtr entry;
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
for (i = 0, entry = type->entries; i < type->nEntries; i++, entry++)
|
|
|
|
{
|
|
|
|
if ((entry->mods.real_mods == mask) && (entry->mods.vmods == vmask))
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
DeleteLevel1MapEntries(KeyTypeInfo * type)
|
|
|
|
{
|
|
|
|
register int i, n;
|
|
|
|
|
|
|
|
for (i = 0; i < type->nEntries; i++)
|
|
|
|
{
|
|
|
|
if (type->entries[i].level == 0)
|
|
|
|
{
|
|
|
|
for (n = i; n < type->nEntries - 1; n++)
|
|
|
|
{
|
|
|
|
type->entries[n] = type->entries[n + 1];
|
|
|
|
}
|
|
|
|
type->nEntries--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-06-23 09:25:10 -06:00
|
|
|
* Return a pointer to the next free XkbcKTMapEntry, reallocating space if
|
2009-03-27 07:55:32 -06:00
|
|
|
* necessary.
|
|
|
|
*/
|
2010-06-23 09:25:10 -06:00
|
|
|
static XkbcKTMapEntryPtr
|
2009-03-27 07:55:32 -06:00
|
|
|
NextMapEntry(KeyTypeInfo * type)
|
|
|
|
{
|
|
|
|
if (type->entries == NULL)
|
|
|
|
{
|
2010-06-23 09:25:10 -06:00
|
|
|
type->entries = uTypedCalloc(2, XkbcKTMapEntryRec);
|
2009-03-27 07:55:32 -06:00
|
|
|
if (type->entries == NULL)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Couldn't allocate map entries for %s\n", TypeTxt(type));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Map entries lost\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
type->szEntries = 2;
|
|
|
|
type->nEntries = 0;
|
|
|
|
}
|
|
|
|
else if (type->nEntries >= type->szEntries)
|
|
|
|
{
|
|
|
|
type->szEntries *= 2;
|
|
|
|
type->entries = uTypedRecalloc(type->entries,
|
|
|
|
type->nEntries, type->szEntries,
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryRec);
|
2009-03-27 07:55:32 -06:00
|
|
|
if (type->entries == NULL)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Couldn't reallocate map entries for %s\n", TypeTxt(type));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Map entries lost\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &type->entries[type->nEntries++];
|
|
|
|
}
|
|
|
|
|
|
|
|
Bool
|
2009-03-27 21:01:32 -06:00
|
|
|
AddPreserve(XkbcDescPtr xkb,
|
2009-03-27 07:55:32 -06:00
|
|
|
KeyTypeInfo * type, PreserveInfo * new, Bool clobber, Bool report)
|
|
|
|
{
|
|
|
|
PreserveInfo *old;
|
|
|
|
|
|
|
|
old = type->preserve;
|
|
|
|
while (old != NULL)
|
|
|
|
{
|
|
|
|
if ((old->indexMods != new->indexMods) ||
|
|
|
|
(old->indexVMods != new->indexVMods))
|
|
|
|
{
|
|
|
|
old = (PreserveInfo *) old->defs.next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((old->preMods == new->preMods)
|
|
|
|
&& (old->preVMods == new->preVMods))
|
|
|
|
{
|
|
|
|
if (warningLevel > 9)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Identical definitions for preserve[%s] in %s\n",
|
2009-03-28 15:09:36 -06:00
|
|
|
PreserveIndexTxt(xkb, old), TypeTxt(type));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Ignored\n");
|
|
|
|
}
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
if (report && (warningLevel > 0))
|
|
|
|
{
|
|
|
|
char *str;
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Multiple definitions for preserve[%s] in %s\n",
|
2009-03-28 15:09:36 -06:00
|
|
|
PreserveIndexTxt(xkb, old), TypeTxt(type));
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
if (clobber)
|
2009-03-28 15:09:36 -06:00
|
|
|
str = PreserveTxt(xkb, new);
|
2009-03-27 07:55:32 -06:00
|
|
|
else
|
2009-03-28 15:09:36 -06:00
|
|
|
str = PreserveTxt(xkb, old);
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Using %s, ", str);
|
2009-03-27 07:55:32 -06:00
|
|
|
if (clobber)
|
2009-03-28 15:09:36 -06:00
|
|
|
str = PreserveTxt(xkb, old);
|
2009-03-27 07:55:32 -06:00
|
|
|
else
|
2009-03-28 15:09:36 -06:00
|
|
|
str = PreserveTxt(xkb, new);
|
2009-03-31 08:21:20 -06:00
|
|
|
INFO("ignoring %s\n", str);
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
if (clobber)
|
|
|
|
{
|
|
|
|
old->preMods = new->preMods;
|
|
|
|
old->preVMods = new->preVMods;
|
|
|
|
}
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
old = uTypedAlloc(PreserveInfo);
|
|
|
|
if (!old)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WSGO("Couldn't allocate preserve in %s\n", TypeTxt(type));
|
|
|
|
ACTION("Preserve[%s] lost\n", PreserveIndexTxt(xkb, old));
|
2009-03-27 07:55:32 -06:00
|
|
|
return False;
|
|
|
|
}
|
|
|
|
*old = *new;
|
|
|
|
old->matchingMapIndex = -1;
|
|
|
|
type->preserve =
|
|
|
|
(PreserveInfo *) AddCommonInfo(&type->preserve->defs, &old->defs);
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a new KTMapEntry to the given key type. If an entry with the same mods
|
|
|
|
* already exists, the level is updated (if clobber is TRUE). Otherwise, a new
|
|
|
|
* entry is created.
|
|
|
|
*
|
|
|
|
* @param clobber Overwrite existing entry.
|
|
|
|
* @param report True if a warning is to be printed on.
|
|
|
|
*/
|
|
|
|
Bool
|
2009-03-27 21:01:32 -06:00
|
|
|
AddMapEntry(XkbcDescPtr xkb,
|
2009-03-27 07:55:32 -06:00
|
|
|
KeyTypeInfo * type,
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryPtr new, Bool clobber, Bool report)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryPtr old;
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
if ((old =
|
|
|
|
FindMatchingMapEntry(type, new->mods.real_mods, new->mods.vmods)))
|
|
|
|
{
|
|
|
|
if (report && (old->level != new->level))
|
|
|
|
{
|
|
|
|
unsigned use, ignore;
|
|
|
|
if (clobber)
|
|
|
|
{
|
|
|
|
use = new->level + 1;
|
|
|
|
ignore = old->level + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
use = old->level + 1;
|
|
|
|
ignore = new->level + 1;
|
|
|
|
}
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Multiple map entries for %s in %s\n",
|
2009-03-28 15:09:36 -06:00
|
|
|
MapEntryTxt(xkb, new), TypeTxt(type));
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Using %d, ignoring %d\n", use, ignore);
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
else if (warningLevel > 9)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Multiple occurences of map[%s]= %d in %s\n",
|
2009-03-28 15:09:36 -06:00
|
|
|
MapEntryTxt(xkb, new), new->level + 1, TypeTxt(type));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Ignored\n");
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
if (clobber)
|
|
|
|
old->level = new->level;
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
if ((old = NextMapEntry(type)) == NULL)
|
|
|
|
return False; /* allocation failure, already reported */
|
|
|
|
if (new->level >= type->numLevels)
|
|
|
|
type->numLevels = new->level + 1;
|
|
|
|
if (new->mods.vmods == 0)
|
|
|
|
old->active = True;
|
|
|
|
else
|
|
|
|
old->active = False;
|
|
|
|
old->mods.mask = new->mods.real_mods;
|
|
|
|
old->mods.real_mods = new->mods.real_mods;
|
|
|
|
old->mods.vmods = new->mods.vmods;
|
|
|
|
old->level = new->level;
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LookupEntry lnames[] = {
|
|
|
|
{"level1", 1},
|
|
|
|
{"level2", 2},
|
|
|
|
{"level3", 3},
|
|
|
|
{"level4", 4},
|
|
|
|
{"level5", 5},
|
|
|
|
{"level6", 6},
|
|
|
|
{"level7", 7},
|
|
|
|
{"level8", 8},
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
SetMapEntry(KeyTypeInfo * type,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr xkb, ExprDef * arrayNdx, ExprDef * value)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
ExprResult rtrn;
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryRec entry;
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
if (arrayNdx == NULL)
|
|
|
|
return ReportTypeShouldBeArray(type, "map entry");
|
2009-03-27 21:13:22 -06:00
|
|
|
if (!ExprResolveModMask(arrayNdx, &rtrn, LookupVModMask, (char *) xkb))
|
2009-03-27 07:55:32 -06:00
|
|
|
return ReportTypeBadType(type, "map entry", "modifier mask");
|
|
|
|
entry.mods.real_mods = rtrn.uval & 0xff; /* modifiers < 512 */
|
|
|
|
entry.mods.vmods = (rtrn.uval >> 8) & 0xffff; /* modifiers > 512 */
|
|
|
|
if ((entry.mods.real_mods & (~type->mask)) ||
|
|
|
|
((entry.mods.vmods & (~type->vmask)) != 0))
|
|
|
|
{
|
|
|
|
if (warningLevel > 0)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Map entry for unused modifiers in %s\n", TypeTxt(type));
|
|
|
|
ACTION("Using %s instead of ",
|
2009-03-28 16:12:30 -06:00
|
|
|
XkbcVModMaskText(xkb,
|
2009-03-27 07:55:32 -06:00
|
|
|
entry.mods.real_mods & type->mask,
|
2009-03-28 16:12:30 -06:00
|
|
|
entry.mods.vmods & type->vmask));
|
2009-03-31 08:21:20 -06:00
|
|
|
INFO("%s\n", MapEntryTxt(xkb, &entry));
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
entry.mods.real_mods &= type->mask;
|
|
|
|
entry.mods.vmods &= type->vmask;
|
|
|
|
}
|
2009-03-27 21:13:22 -06:00
|
|
|
if (!ExprResolveInteger(value, &rtrn, SimpleLookup, (char *) lnames))
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
ERROR("Level specifications in a key type must be integer\n");
|
|
|
|
ACTION("Ignoring malformed level specification\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
if ((rtrn.ival < 1) || (rtrn.ival > XkbMaxShiftLevel + 1))
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Shift level %d out of range (1..%d) in key type %s\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
XkbMaxShiftLevel + 1, rtrn.ival, TypeTxt(type));
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Ignoring illegal definition of map[%s]\n",
|
2009-03-28 15:09:36 -06:00
|
|
|
MapEntryTxt(xkb, &entry));
|
2009-03-27 07:55:32 -06:00
|
|
|
return False;
|
|
|
|
}
|
|
|
|
entry.level = rtrn.ival - 1;
|
|
|
|
return AddMapEntry(xkb, type, &entry, True, True);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
SetPreserve(KeyTypeInfo * type,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr xkb, ExprDef * arrayNdx, ExprDef * value)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
ExprResult rtrn;
|
|
|
|
PreserveInfo new;
|
|
|
|
|
|
|
|
if (arrayNdx == NULL)
|
|
|
|
return ReportTypeShouldBeArray(type, "preserve entry");
|
2009-03-27 21:13:22 -06:00
|
|
|
if (!ExprResolveModMask(arrayNdx, &rtrn, LookupVModMask, (char *) xkb))
|
2009-03-27 07:55:32 -06:00
|
|
|
return ReportTypeBadType(type, "preserve entry", "modifier mask");
|
|
|
|
new.defs = type->defs;
|
|
|
|
new.defs.next = NULL;
|
|
|
|
new.indexMods = rtrn.uval & 0xff;
|
|
|
|
new.indexVMods = (rtrn.uval >> 8) & 0xffff;
|
|
|
|
if ((new.indexMods & (~type->mask)) || (new.indexVMods & (~type->vmask)))
|
|
|
|
{
|
|
|
|
if (warningLevel > 0)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Preserve for modifiers not used by the %s type\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
TypeTxt(type));
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Index %s converted to ", PreserveIndexTxt(xkb, &new));
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
new.indexMods &= type->mask;
|
|
|
|
new.indexVMods &= type->vmask;
|
|
|
|
if (warningLevel > 0)
|
2009-03-31 08:21:20 -06:00
|
|
|
INFO("%s\n", PreserveIndexTxt(xkb, &new));
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
2009-03-27 21:13:22 -06:00
|
|
|
if (!ExprResolveModMask(value, &rtrn, LookupVModMask, (char *) xkb))
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
ERROR("Preserve value in a key type is not a modifier mask\n");
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Ignoring preserve[%s] in type %s\n",
|
2009-03-28 15:09:36 -06:00
|
|
|
PreserveIndexTxt(xkb, &new), TypeTxt(type));
|
2009-03-27 07:55:32 -06:00
|
|
|
return False;
|
|
|
|
}
|
|
|
|
new.preMods = rtrn.uval & 0xff;
|
|
|
|
new.preVMods = (rtrn.uval >> 16) & 0xffff;
|
|
|
|
if ((new.preMods & (~new.indexMods))
|
|
|
|
|| (new.preVMods && (~new.indexVMods)))
|
|
|
|
{
|
|
|
|
if (warningLevel > 0)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Illegal value for preserve[%s] in type %s\n",
|
2009-03-28 15:09:36 -06:00
|
|
|
PreserveTxt(xkb, &new), TypeTxt(type));
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Converted %s to ", PreserveIndexTxt(xkb, &new));
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
new.preMods &= new.indexMods;
|
|
|
|
new.preVMods &= new.indexVMods;
|
|
|
|
if (warningLevel > 0)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
INFO("%s\n", PreserveIndexTxt(xkb, &new));
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return AddPreserve(xkb, type, &new, True, True);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***====================================================================***/
|
|
|
|
|
|
|
|
Bool
|
|
|
|
AddLevelName(KeyTypeInfo * type,
|
2010-06-28 04:50:12 -06:00
|
|
|
unsigned level, uint32_t name, Bool clobber, Bool report)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
if ((type->lvlNames == NULL) || (type->szNames <= level))
|
|
|
|
{
|
|
|
|
type->lvlNames =
|
2010-06-28 04:50:12 -06:00
|
|
|
uTypedRecalloc(type->lvlNames, type->szNames, level + 1, uint32_t);
|
2009-03-27 07:55:32 -06:00
|
|
|
if (type->lvlNames == NULL)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Couldn't allocate level names for type %s\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
TypeTxt(type));
|
|
|
|
ACTION("Level names lost\n");
|
|
|
|
type->szNames = 0;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
type->szNames = level + 1;
|
|
|
|
}
|
|
|
|
else if (type->lvlNames[level] == name)
|
|
|
|
{
|
|
|
|
if (warningLevel > 9)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Duplicate names for level %d of key type %s\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
level + 1, TypeTxt(type));
|
|
|
|
ACTION("Ignored\n");
|
|
|
|
}
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
else if (type->lvlNames[level] != None)
|
|
|
|
{
|
|
|
|
if (warningLevel > 0)
|
|
|
|
{
|
2010-06-30 15:20:56 -06:00
|
|
|
const char *old, *new;
|
2009-03-28 15:06:26 -06:00
|
|
|
old = XkbcAtomText(type->lvlNames[level]);
|
|
|
|
new = XkbcAtomText(name);
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Multiple names for level %d of key type %s\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
level + 1, TypeTxt(type));
|
|
|
|
if (clobber)
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Using %s, ignoring %s\n", new, old);
|
2009-03-27 07:55:32 -06:00
|
|
|
else
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Using %s, ignoring %s\n", old, new);
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
if (!clobber)
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
if (level >= type->numLevels)
|
|
|
|
type->numLevels = level + 1;
|
|
|
|
type->lvlNames[level] = name;
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
SetLevelName(KeyTypeInfo * type, ExprDef * arrayNdx, ExprDef * value)
|
|
|
|
{
|
|
|
|
ExprResult rtrn;
|
|
|
|
unsigned level;
|
2010-06-28 04:50:12 -06:00
|
|
|
uint32_t level_name;
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
if (arrayNdx == NULL)
|
|
|
|
return ReportTypeShouldBeArray(type, "level name");
|
2009-03-27 21:13:22 -06:00
|
|
|
if (!ExprResolveInteger(arrayNdx, &rtrn, SimpleLookup, (char *) lnames))
|
2009-03-27 07:55:32 -06:00
|
|
|
return ReportTypeBadType(type, "level name", "integer");
|
|
|
|
if ((rtrn.ival < 1) || (rtrn.ival > XkbMaxShiftLevel + 1))
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Level name %d out of range (1..%d) in key type %s\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
rtrn.ival,
|
|
|
|
XkbMaxShiftLevel + 1,
|
2009-03-28 15:06:26 -06:00
|
|
|
XkbcAtomText(type->name));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Ignoring illegal level name definition\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
level = rtrn.ival - 1;
|
|
|
|
if (!ExprResolveString(value, &rtrn, NULL, NULL))
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Non-string name for level %d in key type %s\n", level + 1,
|
2009-03-28 15:06:26 -06:00
|
|
|
XkbcAtomText(type->name));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Ignoring illegal level name definition\n");
|
|
|
|
return False;
|
|
|
|
}
|
2010-06-15 08:25:40 -06:00
|
|
|
level_name = XkbcInternAtom(rtrn.str, False);
|
|
|
|
free(rtrn.str);
|
|
|
|
return AddLevelName(type, level, level_name, True, True);
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/***====================================================================***/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the fields in a type "..." { } description.
|
|
|
|
*
|
|
|
|
* @param field The field to parse (e.g. modifiers, map, level_name)
|
|
|
|
*/
|
|
|
|
static Bool
|
|
|
|
SetKeyTypeField(KeyTypeInfo * type,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr xkb,
|
2009-03-27 07:55:32 -06:00
|
|
|
char *field,
|
|
|
|
ExprDef * arrayNdx, ExprDef * value, KeyTypesInfo * info)
|
|
|
|
{
|
|
|
|
ExprResult tmp;
|
|
|
|
|
|
|
|
if (uStrCaseCmp(field, "modifiers") == 0)
|
|
|
|
{
|
|
|
|
unsigned mods, vmods;
|
|
|
|
if (arrayNdx != NULL)
|
|
|
|
{
|
|
|
|
WARN("The modifiers field of a key type is not an array\n");
|
|
|
|
ACTION("Illegal array subscript ignored\n");
|
|
|
|
}
|
|
|
|
/* get modifier mask for current type */
|
2009-03-27 21:13:22 -06:00
|
|
|
if (!ExprResolveModMask(value, &tmp, LookupVModMask, (char *) xkb))
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
ERROR("Key type mask field must be a modifier mask\n");
|
|
|
|
ACTION("Key type definition ignored\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
mods = tmp.uval & 0xff; /* core mods */
|
|
|
|
vmods = (tmp.uval >> 8) & 0xffff; /* xkb virtual mods */
|
|
|
|
if (type->defs.defined & _KT_Mask)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
WARN("Multiple modifier mask definitions for key type %s\n",
|
2009-03-28 15:06:26 -06:00
|
|
|
XkbcAtomText(type->name));
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Using %s, ", TypeMaskTxt(type, xkb));
|
|
|
|
INFO("ignoring %s\n", XkbcVModMaskText(xkb, mods, vmods));
|
2009-03-27 07:55:32 -06:00
|
|
|
return False;
|
|
|
|
}
|
|
|
|
type->mask = mods;
|
|
|
|
type->vmask = vmods;
|
|
|
|
type->defs.defined |= _KT_Mask;
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
else if (uStrCaseCmp(field, "map") == 0)
|
|
|
|
{
|
|
|
|
type->defs.defined |= _KT_Map;
|
|
|
|
return SetMapEntry(type, xkb, arrayNdx, value);
|
|
|
|
}
|
|
|
|
else if (uStrCaseCmp(field, "preserve") == 0)
|
|
|
|
{
|
|
|
|
type->defs.defined |= _KT_Preserve;
|
|
|
|
return SetPreserve(type, xkb, arrayNdx, value);
|
|
|
|
}
|
|
|
|
else if ((uStrCaseCmp(field, "levelname") == 0) ||
|
|
|
|
(uStrCaseCmp(field, "level_name") == 0))
|
|
|
|
{
|
|
|
|
type->defs.defined |= _KT_LevelNames;
|
|
|
|
return SetLevelName(type, arrayNdx, value);
|
|
|
|
}
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Unknown field %s in key type %s\n", field, TypeTxt(type));
|
2009-03-27 07:55:32 -06:00
|
|
|
ACTION("Definition ignored\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
2009-03-27 21:01:32 -06:00
|
|
|
HandleKeyTypeVar(VarDef * stmt, XkbcDescPtr xkb, KeyTypesInfo * info)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
ExprResult elem, field;
|
|
|
|
ExprDef *arrayNdx;
|
|
|
|
|
|
|
|
if (!ExprResolveLhs(stmt->name, &elem, &field, &arrayNdx))
|
|
|
|
return False; /* internal error, already reported */
|
|
|
|
if (elem.str && (uStrCaseCmp(elem.str, "type") == 0))
|
|
|
|
return SetKeyTypeField(&info->dflt, xkb, field.str, arrayNdx,
|
|
|
|
stmt->value, info);
|
|
|
|
if (elem.str != NULL)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Default for unknown element %s\n", uStringText(elem.str));
|
|
|
|
ACTION("Value for field %s ignored\n", uStringText(field.str));
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
else if (field.str != NULL)
|
|
|
|
{
|
2009-03-31 08:21:20 -06:00
|
|
|
ERROR("Default defined for unknown field %s\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
uStringText(field.str));
|
|
|
|
ACTION("Ignored\n");
|
|
|
|
}
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
HandleKeyTypeBody(VarDef * def,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr xkb, KeyTypeInfo * type, KeyTypesInfo * info)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
int ok = 1;
|
|
|
|
ExprResult tmp, field;
|
|
|
|
ExprDef *arrayNdx;
|
|
|
|
|
|
|
|
for (; def != NULL; def = (VarDef *) def->common.next)
|
|
|
|
{
|
|
|
|
if ((def->name) && (def->name->type == ExprFieldRef))
|
|
|
|
{
|
|
|
|
ok = HandleKeyTypeVar(def, xkb, info);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ok = ExprResolveLhs(def->name, &tmp, &field, &arrayNdx);
|
|
|
|
if (ok)
|
|
|
|
ok = SetKeyTypeField(type, xkb, field.str, arrayNdx, def->value,
|
|
|
|
info);
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process a type "XYZ" { } specification in the xkb_types section.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
HandleKeyTypeDef(KeyTypeDef * def,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr xkb, unsigned merge, KeyTypesInfo * info)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
KeyTypeInfo type;
|
|
|
|
|
|
|
|
if (def->merge != MergeDefault)
|
|
|
|
merge = def->merge;
|
|
|
|
|
|
|
|
type.defs.defined = 0;
|
|
|
|
type.defs.fileID = info->fileID;
|
|
|
|
type.defs.merge = merge;
|
|
|
|
type.defs.next = NULL;
|
|
|
|
type.name = def->name;
|
|
|
|
type.mask = info->dflt.mask;
|
|
|
|
type.vmask = info->dflt.vmask;
|
|
|
|
type.groupInfo = info->dflt.groupInfo;
|
|
|
|
type.numLevels = 1;
|
|
|
|
type.nEntries = type.szEntries = 0;
|
|
|
|
type.entries = NULL;
|
|
|
|
type.szNames = 0;
|
|
|
|
type.lvlNames = NULL;
|
|
|
|
type.preserve = NULL;
|
|
|
|
|
|
|
|
/* Parse the actual content. */
|
|
|
|
if (!HandleKeyTypeBody(def->body, xkb, &type, info))
|
|
|
|
{
|
|
|
|
info->errorCount++;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now copy any appropriate map, preserve or level names from the */
|
|
|
|
/* default type */
|
|
|
|
for (i = 0; i < info->dflt.nEntries; i++)
|
|
|
|
{
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryPtr dflt;
|
2009-03-27 07:55:32 -06:00
|
|
|
dflt = &info->dflt.entries[i];
|
|
|
|
if (((dflt->mods.real_mods & type.mask) == dflt->mods.real_mods) &&
|
|
|
|
((dflt->mods.vmods & type.vmask) == dflt->mods.vmods))
|
|
|
|
{
|
|
|
|
AddMapEntry(xkb, &type, dflt, False, False);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (info->dflt.preserve)
|
|
|
|
{
|
|
|
|
PreserveInfo *dflt = info->dflt.preserve;
|
|
|
|
while (dflt)
|
|
|
|
{
|
|
|
|
if (((dflt->indexMods & type.mask) == dflt->indexMods) &&
|
|
|
|
((dflt->indexVMods & type.vmask) == dflt->indexVMods))
|
|
|
|
{
|
|
|
|
AddPreserve(xkb, &type, dflt, False, False);
|
|
|
|
}
|
|
|
|
dflt = (PreserveInfo *) dflt->defs.next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < info->dflt.szNames; i++)
|
|
|
|
{
|
|
|
|
if ((i < type.numLevels) && (info->dflt.lvlNames[i] != None))
|
|
|
|
{
|
|
|
|
AddLevelName(&type, i, info->dflt.lvlNames[i], False, False);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Now add the new keytype to the info struct */
|
|
|
|
if (!AddKeyType(xkb, info, &type))
|
|
|
|
{
|
|
|
|
info->errorCount++;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process an xkb_types section.
|
|
|
|
*
|
|
|
|
* @param file The parsed xkb_types section.
|
|
|
|
* @param merge Merge Strategy (e.g. MergeOverride)
|
|
|
|
* @param info Pointer to memory where the outcome will be stored.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
HandleKeyTypesFile(XkbFile * file,
|
2009-03-27 21:01:32 -06:00
|
|
|
XkbcDescPtr xkb, unsigned merge, KeyTypesInfo * info)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
ParseCommon *stmt;
|
|
|
|
|
2009-04-05 21:27:35 -06:00
|
|
|
info->name = _XkbDupString(file->name);
|
2009-03-27 07:55:32 -06:00
|
|
|
stmt = file->defs;
|
|
|
|
while (stmt)
|
|
|
|
{
|
|
|
|
switch (stmt->stmtType)
|
|
|
|
{
|
|
|
|
case StmtInclude:
|
|
|
|
if (!HandleIncludeKeyTypes((IncludeStmt *) stmt, xkb, info,
|
|
|
|
HandleKeyTypesFile))
|
|
|
|
info->errorCount++;
|
|
|
|
break;
|
|
|
|
case StmtKeyTypeDef: /* e.g. type "ONE_LEVEL" */
|
|
|
|
if (!HandleKeyTypeDef((KeyTypeDef *) stmt, xkb, merge, info))
|
|
|
|
info->errorCount++;
|
|
|
|
break;
|
|
|
|
case StmtVarDef:
|
|
|
|
if (!HandleKeyTypeVar((VarDef *) stmt, xkb, info))
|
|
|
|
info->errorCount++;
|
|
|
|
break;
|
|
|
|
case StmtVModDef: /* virtual_modifiers NumLock, ... */
|
|
|
|
if (!HandleVModDef((VModDef *) stmt, merge, &info->vmods))
|
|
|
|
info->errorCount++;
|
|
|
|
break;
|
|
|
|
case StmtKeyAliasDef:
|
|
|
|
ERROR("Key type files may not include other declarations\n");
|
|
|
|
ACTION("Ignoring definition of key alias\n");
|
|
|
|
info->errorCount++;
|
|
|
|
break;
|
|
|
|
case StmtKeycodeDef:
|
|
|
|
ERROR("Key type files may not include other declarations\n");
|
|
|
|
ACTION("Ignoring definition of key name\n");
|
|
|
|
info->errorCount++;
|
|
|
|
break;
|
|
|
|
case StmtInterpDef:
|
|
|
|
ERROR("Key type files may not include other declarations\n");
|
|
|
|
ACTION("Ignoring definition of symbol interpretation\n");
|
|
|
|
info->errorCount++;
|
|
|
|
break;
|
|
|
|
default:
|
2009-03-31 08:21:20 -06:00
|
|
|
WSGO("Unexpected statement type %d in HandleKeyTypesFile\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
stmt->stmtType);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
stmt = stmt->next;
|
|
|
|
if (info->errorCount > 10)
|
|
|
|
{
|
|
|
|
#ifdef NOISY
|
|
|
|
ERROR("Too many errors\n");
|
|
|
|
#endif
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Abandoning keytypes file \"%s\"\n", file->topName);
|
2009-03-27 07:55:32 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
2010-06-21 07:28:34 -06:00
|
|
|
CopyDefToKeyType(XkbcDescPtr xkb, XkbcKeyTypePtr type, KeyTypeInfo * def)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
PreserveInfo *pre;
|
|
|
|
|
|
|
|
for (pre = def->preserve; pre != NULL;
|
|
|
|
pre = (PreserveInfo *) pre->defs.next)
|
|
|
|
{
|
2010-06-23 09:25:10 -06:00
|
|
|
XkbcKTMapEntryPtr match;
|
|
|
|
XkbcKTMapEntryRec tmp;
|
2009-03-27 07:55:32 -06:00
|
|
|
tmp.mods.real_mods = pre->indexMods;
|
|
|
|
tmp.mods.vmods = pre->indexVMods;
|
|
|
|
tmp.level = 0;
|
|
|
|
AddMapEntry(xkb, def, &tmp, False, False);
|
|
|
|
match = FindMatchingMapEntry(def, pre->indexMods, pre->indexVMods);
|
|
|
|
if (!match)
|
|
|
|
{
|
|
|
|
WSGO("Couldn't find matching entry for preserve\n");
|
|
|
|
ACTION("Aborting\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
pre->matchingMapIndex = match - def->entries;
|
|
|
|
}
|
|
|
|
type->mods.real_mods = def->mask;
|
|
|
|
type->mods.vmods = def->vmask;
|
|
|
|
type->num_levels = def->numLevels;
|
|
|
|
type->map_count = def->nEntries;
|
|
|
|
type->map = def->entries;
|
|
|
|
if (def->preserve)
|
|
|
|
{
|
2010-06-23 09:25:10 -06:00
|
|
|
type->preserve = uTypedCalloc(type->map_count, XkbcModsRec);
|
2009-03-27 07:55:32 -06:00
|
|
|
if (!type->preserve)
|
|
|
|
{
|
|
|
|
WARN("Couldn't allocate preserve array in CopyDefToKeyType\n");
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Preserve setting for type %s lost\n",
|
2009-03-28 15:06:26 -06:00
|
|
|
XkbcAtomText(def->name));
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pre = def->preserve;
|
|
|
|
for (; pre != NULL; pre = (PreserveInfo *) pre->defs.next)
|
|
|
|
{
|
|
|
|
int ndx = pre->matchingMapIndex;
|
|
|
|
type->preserve[ndx].mask = pre->preMods;
|
|
|
|
type->preserve[ndx].real_mods = pre->preMods;
|
|
|
|
type->preserve[ndx].vmods = pre->preVMods;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
type->preserve = NULL;
|
2010-06-28 04:50:12 -06:00
|
|
|
type->name = (uint32_t) def->name;
|
2009-03-27 07:55:32 -06:00
|
|
|
if (def->szNames > 0)
|
|
|
|
{
|
2010-06-28 04:50:12 -06:00
|
|
|
type->level_names = uTypedCalloc(def->numLevels, uint32_t);
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
/* assert def->szNames<=def->numLevels */
|
|
|
|
for (i = 0; i < def->szNames; i++)
|
|
|
|
{
|
2010-06-28 04:50:12 -06:00
|
|
|
type->level_names[i] = (uint32_t) def->lvlNames[i];
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
type->level_names = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->nEntries = def->szEntries = 0;
|
|
|
|
def->entries = NULL;
|
2009-03-28 18:55:46 -06:00
|
|
|
return XkbcComputeEffectiveMap(xkb, type, NULL);
|
2009-03-27 07:55:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
Bool
|
2009-03-27 20:54:50 -06:00
|
|
|
CompileKeyTypes(XkbFile *file, XkbcDescPtr xkb, unsigned merge)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
KeyTypesInfo info;
|
|
|
|
|
|
|
|
InitKeyTypesInfo(&info, xkb, NULL);
|
|
|
|
info.fileID = file->id;
|
|
|
|
HandleKeyTypesFile(file, xkb, merge, &info);
|
|
|
|
|
|
|
|
if (info.errorCount == 0)
|
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
register KeyTypeInfo *def;
|
2010-06-21 07:28:34 -06:00
|
|
|
register XkbcKeyTypePtr type, next;
|
2009-03-27 07:55:32 -06:00
|
|
|
|
|
|
|
if (info.name != NULL)
|
|
|
|
{
|
2009-03-28 12:45:05 -06:00
|
|
|
if (XkbcAllocNames(xkb, XkbTypesNameMask, 0, 0) == Success)
|
2009-03-28 07:56:26 -06:00
|
|
|
xkb->names->types = XkbcInternAtom(info.name, False);
|
2009-03-27 07:55:32 -06:00
|
|
|
else
|
|
|
|
{
|
|
|
|
WSGO("Couldn't allocate space for types name\n");
|
2009-03-31 08:21:20 -06:00
|
|
|
ACTION("Name \"%s\" (from %s) NOT assigned\n",
|
2009-03-27 07:55:32 -06:00
|
|
|
scanFile, info.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i = info.nTypes;
|
|
|
|
if ((info.stdPresent & XkbOneLevelMask) == 0)
|
|
|
|
i++;
|
|
|
|
if ((info.stdPresent & XkbTwoLevelMask) == 0)
|
|
|
|
i++;
|
|
|
|
if ((info.stdPresent & XkbKeypadMask) == 0)
|
|
|
|
i++;
|
|
|
|
if ((info.stdPresent & XkbAlphabeticMask) == 0)
|
|
|
|
i++;
|
2009-03-28 12:45:05 -06:00
|
|
|
if (XkbcAllocClientMap(xkb, XkbKeyTypesMask, i) != Success)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
WSGO("Couldn't allocate client map\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
xkb->map->num_types = i;
|
|
|
|
if (XkbAllRequiredTypes & (~info.stdPresent))
|
|
|
|
{
|
|
|
|
unsigned missing, keypadVMod;
|
|
|
|
|
|
|
|
missing = XkbAllRequiredTypes & (~info.stdPresent);
|
|
|
|
keypadVMod = FindKeypadVMod(xkb);
|
2009-03-28 18:55:46 -06:00
|
|
|
if (XkbcInitCanonicalKeyTypes(xkb, missing, keypadVMod) != Success)
|
2009-03-27 07:55:32 -06:00
|
|
|
{
|
|
|
|
WSGO("Couldn't initialize canonical key types\n");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
if (missing & XkbOneLevelMask)
|
|
|
|
xkb->map->types[XkbOneLevelIndex].name = tok_ONE_LEVEL;
|
|
|
|
if (missing & XkbTwoLevelMask)
|
|
|
|
xkb->map->types[XkbTwoLevelIndex].name = tok_TWO_LEVEL;
|
|
|
|
if (missing & XkbAlphabeticMask)
|
|
|
|
xkb->map->types[XkbAlphabeticIndex].name = tok_ALPHABETIC;
|
|
|
|
if (missing & XkbKeypadMask)
|
|
|
|
xkb->map->types[XkbKeypadIndex].name = tok_KEYPAD;
|
|
|
|
}
|
|
|
|
next = &xkb->map->types[XkbLastRequiredType + 1];
|
|
|
|
for (i = 0, def = info.types; i < info.nTypes; i++)
|
|
|
|
{
|
|
|
|
if (def->name == tok_ONE_LEVEL)
|
|
|
|
type = &xkb->map->types[XkbOneLevelIndex];
|
|
|
|
else if (def->name == tok_TWO_LEVEL)
|
|
|
|
type = &xkb->map->types[XkbTwoLevelIndex];
|
|
|
|
else if (def->name == tok_ALPHABETIC)
|
|
|
|
type = &xkb->map->types[XkbAlphabeticIndex];
|
|
|
|
else if (def->name == tok_KEYPAD)
|
|
|
|
type = &xkb->map->types[XkbKeypadIndex];
|
|
|
|
else
|
|
|
|
type = next++;
|
|
|
|
DeleteLevel1MapEntries(def);
|
|
|
|
if (!CopyDefToKeyType(xkb, type, def))
|
|
|
|
return False;
|
|
|
|
def = (KeyTypeInfo *) def->defs.next;
|
|
|
|
}
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
return False;
|
|
|
|
}
|