/* 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)
{
if (!term_ensurefg(buffer_fd(buffer_0)) && errno != ENOTTY)
return -1;
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));
/* prompt: if prefixed with '>' then use stderr instead of stdout.
* Also a leading '\\' is skipped, to allow to start a prompt with a '>'
* still on stderr */
obuffer *b = obuffer_2;
if (prompt) {
if (*prompt == '>')
b = obuffer_1;
if (*prompt == '>' || *prompt == '\\')
++prompt;
obuffer_putmsg(b, OLVL_NORMAL, (const char *[]) { prompt, PUTMSG_FLUSH }, 2);
}
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) obuffer_putmsg(b, OLVL_NORMAL, (const char *[]) { "\n", PUTMSG_FLUSH }, 2);
/* 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;
}