author | Olivier Brunel
<jjk@jjacky.com> 2023-04-15 07:59:33 UTC |
committer | Olivier Brunel
<jjk@jjacky.com> 2023-05-20 18:06:36 UTC |
parent | 6cff7e205e6591edaaae2ed7bc77c76fd908fbf4 |
src/doc/term.h.0.md | +26 | -0 |
src/doc/term.h/ask_password.3.md | +63 | -0 |
src/doc/term.h/term_echo.3.md | +41 | -0 |
src/liblimb/include/limb/term.h | +12 | -0 |
src/liblimb/term.h/ask_password.c | +46 | -0 |
src/liblimb/term.h/term_echo.c | +29 | -0 |
diff --git a/src/doc/term.h.0.md b/src/doc/term.h.0.md new file mode 100644 index 0000000..7e3e94e --- /dev/null +++ b/src/doc/term.h.0.md @@ -0,0 +1,26 @@ +% limb manual +% term.h(0) + +# NAME + +term.h - helpers for terminal interactions + + +# SYNOPSIS + + #include <limb/term.h> + + +# DESCRIPTION + +The header defines helper functions to interact with a terminal. + +## Functions + +The following functions are defined : + +: [ask_password](3) +:: To prompt user for a password and read it from *stdin*. + +: [term_echo](3) +:: To enable/disable echoing of input characters from a terminal. diff --git a/src/doc/term.h/ask_password.3.md b/src/doc/term.h/ask_password.3.md new file mode 100644 index 0000000..25beec0 --- /dev/null +++ b/src/doc/term.h/ask_password.3.md @@ -0,0 +1,63 @@ +% limb manual +% ask_password(3) + +# NAME + +ask\_password - prompt user for a password and read it from *stdin* + +# SYNOPSIS + + #include <limb/term.h> + +```pre hl +ssize_t ask_password(char *<em>dst</em>, size_t <em>dlen</em>, const char *<em>prompt</em>) +``` + +# DESCRIPTION + +The `ask_password`() function will read data from *stdin* up to a newline (`\n`) +and store it into the memory area pointed by `dst`, up to `dlen` bytes. + +First, it will ensure input character echoing is disabled for *stdin*, then drop +any data that might remain in `buffer_0`. If `prompt` is not NULL, it will be +written to *stdout* before attempting to read from *stdin*. + +All data read will be placed into `dst` (up to `dlen` bytes), reading stops as +soon as a newline is read. It will /not/ write said newline in `dst` but instead +NUL-terminate the string. +Though not checked, it is assumed that no other NUL byte will be present. + +Once done, character echoing is restored for *stdin*, and a newline printed on +*stdout* if `prompt` is not NULL. + +! INFO: +! The `prompt` value will be printed using the [out](3) family of functions, +! with an output level of *OLVL_NORMAL*, and as such written not only to +! `obuffer_1` but duplicated on any extra buffers as well. +! Refer to [outpout.h](0) for more. + +# RETURN VALUE + +On success the `ask_password`() function returns the length of the password read +and placed into `dst`. Note that said password is NUL-terminated when written to +`dst`, meaning that the password (and returned value) can only be up to +`dlen` - 1. + +Otherwise it returns -1 and sets `errno` to indicate the error. + +# ERRORS + +The `ask_password`() function may fail and set `errno` for any of the errors +described for [term_echo](3) and [buffer_getuptoc](3), with the following +exceptions : + +: *ENOTTY* +:: If [term_echo](3) fails with *ENOTTY* when disabling input character echoing, +:: `ask_password`() simply ignores it and attempts to proceed as normal. + +: *EPIPE* +:: If [buffer_getuptoc](3) fails with *EPIPE* it means end of file was reached, +:: in which case anything that was read is used/returned as password. + +These exceptions allow redirection of *stdin* which might only contain the +password itself. diff --git a/src/doc/term.h/term_echo.3.md b/src/doc/term.h/term_echo.3.md new file mode 100644 index 0000000..c49a8f2 --- /dev/null +++ b/src/doc/term.h/term_echo.3.md @@ -0,0 +1,41 @@ +% limb manual +% term_echo(3) + +# NAME + +term\_echo - enable/disable echoing of input characters from a terminal + +# SYNOPSIS + + #include <limb/term.h> + +```pre hl +int term_echo(int <em>fd</em>, int <em>want</em>) +``` + +# DESCRIPTION + +The `term_echo`() function will disable or enable echoing of input characters, +depending or whether `want` is zero or not respectively, for the terminal +referred by `fd`. + +It will first check whether or not the current state of echoing is as requested, +and if so returns 0 to indicate nothing had to be done. + + +# RETURN VALUE + +The `term_echo`() function returns 1 when echoing was successfully set according +to `want`. If the echoing state of the terminal referred by `fd` was already as +requested (and nothing was changed), it returns 0. + +Otherwise it returns -1 and sets `errno` to indicate the error. + +# ERRORS + +The `term_echo`() function may fail and set `errno` for any of the errors +described for [tcgetattr](3) and [tcsetattr](3). + +# SEE ALSO + +[ask_password](3) diff --git a/src/liblimb/include/limb/term.h b/src/liblimb/include/limb/term.h new file mode 100644 index 0000000..f948d93 --- /dev/null +++ b/src/liblimb/include/limb/term.h @@ -0,0 +1,12 @@ +/* This file is part of limb https://lila.oss/limb + * Copyright (C) 2023 Olivier Brunel jjk@jjacky.com */ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LIMB_TERM_H +#define LIMB_TERM_H + +#include <sys/types.h> /* {,s}size_t */ + +extern int term_echo(int fd, int want); +extern ssize_t ask_password(char *dst, size_t dlen, const char *prompt); + +#endif /* LIMB_TERM_H */ diff --git a/src/liblimb/term.h/ask_password.c b/src/liblimb/term.h/ask_password.c new file mode 100644 index 0000000..93d9287 --- /dev/null +++ b/src/liblimb/term.h/ask_password.c @@ -0,0 +1,46 @@ +/* This file is part of limb https://lila.oss/limb + * Copyright (C) 2023 Olivier Brunel jjk@jjacky.com */ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <errno.h> +#include <limb/buffer.h> +#include <limb/term.h> +#include <limb/output.h> + +ssize_t +ask_password(char *dst, size_t dlen, const char *prompt) +{ + int echo = term_echo(buffer_fd(buffer_0), 0); + if (echo < 0) { + if (errno != ENOTTY) return -1; + echo = 0; + } + /* drop any unread/buffered input */ + buffer_rseek(buffer_0, buffer_len(buffer_0)); + + if (prompt) add(prompt, PUTMSG_FLUSH); + + size_t plen = 0; + int r = buffer_getuptoc(buffer_0, dst, dlen, '\n', &plen); + /* EPIPE means reached EOF before finding a LF, in which case we'll just + * use whatever was read (if anything) as password. Might be useful e.g. if + * stdin is redirected and contains only the password. */ + if (!r && errno == EPIPE && plen < dlen) { + ++plen; + r = 1; + } + + /* if we put up a prompt, add a newline */ + if (prompt) out(NULL); + /* restore echo if we did indeed turn it off */ + if (echo) { + int e = errno; + term_echo(buffer_fd(buffer_0), 1); + errno = e; + } + + if (!r) return -1; + + /* we only return the (NUL-terminated) password itself */ + dst[plen - 1] = 0; + return plen - 1; +} diff --git a/src/liblimb/term.h/term_echo.c b/src/liblimb/term.h/term_echo.c new file mode 100644 index 0000000..1cf4016 --- /dev/null +++ b/src/liblimb/term.h/term_echo.c @@ -0,0 +1,29 @@ +/* This file is part of limb https://lila.oss/limb + * Copyright (C) 2023 Olivier Brunel jjk@jjacky.com */ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <errno.h> +#include <termios.h> +#include <limb/term.h> + +int +term_echo(int fd, int want) +{ + struct termios tio; + int r; + + r = tcgetattr(fd, &tio); + if (r < 0) return -1; + + if (want == !!(tio.c_lflag & ECHO)) + return 0; + + if (want) + tio.c_lflag |= ECHO; + else + tio.c_lflag &= ~ECHO; + + if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) + return -1; + + return 1; +}