blob: aa0a01044241974e1d17dd44f3e5b4187eee7f7a [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <stdio.h>
#include "log.h"
#include "strfmt.h"
#include "ktb.h"
#include "ktb_internal.h"
#include "ktb_inspect.h"
typedef struct {
KeyTable *table;
const KeyContext *ctx;
const char *path;
} KeyTableAuditorParameters;
#define KEY_TABLE_AUDITOR(name) int name (const KeyTableAuditorParameters *kta)
typedef KEY_TABLE_AUDITOR(KeyTableAuditor);
static void
reportKeyTableAudit (const char *audit) {
logMessage(LOG_WARNING, "%s", audit);
}
static
STR_BEGIN_FORMATTER(
formatKeyTableAuditPrefix,
const KeyTableAuditorParameters *kta,
const char *problem
)
if (kta->path) STR_PRINTF("%s: ", kta->path);
STR_PRINTF("%s", problem);
if (kta->ctx) STR_PRINTF(": %" PRIws, kta->ctx->name);
STR_END_FORMATTER
static KEY_TABLE_AUDITOR(reportKeyContextProblems) {
int ok = 1;
if (kta->ctx->name && !kta->ctx->isSpecial) {
const char *problem = NULL;
if (!kta->ctx->isDefined) {
problem = "undefined context";
} else if (!kta->ctx->isReferenced) {
problem = "unreferenced context";
} else if (!(kta->ctx->keyBindings.count ||
kta->ctx->mappedKeys.count ||
kta->ctx->mappedKeys.superimpose ||
kta->ctx->hotkeys.count)) {
problem = "empty context";
}
if (problem) {
ok = 0;
char audit[0X100];
STR_BEGIN(audit, sizeof(audit));
STR_FORMAT(formatKeyTableAuditPrefix, kta, problem);
STR_END;
reportKeyTableAudit(audit);
}
}
return ok;
}
static KEY_TABLE_AUDITOR(reportDuplicateKeyBindings) {
int ok = 1;
const KeyBinding *binding = kta->ctx->keyBindings.table;
const KeyBinding *end = binding + kta->ctx->keyBindings.count;
while (binding < end) {
if (binding->flags & KBF_DUPLICATE) {
ok = 0;
char audit[0X100];
STR_BEGIN(audit, sizeof(audit));
STR_FORMAT(formatKeyTableAuditPrefix, kta, "duplicate key binding");
STR_PRINTF(": ");
STR_FORMAT(formatKeyCombination, kta->table, &binding->keyCombination);
STR_END;
reportKeyTableAudit(audit);
}
binding += 1;
}
return ok;
}
static void
reportKeyProblem (const KeyTableAuditorParameters *kta, const KeyValue *key, const char *problem) {
char audit[0X100];
STR_BEGIN(audit, sizeof(audit));
STR_FORMAT(formatKeyTableAuditPrefix, kta, problem);
STR_PRINTF(": ");
STR_FORMAT(formatKeyName, kta->table, key);
STR_END;
reportKeyTableAudit(audit);
}
static KEY_TABLE_AUDITOR(reportDuplicateHotkeys) {
int ok = 1;
const HotkeyEntry *hotkey = kta->ctx->hotkeys.table;
const HotkeyEntry *end = hotkey + kta->ctx->hotkeys.count;
while (hotkey < end) {
if (hotkey->flags & HKF_DUPLICATE) {
ok = 0;
reportKeyProblem(kta, &hotkey->keyValue, "duplicate hotkey");
}
hotkey += 1;
}
return ok;
}
static KEY_TABLE_AUDITOR(reportDuplicateMappedKeys) {
int ok = 1;
const MappedKeyEntry *map = kta->ctx->mappedKeys.table;
const MappedKeyEntry *end = map + kta->ctx->mappedKeys.count;
while (map < end) {
if (map->flags & MKF_DUPLICATE) {
ok = 0;
reportKeyProblem(kta, &map->keyValue, "duplicate mapped key");
}
map += 1;
}
return ok;
}
int
auditKeyTable (KeyTable *table, const char *path) {
int ok = 1;
for (unsigned int context=0; context<table->keyContexts.count; context+=1) {
const KeyContext *ctx = getKeyContext(table, context);
if (ctx) {
static KeyTableAuditor *const auditors[] = {
reportKeyContextProblems,
reportDuplicateKeyBindings,
reportDuplicateHotkeys,
reportDuplicateMappedKeys,
NULL
};
const KeyTableAuditorParameters kta = {
.table = table,
.ctx = ctx,
.path = path
};
for (
KeyTableAuditor *const *auditor=auditors;
*auditor!=NULL; auditor+=1
) {
if (!(*auditor)(&kta)) ok = 0;
}
}
}
return ok;
}