Newer
Older
/*
* 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-2018 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.
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include <ctype.h>
const KeyboardFunction keyboardFunctionTable[] = {
{.name="dot1", .bit=BRL_DOT1},
{.name="dot2", .bit=BRL_DOT2},
{.name="dot3", .bit=BRL_DOT3},
{.name="dot4", .bit=BRL_DOT4},
{.name="dot5", .bit=BRL_DOT5},
{.name="dot6", .bit=BRL_DOT6},
{.name="dot7", .bit=BRL_DOT7},
{.name="dot8", .bit=BRL_DOT8},
{.name="space", .bit=BRL_DOTC},
{.name="control", .bit=BRL_FLG_INPUT_CONTROL},
{.name="meta", .bit=BRL_FLG_INPUT_META},
{.name="altgr", .bit=BRL_FLG_INPUT_ALTGR},
{.name="gui", .bit=BRL_FLG_INPUT_GUI}
unsigned char keyboardFunctionCount = ARRAY_COUNT(keyboardFunctionTable);
const CommandEntry **commandTable;
unsigned int commandCount;
unsigned char context;
unsigned hideRequested:1;
void
copyKeyValues (KeyValue *target, const KeyValue *source, unsigned int count) {
memcpy(target, source, count*sizeof(*target));
}
int
compareKeyValues (const KeyValue *value1, const KeyValue *value2) {
if (value1->group < value2->group) return -1;
if (value1->group > value2->group) return 1;
if (value1->number < value2->number) return -1;
if (value1->number > value2->number) return 1;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
return 0;
}
static int
compareKeyArrays (
unsigned int count1, const KeyValue *array1,
unsigned int count2, const KeyValue *array2
) {
if (count1 < count2) return -1;
if (count1 > count2) return 1;
return memcmp(array1, array2, count1*sizeof(*array1));
}
int
findKeyValue (
const KeyValue *values, unsigned int count,
const KeyValue *target, unsigned int *position
) {
int first = 0;
int last = count - 1;
while (first <= last) {
int current = (first + last) / 2;
const KeyValue *value = &values[current];
int relation = compareKeyValues(target, value);
if (!relation) {
*position = current;
return 1;
}
if (relation < 0) {
last = current - 1;
} else {
first = current + 1;
}
}
*position = first;
return 0;
}
int
insertKeyValue (
KeyValue **values, unsigned int *count, unsigned int *size,
const KeyValue *value, unsigned int position
) {
if (*count == *size) {
unsigned int newSize = (*size)? (*size)<<1: 0X10;
KeyValue *newValues = realloc(*values, ARRAY_SIZE(newValues, newSize));
if (!newValues) {
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
return 0;
}
*values = newValues;
*size = newSize;
}
memmove(&(*values)[position+1], &(*values)[position],
((*count)++ - position) * sizeof(**values));
(*values)[position] = *value;
return 1;
}
void
removeKeyValue (KeyValue *values, unsigned int *count, unsigned int position) {
memmove(&values[position], &values[position+1],
(--*count - position) * sizeof(*values));
}
int
deleteKeyValue (KeyValue *values, unsigned int *count, const KeyValue *value) {
unsigned int position;
int found = findKeyValue(values, *count, value, &position);
if (found) removeKeyValue(values, count, position);
return found;
}
static inline int
hideBindings (const KeyTableData *ktd) {
return ktd->hideRequested || ktd->hideInherited;
}
static KeyContext *
getKeyContext (KeyTableData *ktd, unsigned char context) {
if (context >= ktd->table->keyContexts.count) {
KeyContext *newTable = realloc(ktd->table->keyContexts.table, ARRAY_SIZE(newTable, newCount));
while (ktd->table->keyContexts.count < newCount) {
KeyContext *ctx = &ktd->table->keyContexts.table[ktd->table->keyContexts.count++];
ctx->isSpecial = 0;
ctx->isDefined = 0;
ctx->isReferenced = 0;
ctx->keyBindings.table = NULL;
ctx->keyBindings.size = 0;
ctx->keyBindings.count = 0;
ctx->keyBindings.sorted = NULL;
ctx->hotkeys.table = NULL;
ctx->hotkeys.count = 0;
ctx->hotkeys.sorted = NULL;
ctx->mappedKeys.table = NULL;
ctx->mappedKeys.count = 0;
ctx->mappedKeys.sorted = NULL;
ctx->mappedKeys.superimpose = 0;
return &ktd->table->keyContexts.table[context];
}
static inline KeyContext *
getCurrentKeyContext (KeyTableData *ktd) {
return getKeyContext(ktd, ktd->context);
}
static int
setString (wchar_t **string, const wchar_t *characters, size_t length) {
if (*string) free(*string);
if (!(*string = malloc(ARRAY_SIZE(*string, length+1)))) {
wmemcpy(*string, characters, length);
(*string)[length] = 0;
setKeyContextName (KeyContext *ctx, const wchar_t *name, size_t length) {
return setString(&ctx->name, name, length);
}
static int
setKeyContextTitle (KeyContext *ctx, const wchar_t *title, size_t length) {
return setString(&ctx->title, title, length);
}
static int
findKeyContext (unsigned char *context, const wchar_t *name, int length, KeyTableData *ktd) {
for (*context=0; *context<ktd->table->keyContexts.count; *context+=1) {
KeyContext *ctx = &ktd->table->keyContexts.table[*context];
if (ctx->name) {
if (wcslen(ctx->name) == length) {
if (wmemcmp(ctx->name, name, length) == 0) {
return 1;
}
}
{
KeyContext *ctx = getKeyContext(ktd, *context);
if (ctx) {
if (setKeyContextName(ctx, name, length)) {
return 1;
}
ktd->table->keyContexts.count -= 1;
}
}
static int
compareToName (const wchar_t *location1, int length1, const char *location2) {
const wchar_t *end1 = location1 + length1;
while (1) {
if (location1 == end1) return *location2? -1: 0;
if (!*location2) return 1;
{
wchar_t character1 = towlower(*location1);
char character2 = tolower((unsigned char)*location2);
if (character1 < character2) return -1;
if (character1 > character2) return 1;
}
location1 += 1;
location2 += 1;
}
}
static int
sortKeyNames (const void *element1, const void *element2) {
const KeyNameEntry *const *kne1 = element1;
const KeyNameEntry *const *kne2 = element2;
return strcasecmp((*kne1)->name, (*kne2)->name);
}
static int
searchKeyName (const void *target, const void *element) {
const KeyNameEntry *const *kne = element;
return compareToName(name->characters, name->length, (*kne)->name);
}
static int
sortKeyValues (const void *element1, const void *element2) {
const KeyNameEntry *const *kne1 = element1;
const KeyNameEntry *const *kne2 = element2;
{
int result = compareKeyValues(&(*kne1)->value, &(*kne2)->value);
if (result != 0) return result;
}
if (*kne1 < *kne2) return -1;
if (*kne1 > *kne2) return 1;
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
typedef struct {
unsigned int count;
} CountKeyNameData;
static int
countKeyName (const KeyNameEntry *kne, void *data) {
if (kne) {
CountKeyNameData *ckd = data;
ckd->count += 1;
}
return 1;
}
typedef struct {
const KeyNameEntry **kne;
} AddKeyNameData;
static int
addKeyName (const KeyNameEntry *kne, void *data) {
if (kne) {
AddKeyNameData *akd = data;
*akd->kne++ = kne;
}
return 1;
}
allocateKeyNameTable (KeyTableData *ktd, KEY_NAME_TABLES_REFERENCE keys) {
{
CountKeyNameData ckd = {
.count = 0
};
forEachKeyName(keys, countKeyName, &ckd);
ktd->table->keyNames.count = ckd.count;
if ((ktd->table->keyNames.table = malloc(ARRAY_SIZE(ktd->table->keyNames.table, ktd->table->keyNames.count)))) {
AddKeyNameData akd = {
.kne = ktd->table->keyNames.table
};
forEachKeyName(keys, addKeyName, &akd);
qsort(ktd->table->keyNames.table, ktd->table->keyNames.count, sizeof(*ktd->table->keyNames.table), sortKeyNames);
findKeyName (const wchar_t *characters, int length, KeyTableData *ktd) {
const DataOperand name = {
.characters = characters,
.length = length
};
return bsearch(&name, ktd->table->keyNames.table, ktd->table->keyNames.count, sizeof(*ktd->table->keyNames.table), searchKeyName);
}
static int
parseKeyName (DataFile *file, KeyValue *value, const wchar_t *characters, int length, KeyTableData *ktd) {
const wchar_t *suffix = wmemchr(characters, WC_C('.'), length);
int prefixLength;
int suffixLength;
if (suffix) {
if (!(prefixLength = suffix - characters)) {
reportDataError(file, "missing key group name: %.*" PRIws, length, characters);
return 0;
}
if (!(suffixLength = (characters + length) - ++suffix)) {
reportDataError(file, "missing key number: %.*" PRIws, length, characters);
return 0;
}
} else {
prefixLength = length;
suffixLength = 0;
}
{
const KeyNameEntry *const *kne = findKeyName(characters, prefixLength, ktd);
if (!kne) {
reportDataError(file, "unknown key name: %.*" PRIws, prefixLength, characters);
return 0;
}
if (suffix) {
int ok = 0;
int number;
if (isNumber(&number, suffix, suffixLength))
if (number > 0)
if (--number <= KTB_KEY_MAX)
ok = 1;
if (!ok) {
reportDataError(file, "invalid key number: %.*" PRIws, suffixLength, suffix);
return 0;
}
if (value->number != KTB_KEY_ANY) {
reportDataError(file, "not a key group: %.*" PRIws, prefixLength, characters);
}
static int
getKeyOperand (DataFile *file, KeyValue *value, KeyTableData *ktd) {
DataString name;
if (getDataString(file, &name, 1, "key name")) {
if (parseKeyName(file, value, name.characters, name.length, ktd)) {
return 1;
}
}
newModifierPosition (const KeyCombination *combination, const KeyValue *modifier, unsigned int *position) {
int found = findKeyValue(combination->modifierKeys, combination->modifierCount, modifier, position);
return found && (modifier->number != KTB_KEY_ANY);
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
}
static int
insertModifier (DataFile *file, KeyCombination *combination, unsigned int position, const KeyValue *value) {
if (combination->modifierCount == MAX_MODIFIERS_PER_COMBINATION) {
reportDataError(file, "too many modifier keys");
return 0;
}
{
int index = combination->modifierCount;
while (index--) {
if (index >= position) {
combination->modifierKeys[index+1] = combination->modifierKeys[index];
}
if (combination->modifierPositions[index] >= position) {
combination->modifierPositions[index] += 1;
}
}
}
combination->modifierKeys[position] = *value;
combination->modifierPositions[combination->modifierCount++] = position;
return 1;
}
static int
parseKeyCombination (DataFile *file, KeyCombination *combination, const wchar_t *characters, int length, KeyTableData *ktd) {
KeyValue value;
memset(combination, 0, sizeof(*combination));
combination->modifierCount = 0;
while (1) {
const wchar_t *end = wmemchr(characters, WC_C('+'), length);
if (!end) break;
{
int count = end - characters;
if (!count) {
reportDataError(file, "missing modifier key");
if (!parseKeyName(file, &value, characters, count, ktd)) return 0;
{
unsigned int position;
if (newModifierPosition(combination, &value, &position)) {
reportDataError(file, "duplicate modifier key: %.*" PRIws, count, characters);
return 0;
}
if (!insertModifier(file, combination, position, &value)) return 0;
if (value.number == KTB_KEY_ANY) combination->anyKeyCount += 1;
}
length -= count + 1;
characters = end + 1;
}
}
if (length) {
if (*characters == WC_C('!')) {
characters += 1, length -= 1;
combination->flags |= KCF_IMMEDIATE_KEY;
}
}
if (!parseKeyName(file, &value, characters, length, ktd)) return 0;
{
unsigned int position;
if (newModifierPosition(combination, &value, &position)) {
reportDataError(file, "duplicate key: %.*" PRIws, length, characters);
return 0;
}
if (combination->flags & KCF_IMMEDIATE_KEY) {
combination->immediateKey = value;
} else if (!insertModifier(file, combination, position, &value)) {
return 0;
}
if (value.number == KTB_KEY_ANY) combination->anyKeyCount += 1;
getKeysOperand (DataFile *file, KeyCombination *combination, KeyTableData *ktd) {
DataString names;
if (getDataString(file, &names, 1, "key combination")) {
if (parseKeyCombination(file, combination, names.characters, names.length, ktd)) return 1;
sortKeyboardFunctionNames (const void *element1, const void *element2) {
const KeyboardFunction *const *kbf1 = element1;
const KeyboardFunction *const *kbf2 = element2;
return strcasecmp((*kbf1)->name, (*kbf2)->name);
}
static int
searchKeyboardFunctionName (const void *target, const void *element) {
const KeyboardFunction *const *kbf = element;
return compareToName(name->characters, name->length, (*kbf)->name);
}
static int
parseKeyboardFunctionName (DataFile *file, const KeyboardFunction **keyboardFunction, const wchar_t *characters, int length, KeyTableData *ktd) {
static const KeyboardFunction **sortedKeyboardFunctions = NULL;
const KeyboardFunction **newTable = malloc(ARRAY_SIZE(newTable, keyboardFunctionCount));
const KeyboardFunction *source = keyboardFunctionTable;
const KeyboardFunction **target = newTable;
unsigned int count = keyboardFunctionCount;
do {
*target++ = source++;
} while (--count);
qsort(newTable, keyboardFunctionCount, sizeof(*newTable), sortKeyboardFunctionNames);
}
sortedKeyboardFunctions = newTable;
registerProgramMemory("sorted-keyboard-functions", &sortedKeyboardFunctions);
}
{
const DataOperand name = {
.characters = characters,
.length = length
};
const KeyboardFunction *const *kbf = bsearch(&name, sortedKeyboardFunctions, keyboardFunctionCount, sizeof(*sortedKeyboardFunctions), searchKeyboardFunctionName);
return 1;
}
}
reportDataError(file, "unknown keyboard function: %.*" PRIws, length, characters);
return 0;
getKeyboardFunctionOperand (DataFile *file, const KeyboardFunction **keyboardFunction, KeyTableData *ktd) {
DataOperand name;
if (getDataOperand(file, &name, "keyboard function name")) {
if (parseKeyboardFunctionName(file, keyboardFunction, name.characters, name.length, ktd)) return 1;
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
}
return 0;
}
static int
sortCommandNames (const void *element1, const void *element2) {
const CommandEntry *const *cmd1 = element1;
const CommandEntry *const *cmd2 = element2;
return strcasecmp((*cmd1)->name, (*cmd2)->name);
}
static int
searchCommandName (const void *target, const void *element) {
const DataOperand *name = target;
const CommandEntry *const *cmd = element;
return compareToName(name->characters, name->length, (*cmd)->name);
}
static int
allocateCommandTable (KeyTableData *ktd) {
{
const CommandEntry *command = commandTable;
ktd->commandCount = 0;
while (command->name) {
ktd->commandCount += 1;
command += 1;
}
}
if ((ktd->commandTable = malloc(ktd->commandCount * sizeof(*ktd->commandTable)))) {
{
const CommandEntry *command = commandTable;
const CommandEntry **address = ktd->commandTable;
while (command->name) *address++ = command++;
}
qsort(ktd->commandTable, ktd->commandCount, sizeof(*ktd->commandTable), sortCommandNames);
return 1;
}
return 0;
}
static int
applyCommandModifier (int *command, const CommandModifierEntry *modifiers, const DataOperand *name) {
const CommandModifierEntry *modifier = modifiers;
while (modifier->name) {
if (!(*command & modifier->bit)) {
if (compareToName(name->characters, name->length, modifier->name) == 0) {
*command |= modifier->bit;
return 1;
}
}
modifier += 1;
}
return 0;
}
static int
parseCommandOperand (DataFile *file, BoundCommand *cmd, const wchar_t *characters, int length, KeyTableData *ktd) {
const wchar_t *end = wmemchr(characters, WC_C('+'), length);
{
const DataOperand name = {
.characters = characters,
.length = end? end-characters: length
};
if (!name.length) {
reportDataError(file, "missing command name");
}
if (!(command = bsearch(&name, ktd->commandTable, ktd->commandCount, sizeof(*ktd->commandTable), searchCommandName))) {
reportDataError(file, "unknown command name: %.*" PRIws, name.length, name.characters);
cmd->value = (cmd->entry = *command)->code;
if ((modifier.length = (length -= (end - characters) + 1))) {
modifier.characters = characters = end + 1;
end = wmemchr(characters, WC_C('+'), length);
if (end) modifier.length = end - characters;
}
if (!modifier.length) {
reportDataError(file, "missing command modifier");
if ((*command)->isToggle && !(cmd->value & BRL_FLG_TOGGLE_MASK)) {
if (applyCommandModifier(&cmd->value, commandModifierTable_toggle, &modifier)) continue;
if (applyCommandModifier(&cmd->value, commandModifierTable_motion, &modifier)) continue;
if ((*command)->isRow) {
if (applyCommandModifier(&cmd->value, commandModifierTable_row, &modifier)) continue;
}
if ((*command)->isVertical) {
if (applyCommandModifier(&cmd->value, commandModifierTable_vertical, &modifier)) continue;
if (applyCommandModifier(&cmd->value, commandModifierTable_input, &modifier)) continue;
}
if ((*command)->isCharacter) {
if (applyCommandModifier(&cmd->value, commandModifierTable_character, &modifier)) continue;
if (!unicodeDone) {
if (modifier.length == 1) {
cmd->value |= BRL_ARG_SET(modifier.characters[0]);
if (applyCommandModifier(&cmd->value, commandModifierTable_braille, &modifier)) continue;
if (applyCommandModifier(&cmd->value, commandModifierTable_character, &modifier)) continue;
if (applyCommandModifier(&cmd->value, commandModifierTable_keyboard, &modifier)) continue;
if (!offsetDone) {
if ((*command)->code == BRL_CMD_BLK(CONTEXT)) {
unsigned char context;
if (findKeyContext(&context, modifier.characters, modifier.length, ktd)) {
KeyContext *ctx = getKeyContext(ktd, context);
if (ctx->isSpecial) {
reportDataError(file, "invalid target context: %"PRIws, ctx->name);
} else {
ctx->isReferenced = 1;
cmd->value += context - KTB_CTX_DEFAULT;
}
} else if (((*command)->isOffset || (*command)->isColumn)) {
int maximum = BRL_MSK_ARG - ((*command)->code & BRL_MSK_ARG);
int offset;
if (isNumber(&offset, modifier.characters, modifier.length)) {
if ((offset >= 0) && (offset <= maximum)) {
cmd->value += offset;
offsetDone = 1;
continue;
}
}
}
}
reportDataError(file, "unknown command modifier: %.*" PRIws, modifier.length, modifier.characters);
static int
getCommandsOperand (DataFile *file, BoundCommand **cmds, KeyTableData *ktd) {
DataString commands;
if (getDataString(file, &commands, 1, "command")) {
const wchar_t *characters = commands.characters;
unsigned int length = commands.length;
int first = 1;
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
while (1) {
int count;
BoundCommand *cmd = *cmds++;
if (!cmd) break;
if (first) {
first = 0;
} else if (length) {
characters += 1;
length -= 1;
}
{
const wchar_t *end = wmemchr(characters, WC_C(':'), length);
count = end? (end - characters): length;
}
if (!count) {
*cmd = ktd->nullBoundCommand;
} else if (!parseCommandOperand(file, cmd, characters, count, ktd)) {
return 0;
}
characters += count;
length -= count;
}
if (!length) return 1;
reportDataError(file, "too many commands: %.*" PRIws, length, characters);
return 0;
}
static int
getCommandOperand (DataFile *file, BoundCommand *cmd, KeyTableData *ktd) {
BoundCommand *cmds[] = {cmd, NULL};
return getCommandsOperand(file, cmds, ktd);
}
static int
addKeyBinding (KeyContext *ctx, const KeyBinding *binding) {
if (ctx->keyBindings.count == ctx->keyBindings.size) {
unsigned int newSize = ctx->keyBindings.size? ctx->keyBindings.size<<1: 0X10;
KeyBinding *newTable = realloc(ctx->keyBindings.table, ARRAY_SIZE(newTable, newSize));
ctx->keyBindings.table = newTable;
ctx->keyBindings.size = newSize;
ctx->keyBindings.table[ctx->keyBindings.count++] = *binding;
static DATA_OPERANDS_PROCESSOR(processBindOperands) {
KeyTableData *ktd = data;
KeyBinding binding;
memset(&binding, 0, sizeof(binding));
if (hideBindings(ktd)) binding.flags |= KBF_HIDDEN;
if (getKeysOperand(file, &binding.keyCombination, ktd)) {
BoundCommand *cmds[] = {
&binding.primaryCommand,
&binding.secondaryCommand,
NULL
};
KeyContext *ctx = getCurrentKeyContext(ktd);
if (ctx) {
if (addKeyBinding(ctx, &binding)) {
return 1;
}
}
return 0;
static DATA_OPERANDS_PROCESSOR(processContextOperands) {
if (getDataString(file, &name, 1, "context name")) {
if (findKeyContext(&ktd->context, name.characters, name.length, ktd)) {
KeyContext *ctx = getCurrentKeyContext(ktd);
if (ctx) {
DataOperand title;
if (ctx->title) {
if ((title.length != wcslen(ctx->title)) ||
(wmemcmp(title.characters, ctx->title, title.length) != 0)) {
reportDataError(file, "context title redefined");
}
} else if (!setKeyContextTitle(ctx, title.characters, title.length)) {
return 0;
}
}
}
}
}
return 1;
}
static DATA_OPERANDS_PROCESSOR(processHideOperands) {
KeyTableData *ktd = data;
DataString state;
if (getDataString(file, &state, 1, "hide state")) {
if (isKeyword(WS_C("on"), state.characters, state.length)) {
ktd->hideRequested = 1;
} else if (isKeyword(WS_C("off"), state.characters, state.length)) {
ktd->hideRequested = 0;
} else {
reportDataError(file, "unknown hide state: %.*" PRIws, state.length, state.characters);
}
}
return 1;
}
static int
addHotkey (const HotkeyEntry *hotkey, KeyTableData *ktd) {
KeyContext *ctx = getCurrentKeyContext(ktd);
if (ctx) {
unsigned int newCount = ctx->hotkeys.count + 1;
HotkeyEntry *newTable = realloc(ctx->hotkeys.table, ARRAY_SIZE(newTable, newCount));
if (newTable) {
ctx->hotkeys.table = newTable;
ctx->hotkeys.table[ctx->hotkeys.count++] = *hotkey;
return 1;
} else {
logMallocError();
}
}
return 0;
}
static DATA_OPERANDS_PROCESSOR(processHotkeyOperands) {
KeyTableData *ktd = data;
HotkeyEntry hotkey;
memset(&hotkey, 0, sizeof(hotkey));
if (hideBindings(ktd)) hotkey.flags |= HKF_HIDDEN;
if (getKeyOperand(file, &hotkey.keyValue, ktd)) {
if (getCommandOperand(file, &hotkey.pressCommand, ktd)) {
if (getCommandOperand(file, &hotkey.releaseCommand, ktd)) {
if (!addHotkey(&hotkey, ktd)) {
return 0;
}
}
}
}
static DATA_OPERANDS_PROCESSOR(processIgnoreOperands) {