Welcome to little lamb

Code » limb » master » tree

[master] / src / liblimb / term.h / ask_password.c

/* 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;
}