1035 lines
30 KiB
C
1035 lines
30 KiB
C
/************************************************************
|
|
Copyright (c) 1996 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
|
|
documentation, and that the name of Silicon Graphics not be
|
|
used in advertising or publicity pertaining to distribution
|
|
of the software without specific prior written permission.
|
|
Silicon Graphics makes no representation about the suitability
|
|
of this software for any purpose. It is provided "as is"
|
|
without any express or implied warranty.
|
|
|
|
SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
|
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
|
|
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
********************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <locale.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xos.h>
|
|
#include <X11/XKBlib.h>
|
|
#include <X11/extensions/XKBfile.h>
|
|
#include <X11/extensions/XKBconfig.h>
|
|
#include <X11/extensions/XKBrules.h>
|
|
|
|
#ifndef PATH_MAX
|
|
#ifdef MAXPATHLEN
|
|
#define PATH_MAX MAXPATHLEN
|
|
#else
|
|
#define PATH_MAX 1024
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef DFLT_XKB_CONFIG_ROOT
|
|
#define DFLT_XKB_CONFIG_ROOT "/usr/share/X11/xkb"
|
|
#endif
|
|
#ifndef DFLT_XKB_RULES_FILE
|
|
#define DFLT_XKB_RULES_FILE __XKBDEFRULES__
|
|
#endif
|
|
#ifndef DFLT_XKB_LAYOUT
|
|
#define DFLT_XKB_LAYOUT "us"
|
|
#endif
|
|
#ifndef DFLT_XKB_MODEL
|
|
#define DFLT_XKB_MODEL "pc105"
|
|
#endif
|
|
|
|
/* Values used in svSrc to state how a value was obtained. The order of these
|
|
* is important, the bigger the higher the priority.
|
|
* e.g. FROM_CONFIG overrides FROM_SERVER */
|
|
#define UNDEFINED 0
|
|
#define FROM_SERVER 1 /* retrieved from server at runtime */
|
|
#define FROM_RULES 2 /* xkb rules file */
|
|
#define FROM_CONFIG 3 /* command-line specified config file */
|
|
#define FROM_CMD_LINE 4 /* specified at the cmdline */
|
|
#define NUM_SOURCES 5
|
|
|
|
/* Indices used into svSrc, svNValue */
|
|
#define RULES_NDX 0 /* rules file */
|
|
#define CONFIG_NDX 1 /* config file (if used) */
|
|
#define DISPLAY_NDX 2 /* X display name */
|
|
#define LOCALE_NDX 3 /* machine's locale */
|
|
#define MODEL_NDX 4
|
|
#define LAYOUT_NDX 5
|
|
#define VARIANT_NDX 6
|
|
#define KEYCODES_NDX 7
|
|
#define TYPES_NDX 8
|
|
#define COMPAT_NDX 9
|
|
#define SYMBOLS_NDX 10
|
|
#define GEOMETRY_NDX 11
|
|
#define KEYMAP_NDX 12
|
|
#define NUM_STRING_VALS 13
|
|
|
|
/***====================================================================***/
|
|
static Bool print = False;
|
|
static Bool synch = False;
|
|
static int verbose = 5;
|
|
|
|
static Display *dpy;
|
|
|
|
/**
|
|
* human-readable versions of FROM_CONFIG, FROM_SERVER, etc. Used for error
|
|
* reporting.
|
|
*/
|
|
static char *srcName[NUM_SOURCES] = {
|
|
"undefined", "X server", "rules file", "config file", "command line"
|
|
};
|
|
|
|
/**
|
|
* human-readable versions for RULES_NDX, CONFIG_NDX, etc. Used for error
|
|
* reporting.
|
|
*/
|
|
static char *svName[NUM_STRING_VALS] = {
|
|
"rules file", "config file", "X display", "locale",
|
|
"keyboard model", "keyboard layout", "layout variant",
|
|
"keycodes", "types", "compatibility map", "symbols", "geometry",
|
|
"keymap"
|
|
};
|
|
/**
|
|
* Holds the source for each of RULES, CONFIG, DISPLAY, etc.
|
|
* i.e. if svSrc[LAYOUT_NDX] == FROM_SERVER, then the layout has been fetched
|
|
* from the server.
|
|
*/
|
|
static int svSrc[NUM_STRING_VALS];
|
|
/**
|
|
* Holds the value for each of RULES, CONFIG, DISPLAY, etc.
|
|
*/
|
|
static char *svValue[NUM_STRING_VALS];
|
|
|
|
static XkbConfigRtrnRec cfgResult;
|
|
|
|
static XkbRF_RulesPtr rules = NULL;
|
|
static XkbRF_VarDefsRec rdefs;
|
|
|
|
static Bool clearOptions = False;
|
|
static int szOptions = 0;
|
|
static int numOptions = 0;
|
|
static char **options = NULL;
|
|
|
|
static int szInclPath = 0;
|
|
static int numInclPath = 0;
|
|
static char **inclPath = NULL;
|
|
|
|
static XkbDescPtr xkb = NULL;
|
|
|
|
static int deviceSpec = XkbUseCoreKbd;
|
|
|
|
/***====================================================================***/
|
|
|
|
#define streq(s1,s2) (strcmp(s1,s2)==0)
|
|
#define strpfx(s1,s2) (strncmp(s1,s2,strlen(s2))==0)
|
|
|
|
#define MSG(s) printf(s)
|
|
#define MSG1(s,a) printf(s,a)
|
|
#define MSG2(s,a,b) printf(s,a,b)
|
|
#define MSG3(s,a,b,c) printf(s,a,b,c)
|
|
|
|
#define VMSG(l,s) if (verbose>(l)) printf(s)
|
|
#define VMSG1(l,s,a) if (verbose>(l)) printf(s,a)
|
|
#define VMSG2(l,s,a,b) if (verbose>(l)) printf(s,a,b)
|
|
#define VMSG3(l,s,a,b,c) if (verbose>(l)) printf(s,a,b,c)
|
|
|
|
#define ERR(s) fprintf(stderr,s)
|
|
#define ERR1(s,a) fprintf(stderr,s,a)
|
|
#define ERR2(s,a,b) fprintf(stderr,s,a,b)
|
|
#define ERR3(s,a,b,c) fprintf(stderr,s,a,b,c)
|
|
|
|
/***====================================================================***/
|
|
|
|
Bool addToList(int *sz, int *num, char ***listIn, char *newVal);
|
|
void usage(int argc, char **argv);
|
|
void dumpNames(Bool wantRules, Bool wantCNames);
|
|
void trySetString(int which, char *newVal, int src);
|
|
Bool setOptString(int *arg, int argc, char **argv, int which, int src);
|
|
int parseArgs(int argc, char **argv);
|
|
Bool getDisplay(int argc, char **argv);
|
|
Bool getServerValues(void);
|
|
FILE *findFileInPath(char *name, char *subdir);
|
|
Bool addStringToOptions(char *opt_str, int *sz_opts, int *num_opts,
|
|
char ***opts);
|
|
char *stringFromOptions(char *orig, int numNew, char **newOpts);
|
|
Bool applyConfig(char *name);
|
|
Bool applyRules(void);
|
|
Bool applyComponentNames(void);
|
|
void printKeymap(void);
|
|
|
|
/***====================================================================***/
|
|
|
|
Bool
|
|
addToList(int *sz, int *num, char ***listIn, char *newVal)
|
|
{
|
|
register int i;
|
|
char **list;
|
|
|
|
if ((!newVal) || (!newVal[0]))
|
|
{
|
|
*num = 0;
|
|
return True;
|
|
}
|
|
list = *listIn;
|
|
for (i = 0; i < *num; i++)
|
|
{
|
|
if (streq(list[i], newVal))
|
|
return True;
|
|
}
|
|
if ((list == NULL) || (*sz < 1))
|
|
{
|
|
*num = 0;
|
|
*sz = 4;
|
|
list = (char **) calloc(*sz, sizeof(char *));
|
|
*listIn = list;
|
|
}
|
|
else if (*num >= *sz)
|
|
{
|
|
*sz *= 2;
|
|
list = (char **) realloc(list, (*sz) * sizeof(char *));
|
|
*listIn = list;
|
|
}
|
|
if (!list)
|
|
{
|
|
ERR("Internal Error! Allocation failure in add to list!\n");
|
|
ERR(" Exiting.\n");
|
|
exit(-1);
|
|
}
|
|
list[*num] = strdup(newVal);
|
|
(*num) = (*num) + 1;
|
|
return True;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
void
|
|
usage(int argc, char **argv)
|
|
{
|
|
MSG1("Usage: %s [args] [<layout> [<variant> [<option> ... ]]]\n",
|
|
argv[0]);
|
|
MSG("Where legal args are:\n");
|
|
MSG("-?,-help Print this message\n");
|
|
MSG("-compat <name> Specifies compatibility map component name\n");
|
|
MSG("-config <file> Specifies configuration file to use\n");
|
|
MSG("-device <deviceid> Specifies the device ID to use\n");
|
|
MSG("-display <dpy> Specifies display to use\n");
|
|
MSG("-geometry <name> Specifies geometry component name\n");
|
|
MSG("-I[<dir>] Add <dir> to list of directories to be used\n");
|
|
MSG("-keycodes <name> Specifies keycodes component name\n");
|
|
MSG("-keymap <name> Specifies name of keymap to load\n");
|
|
MSG("-layout <name> Specifies layout used to choose component names\n");
|
|
MSG("-model <name> Specifies model used to choose component names\n");
|
|
MSG("-option <name> Adds an option used to choose component names\n");
|
|
MSG("-print Print a complete xkb_keymap description and exit\n");
|
|
MSG("-rules <name> Name of rules file to use\n");
|
|
MSG("-symbols <name> Specifies symbols component name\n");
|
|
MSG("-synch Synchronize request w/X server\n");
|
|
MSG("-types <name> Specifies types component name\n");
|
|
MSG("-v[erbose] [<lvl>] Sets verbosity (1..10). Higher values yield\n");
|
|
MSG(" more messages\n");
|
|
MSG("-variant <name> Specifies layout variant used to choose component names\n");
|
|
}
|
|
|
|
void
|
|
dumpNames(Bool wantRules, Bool wantCNames)
|
|
{
|
|
if (wantRules)
|
|
{
|
|
if (svValue[MODEL_NDX])
|
|
MSG1("model: %s\n", svValue[MODEL_NDX]);
|
|
if (svValue[LAYOUT_NDX])
|
|
MSG1("layout: %s\n", svValue[LAYOUT_NDX]);
|
|
if (svValue[VARIANT_NDX])
|
|
MSG1("variant: %s\n", svValue[VARIANT_NDX]);
|
|
if (options)
|
|
{
|
|
char *opt_str = stringFromOptions(NULL, numOptions, options);
|
|
MSG1("options: %s\n", opt_str);
|
|
free(opt_str);
|
|
}
|
|
}
|
|
if (wantCNames)
|
|
{
|
|
if (svValue[KEYMAP_NDX])
|
|
MSG1("keymap: %s\n", svValue[KEYMAP_NDX]);
|
|
if (svValue[KEYCODES_NDX])
|
|
MSG1("keycodes: %s\n", svValue[KEYCODES_NDX]);
|
|
if (svValue[TYPES_NDX])
|
|
MSG1("types: %s\n", svValue[TYPES_NDX]);
|
|
if (svValue[COMPAT_NDX])
|
|
MSG1("compat: %s\n", svValue[COMPAT_NDX]);
|
|
if (svValue[SYMBOLS_NDX])
|
|
MSG1("symbols: %s\n", svValue[SYMBOLS_NDX]);
|
|
if (svValue[GEOMETRY_NDX])
|
|
MSG1("geometry: %s\n", svValue[GEOMETRY_NDX]);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
/**
|
|
* Set the given string (obtained from src) in the svValue/svSrc globals.
|
|
* If the given item is already set, it is overridden if the original source
|
|
* is less significant than the given one.
|
|
*
|
|
* @param which What value is it (one of RULES_NDX, CONFIG_NDX, ...)
|
|
*/
|
|
void
|
|
trySetString(int which, char *newVal, int src)
|
|
{
|
|
if (svValue[which] != NULL)
|
|
{
|
|
if (svSrc[which] == src)
|
|
{
|
|
VMSG2(0, "Warning! More than one %s from %s\n",
|
|
svName[which], srcName[src]);
|
|
VMSG2(0, " Using \"%s\", ignoring \"%s\"\n",
|
|
svValue[which], newVal);
|
|
return;
|
|
}
|
|
else if (svSrc[which] > src)
|
|
{
|
|
VMSG1(5, "Warning! Multiple definitions of %s\n", svName[which]);
|
|
VMSG2(5, " Using %s, ignoring %s\n",
|
|
srcName[svSrc[which]], srcName[src]);
|
|
return;
|
|
}
|
|
}
|
|
svSrc[which] = src;
|
|
svValue[which] = newVal;
|
|
return;
|
|
}
|
|
|
|
Bool
|
|
setOptString(int *arg, int argc, char **argv, int which, int src)
|
|
{
|
|
int ndx;
|
|
char *opt;
|
|
|
|
ndx = *arg;
|
|
opt = argv[ndx];
|
|
if (ndx >= argc - 1)
|
|
{
|
|
VMSG1(0, "No %s specified on the command line\n", svName[which]);
|
|
VMSG1(0, "Trailing %s option ignored\n", opt);
|
|
return True;
|
|
}
|
|
ndx++;
|
|
*arg = ndx;
|
|
if (svValue[which] != NULL)
|
|
{
|
|
if (svSrc[which] == src)
|
|
{
|
|
VMSG2(0, "More than one %s on %s\n", svName[which], srcName[src]);
|
|
VMSG2(0, "Using \"%s\", ignoring \"%s\"\n", svValue[which],
|
|
argv[ndx]);
|
|
return True;
|
|
}
|
|
else if (svSrc[which] > src)
|
|
{
|
|
VMSG1(5, "Multiple definitions of %s\n", svName[which]);
|
|
VMSG2(5, "Using %s, ignoring %s\n", srcName[svSrc[which]],
|
|
srcName[src]);
|
|
return True;
|
|
}
|
|
}
|
|
svSrc[which] = src;
|
|
svValue[which] = argv[ndx];
|
|
return True;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
/**
|
|
* Parse commandline arguments.
|
|
* Return True on success or False if an unrecognized option has been
|
|
* specified.
|
|
*/
|
|
int
|
|
parseArgs(int argc, char **argv)
|
|
{
|
|
int i;
|
|
Bool ok;
|
|
unsigned present;
|
|
|
|
ok = True;
|
|
addToList(&szInclPath, &numInclPath, &inclPath, ".");
|
|
addToList(&szInclPath, &numInclPath, &inclPath, DFLT_XKB_CONFIG_ROOT);
|
|
for (i = 1; (i < argc) && ok; i++)
|
|
{
|
|
if (argv[i][0] != '-')
|
|
{
|
|
/* Allow a call like "setxkbmap us" to work. Layout is default,
|
|
if -layout is given, then try parsing variant, then options */
|
|
if (!svSrc[LAYOUT_NDX])
|
|
trySetString(LAYOUT_NDX, argv[i], FROM_CMD_LINE);
|
|
else if (!svSrc[VARIANT_NDX])
|
|
trySetString(VARIANT_NDX, argv[i], FROM_CMD_LINE);
|
|
else
|
|
ok = addToList(&szOptions, &numOptions, &options, argv[i]);
|
|
}
|
|
else if (streq(argv[i], "-compat"))
|
|
ok = setOptString(&i, argc, argv, COMPAT_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-config"))
|
|
ok = setOptString(&i, argc, argv, CONFIG_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-device"))
|
|
deviceSpec = atoi(argv[++i]); /* only allow device IDs, not names */
|
|
else if (streq(argv[i], "-display"))
|
|
ok = setOptString(&i, argc, argv, DISPLAY_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-geometry"))
|
|
ok = setOptString(&i, argc, argv, GEOMETRY_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-help") || streq(argv[i], "-?"))
|
|
{
|
|
usage(argc, argv);
|
|
exit(0);
|
|
}
|
|
else if (strpfx(argv[i], "-I"))
|
|
ok = addToList(&szInclPath, &numInclPath, &inclPath, &argv[i][2]);
|
|
else if (streq(argv[i], "-keycodes"))
|
|
ok = setOptString(&i, argc, argv, KEYCODES_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-keymap"))
|
|
ok = setOptString(&i, argc, argv, KEYMAP_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-layout"))
|
|
ok = setOptString(&i, argc, argv, LAYOUT_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-model"))
|
|
ok = setOptString(&i, argc, argv, MODEL_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-option"))
|
|
{
|
|
if ((i == argc - 1) || (argv[i + 1][0] == '\0')
|
|
|| (argv[i + 1][0] == '-'))
|
|
{
|
|
clearOptions = True;
|
|
ok = addToList(&szOptions, &numOptions, &options, "");
|
|
if (i < argc - 1 && argv[i + 1][0] == '\0')
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
ok = addToList(&szOptions, &numOptions, &options, argv[++i]);
|
|
}
|
|
}
|
|
else if (streq(argv[i], "-print"))
|
|
print = True;
|
|
else if (streq(argv[i], "-rules"))
|
|
ok = setOptString(&i, argc, argv, RULES_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-symbols"))
|
|
ok = setOptString(&i, argc, argv, SYMBOLS_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-synch"))
|
|
synch = True;
|
|
else if (streq(argv[i], "-types"))
|
|
ok = setOptString(&i, argc, argv, TYPES_NDX, FROM_CMD_LINE);
|
|
else if (streq(argv[i], "-verbose") || (streq(argv[i], "-v")))
|
|
{
|
|
if ((i < argc - 1) && (isdigit(argv[i + 1][0])))
|
|
verbose = atoi(argv[++i]);
|
|
else
|
|
verbose++;
|
|
if (verbose < 0)
|
|
{
|
|
ERR1("Illegal verbose level %d. Reset to 0\n", verbose);
|
|
verbose = 0;
|
|
}
|
|
else if (verbose > 10)
|
|
{
|
|
ERR1("Illegal verbose level %d. Reset to 10\n", verbose);
|
|
verbose = 10;
|
|
}
|
|
VMSG1(7, "Setting verbose level to %d\n", verbose);
|
|
}
|
|
else if (streq(argv[i], "-variant"))
|
|
ok = setOptString(&i, argc, argv, VARIANT_NDX, FROM_CMD_LINE);
|
|
else
|
|
{
|
|
ERR1("Error! Option \"%s\" not recognized\n", argv[i]);
|
|
ok = False;
|
|
}
|
|
}
|
|
|
|
present = 0;
|
|
if (svValue[TYPES_NDX])
|
|
present++;
|
|
if (svValue[COMPAT_NDX])
|
|
present++;
|
|
if (svValue[SYMBOLS_NDX])
|
|
present++;
|
|
if (svValue[KEYCODES_NDX])
|
|
present++;
|
|
if (svValue[GEOMETRY_NDX])
|
|
present++;
|
|
if (svValue[CONFIG_NDX])
|
|
present++;
|
|
if (svValue[MODEL_NDX])
|
|
present++;
|
|
if (svValue[LAYOUT_NDX])
|
|
present++;
|
|
if (svValue[VARIANT_NDX])
|
|
present++;
|
|
if (svValue[KEYMAP_NDX] && present)
|
|
{
|
|
ERR("No other components can be specified when a keymap is present\n");
|
|
return False;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* Open a connection to the display and print error if it fails.
|
|
*
|
|
* @return True on success or False otherwise.
|
|
*/
|
|
Bool
|
|
getDisplay(int argc, char **argv)
|
|
{
|
|
int major, minor, why;
|
|
|
|
major = XkbMajorVersion;
|
|
minor = XkbMinorVersion;
|
|
dpy =
|
|
XkbOpenDisplay(svValue[DISPLAY_NDX], NULL, NULL, &major, &minor,
|
|
&why);
|
|
if (!dpy)
|
|
{
|
|
if (svValue[DISPLAY_NDX] == NULL)
|
|
svValue[DISPLAY_NDX] = getenv("DISPLAY");
|
|
if (svValue[DISPLAY_NDX] == NULL)
|
|
svValue[DISPLAY_NDX] = "default display";
|
|
switch (why)
|
|
{
|
|
case XkbOD_BadLibraryVersion:
|
|
ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
|
|
XkbMajorVersion, XkbMinorVersion);
|
|
ERR2("Xlib supports incompatible version %d.%02d\n",
|
|
major, minor);
|
|
break;
|
|
case XkbOD_ConnectionRefused:
|
|
ERR1("Cannot open display \"%s\"\n", svValue[DISPLAY_NDX]);
|
|
break;
|
|
case XkbOD_NonXkbServer:
|
|
ERR1("XKB extension not present on %s\n", svValue[DISPLAY_NDX]);
|
|
break;
|
|
case XkbOD_BadServerVersion:
|
|
ERR3("%s was compiled with XKB version %d.%02d\n", argv[0],
|
|
XkbMajorVersion, XkbMinorVersion);
|
|
ERR3("Server %s uses incompatible version %d.%02d\n",
|
|
svValue[DISPLAY_NDX], major, minor);
|
|
break;
|
|
default:
|
|
ERR1("Unknown error %d from XkbOpenDisplay\n", why);
|
|
break;
|
|
}
|
|
return False;
|
|
}
|
|
if (synch)
|
|
XSynchronize(dpy, True);
|
|
return True;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
/**
|
|
* Retrieve xkb values from th the XKB_RULES_NAMES property and store their
|
|
* contents in svValues.
|
|
* If the property cannot be read, the built-in defaults are used.
|
|
*
|
|
* @return True.
|
|
*/
|
|
Bool
|
|
getServerValues(void)
|
|
{
|
|
XkbRF_VarDefsRec vd;
|
|
char *tmp = NULL;
|
|
|
|
if (!XkbRF_GetNamesProp(dpy, &tmp, &vd) || !tmp)
|
|
{
|
|
VMSG1(3, "Couldn't interpret %s property\n", _XKB_RF_NAMES_PROP_ATOM);
|
|
tmp = DFLT_XKB_RULES_FILE;
|
|
vd.model = DFLT_XKB_MODEL;
|
|
vd.layout = DFLT_XKB_LAYOUT;
|
|
vd.variant = NULL;
|
|
vd.options = NULL;
|
|
VMSG3(3, "Use defaults: rules - '%s' model - '%s' layout - '%s'\n",
|
|
tmp, vd.model, vd.layout);
|
|
}
|
|
if (tmp)
|
|
trySetString(RULES_NDX, tmp, FROM_SERVER);
|
|
if (vd.model)
|
|
trySetString(MODEL_NDX, vd.model, FROM_SERVER);
|
|
if (vd.layout)
|
|
trySetString(LAYOUT_NDX, vd.layout, FROM_SERVER);
|
|
if (vd.variant)
|
|
trySetString(VARIANT_NDX, vd.variant, FROM_SERVER);
|
|
if ((vd.options) && (!clearOptions))
|
|
{
|
|
addStringToOptions(vd.options, &szOptions, &numOptions, &options);
|
|
XFree(vd.options);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
FILE *
|
|
findFileInPath(char *name, char *subdir)
|
|
{
|
|
register int i;
|
|
char buf[PATH_MAX];
|
|
FILE *fp;
|
|
|
|
if (name[0] == '/')
|
|
{
|
|
fp = fopen(name, "r");
|
|
if ((verbose > 7) || ((!fp) && (verbose > 0)))
|
|
MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), name);
|
|
return fp;
|
|
}
|
|
for (i = 0; (i < numInclPath); i++)
|
|
{
|
|
if ((strlen(inclPath[i]) + strlen(subdir) + strlen(name) + 2) >
|
|
PATH_MAX)
|
|
{
|
|
VMSG3(0, "Path too long (%s/%s%s). Ignored.\n", inclPath[i],
|
|
subdir, name);
|
|
continue;
|
|
}
|
|
sprintf(buf, "%s/%s%s", inclPath[i], subdir, name);
|
|
fp = fopen(name, "r");
|
|
if ((verbose > 7) || ((!fp) && (verbose > 5)))
|
|
MSG2("%s file %s\n", (fp ? "Found" : "Didn't find"), buf);
|
|
if (fp != NULL)
|
|
return fp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
Bool
|
|
addStringToOptions(char *opt_str, int *sz_opts, int *num_opts, char ***opts)
|
|
{
|
|
char *tmp, *str, *next;
|
|
Bool ok = True;
|
|
|
|
if ((str = strdup(opt_str)) == NULL)
|
|
return False;
|
|
for (tmp = str, next = NULL; (tmp && *tmp != '\0') && ok; tmp = next)
|
|
{
|
|
next = strchr(str, ',');
|
|
if (next)
|
|
{
|
|
*next = '\0';
|
|
next++;
|
|
}
|
|
ok = addToList(sz_opts, num_opts, opts, tmp) && ok;
|
|
}
|
|
free(str);
|
|
return ok;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
char *
|
|
stringFromOptions(char *orig, int numNew, char **newOpts)
|
|
{
|
|
int len, i, nOut;
|
|
|
|
if (orig)
|
|
len = strlen(orig) + 1;
|
|
else
|
|
len = 0;
|
|
for (i = 0; i < numNew; i++)
|
|
{
|
|
if (newOpts[i])
|
|
len += strlen(newOpts[i]) + 1;
|
|
}
|
|
if (len < 1)
|
|
return NULL;
|
|
if (orig)
|
|
{
|
|
orig = (char *) realloc(orig, len);
|
|
if (!orig)
|
|
{
|
|
ERR("OOM in stringFromOptions\n");
|
|
return NULL;
|
|
}
|
|
nOut = 1;
|
|
}
|
|
else
|
|
{
|
|
orig = (char *) calloc(len, 1);
|
|
if (!orig)
|
|
{
|
|
ERR("OOM in stringFromOptions\n");
|
|
return NULL;
|
|
}
|
|
nOut = 0;
|
|
}
|
|
for (i = 0; i < numNew; i++)
|
|
{
|
|
if (!newOpts[i])
|
|
continue;
|
|
if (nOut > 0)
|
|
{
|
|
strcat(orig, ",");
|
|
strcat(orig, newOpts[i]);
|
|
}
|
|
else
|
|
strcpy(orig, newOpts[i]);
|
|
nOut++;
|
|
}
|
|
return orig;
|
|
}
|
|
|
|
/***====================================================================***/
|
|
|
|
Bool
|
|
applyConfig(char *name)
|
|
{
|
|
FILE *fp;
|
|
Bool ok;
|
|
|
|
if ((fp = findFileInPath(name, "")) == NULL)
|
|
return False;
|
|
ok = XkbCFParse(fp, XkbCFDflts, NULL, &cfgResult);
|
|
fclose(fp);
|
|
if (!ok)
|
|
{
|
|
ERR1("Couldn't find configuration file \"%s\"\n", name);
|
|
return False;
|
|
}
|
|
if (cfgResult.rules_file)
|
|
{
|
|
trySetString(RULES_NDX, cfgResult.rules_file, FROM_CONFIG);
|
|
cfgResult.rules_file = NULL;
|
|
}
|
|
if (cfgResult.model)
|
|
{
|
|
trySetString(MODEL_NDX, cfgResult.model, FROM_CONFIG);
|
|
cfgResult.model = NULL;
|
|
}
|
|
if (cfgResult.layout)
|
|
{
|
|
trySetString(LAYOUT_NDX, cfgResult.layout, FROM_CONFIG);
|
|
cfgResult.layout = NULL;
|
|
}
|
|
if (cfgResult.variant)
|
|
{
|
|
trySetString(VARIANT_NDX, cfgResult.variant, FROM_CONFIG);
|
|
cfgResult.variant = NULL;
|
|
}
|
|
if (cfgResult.options)
|
|
{
|
|
addStringToOptions(cfgResult.options, &szOptions, &numOptions,
|
|
&options);
|
|
cfgResult.options = NULL;
|
|
}
|
|
if (cfgResult.keymap)
|
|
{
|
|
trySetString(KEYMAP_NDX, cfgResult.keymap, FROM_CONFIG);
|
|
cfgResult.keymap = NULL;
|
|
}
|
|
if (cfgResult.keycodes)
|
|
{
|
|
trySetString(KEYCODES_NDX, cfgResult.keycodes, FROM_CONFIG);
|
|
cfgResult.keycodes = NULL;
|
|
}
|
|
if (cfgResult.geometry)
|
|
{
|
|
trySetString(GEOMETRY_NDX, cfgResult.geometry, FROM_CONFIG);
|
|
cfgResult.geometry = NULL;
|
|
}
|
|
if (cfgResult.symbols)
|
|
{
|
|
trySetString(SYMBOLS_NDX, cfgResult.symbols, FROM_CONFIG);
|
|
cfgResult.symbols = NULL;
|
|
}
|
|
if (cfgResult.types)
|
|
{
|
|
trySetString(TYPES_NDX, cfgResult.types, FROM_CONFIG);
|
|
cfgResult.types = NULL;
|
|
}
|
|
if (cfgResult.compat)
|
|
{
|
|
trySetString(COMPAT_NDX, cfgResult.compat, FROM_CONFIG);
|
|
cfgResult.compat = NULL;
|
|
}
|
|
if (verbose > 5)
|
|
{
|
|
MSG("After config file:\n");
|
|
dumpNames(True, True);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
/**
|
|
* If any of model, layout, variant or options is specified, then compile the
|
|
* options into the
|
|
*
|
|
* @return True on success or false otherwise.
|
|
*/
|
|
Bool
|
|
applyRules(void)
|
|
{
|
|
int i;
|
|
char *rfName;
|
|
|
|
if (svSrc[MODEL_NDX] || svSrc[LAYOUT_NDX] || svSrc[VARIANT_NDX]
|
|
|| options)
|
|
{
|
|
char buf[PATH_MAX];
|
|
XkbComponentNamesRec rnames;
|
|
|
|
if (svSrc[VARIANT_NDX] < svSrc[LAYOUT_NDX])
|
|
svValue[VARIANT_NDX] = NULL;
|
|
|
|
rdefs.model = svValue[MODEL_NDX];
|
|
rdefs.layout = svValue[LAYOUT_NDX];
|
|
rdefs.variant = svValue[VARIANT_NDX];
|
|
if (options)
|
|
rdefs.options =
|
|
stringFromOptions(rdefs.options, numOptions, options);
|
|
|
|
if (svSrc[RULES_NDX])
|
|
rfName = svValue[RULES_NDX];
|
|
else
|
|
rfName = DFLT_XKB_RULES_FILE;
|
|
|
|
if (rfName[0] == '/')
|
|
{
|
|
rules = XkbRF_Load(rfName, svValue[LOCALE_NDX], True, True);
|
|
}
|
|
else
|
|
{
|
|
/* try to load rules files from all include paths until the first
|
|
* we succeed with */
|
|
for (i = 0; (i < numInclPath) && (!rules); i++)
|
|
{
|
|
if ((strlen(inclPath[i]) + strlen(rfName) + 8) > PATH_MAX)
|
|
{
|
|
VMSG2(0, "Path too long (%s/rules/%s). Ignored.\n",
|
|
inclPath[i], rfName);
|
|
continue;
|
|
}
|
|
sprintf(buf, "%s/rules/%s", inclPath[i], svValue[RULES_NDX]);
|
|
rules = XkbRF_Load(buf, svValue[LOCALE_NDX], True, True);
|
|
}
|
|
}
|
|
if (!rules)
|
|
{
|
|
ERR1("Couldn't find rules file (%s) \n", svValue[RULES_NDX]);
|
|
return False;
|
|
}
|
|
/* Let the rules file to the magic, then update the svValues with
|
|
* those returned after processing the rules */
|
|
XkbRF_GetComponents(rules, &rdefs, &rnames);
|
|
if (rnames.keycodes)
|
|
{
|
|
trySetString(KEYCODES_NDX, rnames.keycodes, FROM_RULES);
|
|
rnames.keycodes = NULL;
|
|
}
|
|
if (rnames.symbols)
|
|
{
|
|
trySetString(SYMBOLS_NDX, rnames.symbols, FROM_RULES);
|
|
rnames.symbols = NULL;
|
|
}
|
|
if (rnames.types)
|
|
{
|
|
trySetString(TYPES_NDX, rnames.types, FROM_RULES);
|
|
rnames.types = NULL;
|
|
}
|
|
if (rnames.compat)
|
|
{
|
|
trySetString(COMPAT_NDX, rnames.compat, FROM_RULES);
|
|
rnames.compat = NULL;
|
|
}
|
|
if (rnames.geometry)
|
|
{
|
|
trySetString(GEOMETRY_NDX, rnames.geometry, FROM_RULES);
|
|
rnames.geometry = NULL;
|
|
}
|
|
if (rnames.keymap)
|
|
{
|
|
trySetString(KEYMAP_NDX, rnames.keymap, FROM_RULES);
|
|
rnames.keymap = NULL;
|
|
}
|
|
if (verbose > 6)
|
|
{
|
|
MSG1("Applied rules from %s:\n", svValue[RULES_NDX]);
|
|
dumpNames(True, False);
|
|
}
|
|
}
|
|
else if (verbose > 6)
|
|
{
|
|
MSG("No rules variables specified. Rules file ignored\n");
|
|
}
|
|
return True;
|
|
}
|
|
|
|
/* Primitive sanity check - filter out 'map names' (inside parenthesis) */
|
|
/* that can confuse xkbcomp parser */
|
|
static Bool
|
|
checkName(char *name, char *string)
|
|
{
|
|
char *i = name, *opar = NULL;
|
|
Bool ret = True;
|
|
|
|
if (!name)
|
|
return True;
|
|
|
|
while (*i)
|
|
{
|
|
if (opar == NULL)
|
|
{
|
|
if (*i == '(')
|
|
opar = i;
|
|
}
|
|
else
|
|
{
|
|
if ((*i == '(') || (*i == '|') || (*i == '+'))
|
|
{
|
|
ret = False;
|
|
break;
|
|
}
|
|
if (*i == ')')
|
|
opar = NULL;
|
|
}
|
|
i++;
|
|
}
|
|
if (opar)
|
|
ret = False;
|
|
if (!ret)
|
|
{
|
|
char c;
|
|
int n = 1;
|
|
for (i = opar + 1; *i && n; i++)
|
|
{
|
|
if (*i == '(')
|
|
n++;
|
|
if (*i == ')')
|
|
n--;
|
|
}
|
|
if (*i)
|
|
i++;
|
|
c = *i;
|
|
*i = '\0';
|
|
ERR1("Illegal map name '%s' ", opar);
|
|
*i = c;
|
|
ERR2("in %s name '%s'\n", string, name);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
printKeymap(void)
|
|
{
|
|
MSG("xkb_keymap {\n");
|
|
if (svValue[KEYCODES_NDX])
|
|
MSG1("\txkb_keycodes { include \"%s\"\t};\n", svValue[KEYCODES_NDX]);
|
|
if (svValue[TYPES_NDX])
|
|
MSG1("\txkb_types { include \"%s\"\t};\n", svValue[TYPES_NDX]);
|
|
if (svValue[COMPAT_NDX])
|
|
MSG1("\txkb_compat { include \"%s\"\t};\n", svValue[COMPAT_NDX]);
|
|
if (svValue[SYMBOLS_NDX])
|
|
MSG1("\txkb_symbols { include \"%s\"\t};\n", svValue[SYMBOLS_NDX]);
|
|
if (svValue[GEOMETRY_NDX])
|
|
MSG1("\txkb_geometry { include \"%s\"\t};\n", svValue[GEOMETRY_NDX]);
|
|
MSG("};\n");
|
|
}
|
|
|
|
Bool
|
|
applyComponentNames(void)
|
|
{
|
|
if (!checkName(svValue[TYPES_NDX], "types"))
|
|
return False;
|
|
if (!checkName(svValue[COMPAT_NDX], "compat"))
|
|
return False;
|
|
if (!checkName(svValue[SYMBOLS_NDX], "symbols"))
|
|
return False;
|
|
if (!checkName(svValue[KEYCODES_NDX], "keycodes"))
|
|
return False;
|
|
if (!checkName(svValue[GEOMETRY_NDX], "geometry"))
|
|
return False;
|
|
if (!checkName(svValue[KEYMAP_NDX], "keymap"))
|
|
return False;
|
|
|
|
if (verbose > 5)
|
|
{
|
|
MSG("Trying to build keymap using the following components:\n");
|
|
dumpNames(False, True);
|
|
}
|
|
/* Upload the new description to the server. */
|
|
if (dpy && !print)
|
|
{
|
|
XkbComponentNamesRec cmdNames;
|
|
cmdNames.types = svValue[TYPES_NDX];
|
|
cmdNames.compat = svValue[COMPAT_NDX];
|
|
cmdNames.symbols = svValue[SYMBOLS_NDX];
|
|
cmdNames.keycodes = svValue[KEYCODES_NDX];
|
|
cmdNames.geometry = svValue[GEOMETRY_NDX];
|
|
cmdNames.keymap = svValue[KEYMAP_NDX];
|
|
xkb = XkbGetKeyboardByName(dpy, deviceSpec, &cmdNames,
|
|
XkbGBN_AllComponentsMask,
|
|
XkbGBN_AllComponentsMask &
|
|
(~XkbGBN_GeometryMask), True);
|
|
if (!xkb)
|
|
{
|
|
ERR("Error loading new keyboard description\n");
|
|
return False;
|
|
}
|
|
/* update the XKB root property */
|
|
if (svValue[RULES_NDX] && (rdefs.model || rdefs.layout))
|
|
{
|
|
if (!XkbRF_SetNamesProp(dpy, svValue[RULES_NDX], &rdefs))
|
|
{
|
|
VMSG(0, "Error updating the XKB names property\n");
|
|
}
|
|
}
|
|
}
|
|
if (print)
|
|
{
|
|
printKeymap();
|
|
}
|
|
return True;
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
if ((!parseArgs(argc, argv)) || (!getDisplay(argc, argv)))
|
|
exit(-1);
|
|
svValue[LOCALE_NDX] = setlocale(LC_ALL, svValue[LOCALE_NDX]);
|
|
svSrc[LOCALE_NDX] = FROM_SERVER;
|
|
VMSG1(7, "locale is %s\n", svValue[LOCALE_NDX]);
|
|
if (dpy)
|
|
getServerValues();
|
|
if (svValue[CONFIG_NDX] && (!applyConfig(svValue[CONFIG_NDX])))
|
|
exit(-3);
|
|
if (!applyRules())
|
|
exit(-4);
|
|
if (!applyComponentNames())
|
|
exit(-5);
|
|
if (dpy)
|
|
XCloseDisplay(dpy);
|
|
exit(0);
|
|
}
|