#define RULE_OP_MANGLE_NOOP              ':'  // does nothing
#define RULE_OP_MANGLE_LREST             'l'  // lower case all chars
#define RULE_OP_MANGLE_UREST             'u'  // upper case all chars
#define RULE_OP_MANGLE_LREST_UFIRST      'c'  // lower case all chars, upper case 1st
#define RULE_OP_MANGLE_UREST_LFIRST      'C'  // upper case all chars, lower case 1st
#define RULE_OP_MANGLE_TREST             't'  // switch the case of each char
#define RULE_OP_MANGLE_SHIFT_CASE        'S'  // shift the case of each char, JtR-like
#define RULE_OP_MANGLE_TOGGLE_AT         'T'  // switch the case of each char on pos N
#define RULE_OP_MANGLE_TOGGLE_AT_SEP     '3'  // switch the case of the first letter after occurrence N of char X
#define RULE_OP_MANGLE_REVERSE           'r'  // reverse word
#define RULE_OP_MANGLE_DUPEWORD          'd'  // append word to itself
#define RULE_OP_MANGLE_DUPEWORD_TIMES    'p'  // append word to itself N times
#define RULE_OP_MANGLE_REFLECT           'f'  // reflect word (append reversed word)
#define RULE_OP_MANGLE_ROTATE_LEFT       '{'  // rotate the word left.  ex: hello -> elloh
#define RULE_OP_MANGLE_ROTATE_RIGHT      '}'  // rotate the word right. ex: hello -> ohell
#define RULE_OP_MANGLE_APPEND            '$'  // append char X
#define RULE_OP_MANGLE_PREPEND           '^'  // prepend char X
#define RULE_OP_MANGLE_DELETE_FIRST      '['  // delete first char of word
#define RULE_OP_MANGLE_DELETE_LAST       ']'  // delete last char of word
#define RULE_OP_MANGLE_DELETE_AT         'D'  // delete char of word at pos N
#define RULE_OP_MANGLE_EXTRACT           'x'  // extract X chars of word at pos N
#define RULE_OP_MANGLE_OMIT              'O'  // omit X chars of word at pos N
#define RULE_OP_MANGLE_INSERT            'i'  // insert char X at pos N
#define RULE_OP_MANGLE_INSERT_EVERY      'v'  // insert char X every N chars
#define RULE_OP_MANGLE_OVERSTRIKE        'o'  // overwrite with char X at pos N
#define RULE_OP_MANGLE_TRUNCATE_AT       '\'' // cut the word at pos N
#define RULE_OP_MANGLE_REPLACE           's'  // replace all chars X with char Y
#define RULE_OP_MANGLE_PURGECHAR         '@'  // purge all instances of char X
#define RULE_OP_MANGLE_DUPECHAR_FIRST    'z'  // prepend first char of word to itself N times. ex: hello -> hhhello
#define RULE_OP_MANGLE_DUPECHAR_LAST     'Z'  // append last char of word to itself N times.   ex: hello -> hellooo
#define RULE_OP_MANGLE_DUPECHAR_ALL      'q'  // duplicate all chars. ex: hello -> hheelllloo
#define RULE_OP_MANGLE_SWITCH_FIRST      'k'  // switches the first 2 chars. ex: hello -> ehllo
#define RULE_OP_MANGLE_SWITCH_LAST       'K'  // switches the last 2 chars.  ex: hello -> helol
#define RULE_OP_MANGLE_SWITCH_AT         '*'  // switches char at pos N with char at pos M. ex: hello -> holle
#define RULE_OP_MANGLE_CHR_SHIFTL        'L'  // bitwise shift left char at pos N.  ex: hello0 -> hello`
#define RULE_OP_MANGLE_CHR_SHIFTR        'R'  // bitwise shift right char at pos N. ex: hello` -> hello0
#define RULE_OP_MANGLE_CHR_INCR          '+'  // bytewise increase at pos N. ex: hello0 -> hello1
#define RULE_OP_MANGLE_CHR_DECR          '-'  // bytewise decreate at pos N. ex: hello1 -> hello0
#define RULE_OP_MANGLE_CHR_ADD           'B'  // add byte value of X at pos N, bytewise. ex: hello0 -> hello`
#define RULE_OP_MANGLE_REPLACE_NP1       '.'  // replaces char @ n with value at @ n plus 1
#define RULE_OP_MANGLE_REPLACE_NM1       ','  // replaces char @ n with value at @ n minus 1
#define RULE_OP_MANGLE_DUPEBLOCK_FIRST   'y'  // duplicates first N chars
#define RULE_OP_MANGLE_DUPEBLOCK_LAST    'Y'  // duplicates last N chars
#define RULE_OP_MANGLE_TITLE             'E'  // lowercase everything then upper case the first letter and every letter after a space
#define RULE_OP_MANGLE_TITLE_SEP         'e'  // lowercase everything then upper case the first letter and every letter after char X
#define RULE_OP_MANGLE_TO_HEX_LOWER      'h'  // convert the entire password to lowercase hex
#define RULE_OP_MANGLE_TO_HEX_UPPER      'H'  // convert the entire password to uppercase hex

/* With -j or -k only */

#define RULE_OP_MANGLE_EXTRACT_MEMORY    'X'  // insert substring delimited by N and M into current word at pos I
#define RULE_OP_MANGLE_APPEND_MEMORY     '4'  // insert the word saved by 'M' at the end of current word
#define RULE_OP_MANGLE_PREPEND_MEMORY    '6'  // insert the word saved by 'M' at the beginning of current word
#define RULE_OP_MEMORIZE_WORD            'M'  // memorize current word

#define RULE_OP_REJECT_LESS              '<'  // reject plains of length greater than N
#define RULE_OP_REJECT_GREATER           '>'  // reject plains of length less than N
#define RULE_OP_REJECT_EQUAL             '_'  // reject plains of length not equal to N
#define RULE_OP_REJECT_CONTAIN           '!'  // reject plains that contain char X
#define RULE_OP_REJECT_NOT_CONTAIN       '/'  // reject plains that do not contain char X
#define RULE_OP_REJECT_EQUAL_FIRST       '('  // reject plains that do not contain char X at first pos
#define RULE_OP_REJECT_EQUAL_LAST        ')'  // reject plains that do not contain char X at last pos
#define RULE_OP_REJECT_EQUAL_AT          '='  // reject plains that do not contain char X at pos N
#define RULE_OP_REJECT_CONTAINS          '%'  // reject plains that contain char X less than N times
#define RULE_OP_REJECT_MEMORY            'Q'  // reject plains that match the plain saved (see M), i.e. if unchanged
#define RULE_LAST_REJECTED_SAVED_POS     'p'  // pos of the char last found with '/' or '%'

/* using character classes */

#define RULE_OP_CLASS_BASED              '~'
#define RULE_OP_MANGLE_REPLACE_CLASS     0x01 // replace all instances of chars in class C with char Y, ~s?CY
#define RULE_OP_MANGLE_PURGECHAR_CLASS   0x02 // purge all instances of chars in class C, ~@?C
#define RULE_OP_MANGLE_TITLE_SEP_CLASS   0x03 // lowercase everything then upper case the first letter and every letter after chars in class C, ~e?C

/* using character classes, with -j or -k only */

#define RULE_OP_REJECT_CONTAIN_CLASS     0x04 // reject plains that contain chars in class C, ~!?C
#define RULE_OP_REJECT_NOT_CONTAIN_CLASS 0x05 // reject plains that do not contain chars in class C, ~/?C
#define RULE_OP_REJECT_EQUAL_FIRST_CLASS 0x06 // reject plains that do not contain chars in class C at first pos, ~(?C
#define RULE_OP_REJECT_EQUAL_LAST_CLASS  0x07 // reject plains that do not contain chars in class C at last pos, ~)?C
#define RULE_OP_REJECT_EQUAL_AT_CLASS    0x08 // reject plains that do not contain chars in class C at pos N, ~=N?C
#define RULE_OP_REJECT_CONTAINS_CLASS    0x09 // reject plains that contain chars in class C less than N times, ~%N?C

/* writing a byte that is not typeable, including any character outside ASCII */

Any rule operand that takes a character accepts \xHH in place of that character, so a rule can
emit a byte the keyboard cannot produce. Two hex digits, lower or upper case.

  $\x41          append 'A'
  ^\x41          prepend 'A'
  i2\x41         insert 'A' at position 2
  o0\x41         overwrite position 0 with 'A'
  s\x41\x42      replace every 'A' with 'B'

A character outside ASCII is more than one byte in UTF-8. It can be written one \xHH per byte, and
it can also be written as itself. These two rules are the same rule, and both append U+2116:

  $\xe2$\x84$\x96
  $<the character>

The five operations that emit bytes take a character written directly: '$', '^', 'i', 'v' and 'o'.
A character there compiles to one byte operation per byte, which is what the first form spells out
by hand, so nothing about the rule engine changes. It stays byte oriented, and the two forms cost
the same number of the 31 operations a rule may hold: a 3 byte character costs 3, and a rule that
would need more than 31 is rejected with a message naming it.

Bytes that are not a complete UTF-8 sequence keep the meaning they have today, one byte, so a rule
file holding raw bytes is unaffected.

Order matters and it is not the same for every operation. Append, insert and overwrite take the
bytes left to right. Prepend does not: '^' puts its byte at position 0, so each byte lands in
front of the one before it and the bytes have to be written back to front. Prepending U+00E9,
which is c3 a9, is:

  ^\xa9^\xc3

The rule engine works on bytes throughout, and this does not change that. Rules that match or
count rather than emit stay byte oriented, so '@', 's', 'e' and the reject rules test single
bytes and will happily split a multi-byte character. Position arguments such as those of 'D',
'x' and 'O' are byte positions, and the length used by '<' and '>' is a byte length, so a word
of 6 non-ASCII characters can be 12 bytes long as far as those rules are concerned.
