0

Is there a way to replicate this: TUI dialog(screenshot not mine of course) on something Shell Script or something like C? This looks better than using dialog package.

3 Answers3

1

I don't think that you can get the same TUI as in your screenshot with dialog but here's an attempt at it; I define the colors with a DIALOGRC generated on the fly:

#!/bin/bash

# DIALOGRC overrides:
IFS='' read -r -d '' dialogrc <<'EOF'
use_shadow = OFF
use_colors = ON
screen_color = (GREEN,BLACK,ON)
dialog_color = screen_color
border_color = screen_color
border2_color = screen_color
inputbox_color = screen_color
button_active_color = screen_color
button_inactive_color = screen_color
button_key_active_color = screen_color
button_key_inactive_color = screen_color
button_label_active_color = screen_color
button_label_inactive_color = screen_color
EOF

input_passwd=$(
    3>&1 >/dev/tty \
    DIALOGRC=<(printf '%s' "$dialogrc") \
    dialog \
    --output-fd 3 \
    --no-lines \
    --insecure \
    --passwordbox $' Please enter the passphrase to\n protect your key' 8 60
) || exit 1

printf '\ninput_password=%q\n' "$input_passwd"

enter image description here

Please notice the redirection of dialog's stdout to /dev/tty and how I capture the result though a different file descriptor; the order in 3>&1 >/dev/tty is important for doing so.

Fravadona
  • 13,917
  • 1
  • 23
  • 35
1

Basically every programming language allows you to make TUI or CLI.

Reproducing what you want to achieve

Probably, the most dumb way to implement that is to use ANSI escape codes and UNICODE characters. You can draw a colored menu and move the cursor to override the output or to hide the useless stuff.
Example using bare bash: link to GitHub Gist - password_input_tui.sh

#!/bin/bash

max_pass_length=40
pass_length=30
i=0
password=
prompt=$'     \u2502 Passphrase: '

clear
echo -e "\e[32;40m\e[H"

echo -e "     \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510     "
echo -e "     \u2502 Please enter the passphrase to                       \u2502     "
echo -e "     \u2502 protect your new key                                 \u2502     "
echo -e "     \u2502                                                      \u2502     "
echo -e -n "     \u2502 Passphrase: "; while [ $i -lt $pass_length ]; do echo -e -n "_"; i=$(expr $i + 1); done; while [ $i -lt $max_pass_length ]; do echo -e -n " "; i=$(expr $i + 1); done; echo -e " \u2502     "
echo -e "     \u2502                                                      \u2502     "
echo -e "     \u2502       <OK>                              <Cancel>     \u2502     "
echo -e "     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518     "


echo -e "\e[5;H"

# Read password

i=0
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $i -eq $(expr $pass_length - 1) ]]
    then
        password+=$'\0'
        break
    fi
    if [[ $char == $'\0' ]]
    then
        break
    fi
    if [[ $char == $'\177' || $char == $'\b' ]]
    then
        if [ $i -gt 0 ]
        then
            i=$(expr $i - 1)
            prompt=$'\b_\b'
            password="${password%?}"
        else
            prompt=''
        fi
    else
        i=$(expr $i + 1)
        prompt='*'
        password+="$char"
    fi
done

# Select <OK> or <Cancel>

choice=1

echo -e -n "\e[8;14H\e[30;42m<OK>\e[0m\e[10H"
while read -r -s -n 1 ui; do
    case "$ui" in
    $'\x1b')    # Handle ESC sequence.
        # Flush read. We account for sequences for Fx keys as
        # well. 6 should suffice far more then enough.
        read -r -s -n 1 -t 0.1 char
        if [[ "$char" == "[" ]]; then
            read -r -s -n 1 -t 0.1 char
            case "$char" in
            #"A") printf "Up\n";;
            #"B") printf "Down\n";;
            "C") # right
                echo -e -n "\e[8;14H\e[32;40m<OK>\e[0m" 
                echo -e -n "\e[8;48H\e[30;42m<Cancel>\e[0m"
                echo -e -n "\e[10H"
                choice=0;;
            "D") # left
                echo -e -n "\e[8;14H\e[30;42m<OK>\e[0m" 
                echo -e -n "\e[8;48H\e[32;40m<Cancel>\e[0m"
                echo -e -n "\e[10H"
                choice=1;;
            esac
        fi
        # Flush "stdin" with 0.1  sec timeout.
        read -rsn5 -t 0.1
        ;;
    $'\0')
        break ;;     
    # Other one byte (char) cases. Here only quit.
    q)
        choice=1
        break;;
    esac
done

# Show result

if [[ choice -eq 1 ]]
then
    echo "Password: $password"
else
    echo "Operation canceled"
fi

exit 0

Output: bash.gif

References:

But that example has many problems and bits that could be improved (for example this code doesn't validate the password characters, and special characters like CTRL+stuff are considered as 2+ characters) and it's probably much harder than using tools made exactly for this task (e.g. dialog).

Another example (C++) using curses: Link to GitHub Gist - password_input_tui.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>

#define MAX_PASSWD_LENGTH 32
#define M1Y     7
#define M1X     8
#define M2Y     7
#define M2X     33

void menu(int choice);

int yBottom, xBottom;
int yTop, xTop;
WINDOW* win;

int main(int argc, char** argv)
{
    initscr();
    
    noecho();
    curs_set(0);
    
    
    int xMax, yMax, i, choice, exit;
    char password[MAX_PASSWD_LENGTH];
    char ch;
    
    getmaxyx(stdscr, yMax, xMax);
    yBottom = yMax / 1.5, xBottom = xMax / 1.5;
    yTop = yMax / 6, xTop = xMax / 6;
    win = newwin(yBottom, xBottom, yTop, xTop);
    
    
    box(win, 0, 0);
    
    mvwprintw(win, 2, 2, "Please enter the passphrase to");
    mvwprintw(win, 3, 2, "protect your new key");
    mvwprintw(win, 5, 2, "Passphrase: ");
    
    i=0;
    while (i < MAX_PASSWD_LENGTH) {
        ch = wgetch(win);
        
        if (ch == ' ' || ch == 27 || ch == 127) {
            continue;
        }
        else if (ch == '\b' || ch == 8) {
            if (i > 0) {
                mvwprintw(win, 5, 14 + i, "\b \b");
                --i;
            }
            else {
                continue;
            }
        }
        else if (ch == '\n' || ch == 10 || ch == '\r' || ch == '\t') {
            break;
        }
        else if (ch == 0 || ch == 224) {
            ch = wgetch(win);
            continue;
        }
        else {
            mvwprintw(win, 5, 14 + i, "*");
            password[i++] = ch;
        }
    }
    password[i] = '\0';
    
    wattron(win, A_STANDOUT);
    mvwprintw(win, M1Y, M1X, "<OK>");
    wattroff(win, A_STANDOUT);
    mvwprintw(win, M2Y, M2X, "<Cancel>");
    
    choice = 1;
    exit = 0;
    while(ch = wgetch(win))
    {
        switch(ch)
        {
            case '\033':
            {
                wgetch(win);
                switch(wgetch(win))
                {
                    case 'D':
                    {
                        choice = 1;
                        break;
                    }
                    case 'C':
                    {
                        choice = 2;
                        break;
                    }
                }
                break;
            }
            case '\n':
            {
                exit = 1;
                break;
            }
            default:
                break;
        }
        if(exit)
            break;
        menu(choice);
    }
    
    if(choice == 1)
        mvwprintw(win, 10, 10, "Password: %s", password);
    else
        mvwprintw(win, 10, 10, "Operation canceled");
    wgetch(win);
    
    endwin();
    
    return 0;
}

void menu(int choice)
{
    switch(choice)
    {
        case 1:
            wattron(win, A_STANDOUT);
            mvwprintw(win, M1Y, M1X, "<OK>");
            wattroff(win, A_STANDOUT);
            mvwprintw(win, M2Y, M2X, "<Cancel>");
            break;
        case 2:
            mvwprintw(win, M1Y, M1X, "<OK>");
            wattron(win, A_STANDOUT);
            mvwprintw(win, M2Y, M2X, "<Cancel>");
            wattroff(win, A_STANDOUT);
            break;
        default:
            wattron(win, A_STANDOUT);
            mvwprintw(win, M1Y, M1X, "<OK>");
            wattroff(win, A_STANDOUT);
            mvwprintw(win, M2Y, M2X, "<Cancel>");
            break;
    }
}

Output: cpp.gif

To make this work (as per dialog) obviously you have to make sure you have installed curses (Example for Debian: sudo apt-get install libncurses5-dev libncursesw5-dev, and use -lncurses flag when you compile it with g++).

Some C Code for Windows

I suggest you take a look at TurboVision, which is a TUI framework released for MS-DOS in 1990. There's a interesting modern port on GitHub, which is cross platform. Here's a bit of what you can do: preview.

Otherwise you could include windows.h header and use SetConsoleTextAttribute() to color the output:

#include <stdio.h>
#include <windows.h>

int main(int argc, char** argv)
{
    HANDLE hConsole;
    int k;

    hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    // you can loop k higher to see more color choices
    for (k = 1; k < 255; k++)
    {
        // pick the colorattribute k you want
        SetConsoleTextAttribute(hConsole, k);
        printf("%3d  %s\n", k, "I want to be nice today!");
    }

    return 0;
}

Output: example

Or simply handle the input stream with _getch() to mask the password with asterisks:

#include <stdio.h>
#define PASSWORD_LENGTH 32

int main(int argc, char** argv) {
    int i = 0;
    char password[PASSWORD_LENGTH + 1];
    int ch;
    
    printf("Enter Password (max length: %d): ", PASSWORD_LENGTH);

    while (i < PASSWORD_LENGTH) {
        ch = _getch();
        if (ch == ' ' || ch == 27) {
            continue;
        }
        else if (ch == '\b') {
            if (i > 0) {
                printf("\b \b");
                --i;
            }
            else {
                continue;
            }
        }
        else if (ch == '\r' || ch == '\t') {
            break;
        }
        else if (ch == 0 || ch == 224) {
            ch = _getch();
            continue;
        }
        else {
            password[i++] = ch;
            printf("*");
        }
    }
    password[i] = '\0';

    printf("\nPassword: %s", password);

    return 0;
}

Output: example

mikyll98
  • 1,195
  • 3
  • 8
  • 29
0

I had to implement a TUI database interface to build workflow for permission database. Given that TUI interface will not look as good as GUI, dialog provides very reasonable solution, and can be done in 1-2 hours.

For single password field, use password box as below. It produces output similar to your screenshot.

input_pw=$(dialog --strdo--insecure --passwordbox "Enter Password ..." 0 0 )

if [ "$input_pw" = "SeCrEt" ] ; then
   do-something
fi

Note that for better looking screen you will most likely want to use the "--form" option, which make it easier to "decorate" the screen, add instructions, control field layout, size of widgets, etc. It took me about 1 day of work to figure it out, and integrate it into my scripts (front end for permission database).

dash-o
  • 13,723
  • 1
  • 10
  • 37