/**
* libpassphrase – Personalisable library for TTY passphrase reading
*
* Copyright © 2013, 2014, 2015 Mattias Andrée (maandree@member.fsf.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PASSPHRASE_HELPER_H
#define PASSPHRASE_HELPER_H
/* Fix conflicting configurations */
#if defined(PASSPHRASE_TEXT) && defined(PASSPHRASE_STAR)
# warning You cannot have both PASSPHRASE_TEXT and PASSPHRASE_STAR
# undef PASSPHRASE_TEXT
#endif
/* Default texts */
#ifndef PASSPHRASE_STAR_CHAR
# define PASSPHRASE_STAR_CHAR "*" /* TODO document */
#endif
#ifndef PASSPHRASE_TEXT_EMPTY
# define PASSPHRASE_TEXT_EMPTY "(empty)" /* TODO document */
#endif
#ifndef PASSPHRASE_TEXT_NOT_EMPTY
# define PASSPHRASE_TEXT_NOT_EMPTY "(not empty)" /* TODO document */
#endif
/* Control keys. */
/**
* Home-key.
* Character sequences: \e[1~ \eOH
* Control-key combination: ^A
*/
#define KEY_HOME -1
/**
* Insert-key.
* Character sequences: \e[2~
*/
#define KEY_INSERT -2
/**
* Delete-key.
* Character sequences: \e[3~
* Control-key combination: ^D
*/
#define KEY_DELETE -3
/**
* End-key.
* Character sequences: \e[4~ \eOF
* Control-key combination: ^E
*/
#define KEY_END -4
/**
* Erase-key, also known as backspace.
* Character sequences: \d8 \d127
*/
#define KEY_ERASE -5
/**
* Right-key.
* Character sequences: \e[C
* Control-key combination: ^F
*/
#define KEY_RIGHT -6
/**
* Left-key.
* Character sequences: \e[D
* Control-key combination: ^B
*/
#define KEY_LEFT -7
/* Custom fflush and fprintf */
#if defined(PASSPHRASE_STAR) || defined(PASSPHRASE_TEXT)
# define xprintf(...) fprintf(stderr, __VA_ARGS__)
# define xflush() fflush(stderr)
#elif defined(PASSPHRASE_MOVE) && !defined(PASSPHRASE_ECHO)
# define xprintf(...) ({ /* do nothing */ })
# define xflush() ({ /* do nothing */ })
#elif defined(PASSPHRASE_MOVE)
# define xprintf(...) fprintf(stderr, __VA_ARGS__)
# define xflush() fflush(stderr)
#else
# define xflush() fflush(stderr)
#endif
/* Custom putchar */
#if defined(PASSPHRASE_STAR)
# define xputchar(C) (((C & 0xC0) != 0x80) ? fprintf(stderr, "%s", PASSPHRASE_STAR_CHAR) : 0)
#elif defined(PASSPHRASE_ECHO) && defined(PASSPHRASE_MOVE)
# define xputchar(C) fputc(C, stderr)
#else
# define xputchar(C) ({ /* be silent */ })
#endif
/* Is insert active by default? */
#if defined(PASSPHRASE_OVERRIDE) && defined(PASSPHRASE_INSERT)
# if defined(DEFAULT_INSERT)
# define DEFAULT_INSERT_VALUE 1
# else
# define DEFAULT_INSERT_VALUE 0
# endif
#endif
/* PASSPHRASE_INVALID's affect */
#if defined(PASSPHRASE_INVALID)
# define null_terminate() (*(rc + len) = 0)
#else
# define null_terminate() ({ /* do nothing*/ })
#endif
/* Implementation of the right-key's action */
#define move_right() \
({ \
xprintf("\033[C"); \
do \
point++; \
while ((len != point) && ((*(rc + point) & 0xC0) == 0x80)); \
})
/* Implementation of the left-key's action */
#define move_left() \
({ \
xprintf("\033[D"); \
point--; \
while (point && ((*(rc + point) & 0xC0) == 0x80)) \
point--; \
})
/* Implementation of the home-key's action */
#define move_home() \
({ \
size_t n = 0; \
for (i = 0; i < point; i++) \
if ((*(rc + i) & 0xC0) != 0x80) \
n++; \
xprintf("\033[%zuD", n); \
point = 0; \
})
/* Implementation of the end-key's action */
#define move_end() \
({ \
size_t n = 0; \
for (i = point; i < len; i++) \
if ((*(rc + i) & 0xC0) != 0x80) \
n++; \
xprintf("\033[%zuC", n); \
point = len; \
})
/* Implementation of the delete-key's action upon the passphrase buffer */
#define delete_next() \
({ \
null_terminate(); \
do \
{ \
for (i = point; i < len; i++) \
*(rc + i) = *(rc + i + 1); \
len--; \
} \
while ((len != point) && ((*(rc + point) & 0xC0) == 0x80)); \
})
/* Implementation of the erase-key's action upon the passphrase buffer */
#if defined(PASSPHRASE_MOVE)
#define erase_prev() \
({ \
char redo = 1; \
null_terminate(); \
while (redo) \
{ \
redo = (*(rc + point - 1) & 0xC0) == 0x80; \
for (i = point; i < len; i++) \
*(rc + i - 1) = *(rc + i); \
if (point <= len) \
*(rc + len - 1) = *(rc + len); \
point--; \
len--; \
} \
})
#else
# define erase_prev() (*(rc + --len) = 0)
#endif
#if defined(PASSPHRASE_MOVE)
# define append_char() (xputchar(c), *(rc + len++) = (char)c, point++)
#elif defined(PASSPHRASE_TEXT)
# define append_char() \
({ \
if (len == 0) \
{ \
xprintf("\e[K%s%zn", PASSPHRASE_TEXT_NOT_EMPTY, &printed_len); \
if (printed_len) \
xprintf("\e[%zuD", printed_len); \
} \
*(rc + len++) = (char)c; \
})
#else
# define append_char() (xputchar(c), *(rc + len++) = (char)c)
#endif
#define insert_char() \
({ \
if ((c & 0xC0) != 0x80) \
xprintf("\033[@"); \
xputchar(c); \
for (i = len; i > point; i--) \
*(rc + i) = *(rc + i - 1); \
len++; \
*(rc + point++) = (char)c; \
})
#define override_char() \
({ \
size_t n = 1; \
char cn = (char)c; \
while ((*(rc + point + n) & 0xC0) == 0x80) \
n++; \
for (i = point + n; i < len; i++) \
*(rc + i - n) = *(rc + i); \
passphrase_wipe(rc + len - n, n); \
len -= n; \
n = 0; \
while (cn & 0x80) \
{ \
cn = (char)(cn << 1); \
n++; \
} \
n = n ?: 1; \
if (len + n > size) \
{ \
if ((rc = xrealloc(rc, size, size << 1L)) == NULL) \
return NULL; \
size <<= 1L; \
} \
len += n; \
for (i = len - 1; i > point + n; i--) \
*(rc + i) = *(rc + i - n); \
if (len - 1 >= point + n) \
*(rc + point + n) = *(rc + point); \
for (i = 0; i < n; i++) \
{ \
if (i) \
c = getchar(); \
xputchar(c); \
*(rc + point++) = (char)c; \
} \
})
/* Implementation of the delete-key's action upon the display */
#if defined(PASSPHRASE_TEXT)
# define print_delete() \
(len == 0 ? 0 : (xprintf("\e[K%s%zn", PASSPHRASE_TEXT_EMPTY, &printed_len), \
(printed_len ? xprintf("\e[%zuD", printed_len) : 0)))
#else
# define print_delete() xprintf("\033[P")
#endif
/* Implementation of the erase-key's action upon the display */
#if defined(PASSPHRASE_TEXT)
# define print_erase() \
(len == 0 ? 0 : (xprintf("\e[K%s%zn", PASSPHRASE_TEXT_EMPTY, &printed_len), \
(printed_len ? xprintf("\e[%zuD", printed_len) : 0)))
#elif defined(PASSPHRASE_MOVE)
# define print_erase() xprintf("\033[D\033[P")
#elif defined(PASSPHRASE_STAR)
# define print_erase() xprintf("\033[D \033[D")
#endif
#endif