Welcome to little lamb

Code » limb » commit 6ac0611

Add term.h: term_echo() & ask_password()

author Olivier Brunel
2023-04-15 07:59:33 UTC
committer Olivier Brunel
2023-05-20 18:06:36 UTC
parent 6cff7e205e6591edaaae2ed7bc77c76fd908fbf4

Add term.h: term_echo() & ask_password()

Some terminal helpers functions:
- term_echo() to turn on/off input echoing on a tty;
- ask_password() to prompt for a password and read it (up to LF) from
  stdin, handling the echoing bit.

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;
+}