• C

_getch kbhit in linux

i'm trying to make a little software in linux, which ask for password.

i want that each single character that pressed to output '*'.

LVL 1
yahelbAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

sunnycoderCommented:
getpass()
0
sunnycoderCommented:
altrnatively, you can use curses library
noecho();

next in a loop, do  a
getch();
putc(); //put *
0
The Firewall Audit Checklist

Preparing for a firewall audit today is almost impossible.
AlgoSec, together with some of the largest global organizations and auditors, has created a checklist to follow when preparing for your firewall audit. Simplify risk mitigation while staying compliant all of the time!

yahelbAuthor Commented:
Hi  sunnycoder:
1. getpass block the echo output &&  qoute: "This function is obsolete. Do not use it.".
2. there is nothing good for me in that link _getch is not ansi function.

3. i'm trying to avoid the curses library, but if there is no ather solution...

        maybe i should ask that diffrently.

i want to react for each single character that have been pressed.
you should not think about that as a password attempts (i just thout this would help).

0
sunnycoderCommented:
Given your constraints I cannot think of anything but curses... the way I listed in my second post
0
Kent OlsenData Warehouse Architect / DBACommented:

Hi yahelb,

If you're going to capture and record input yourself, there's some other housekeeping that you should probably do:


cbreak();  /*  disable control break  */
noecho();  /*  disable character echo  */

while (1)  /*  Loop on input, exit when CR pressed, reset input and screen buffer when ESC pressed?  */
{
}

echo ();
nocbreak ();



Kent
0
g0rathCommented:
Here is a way to do this without using curses...

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/termios.h>


// Local prototypes
int suppress_echo( void );
int make_echo( void );
char *tty_path( void );
static struct termios default_stty, stty;

int main( void )
{
        char buf[128];
        int i=0;
        char ch;

        memset(&buf,0,128);

        suppress_echo();

        printf("Enter you password: ");
        while ((ch = getchar()) != '\n')
        {
                if (i > 127)
                        break;
                else
                        buf[i++] = ch;
        }

        fflush(stdin);
        fflush(stdout);

        make_echo();

        printf("\n\nbuf = %s\n", buf);
        return 0;
}

int suppress_echo( void )
{
        int fd;
        fd = open(tty_path(), O_RDWR);
        if (fd < 0)
                return -1;

        if (ioctl(fd, TCGETS, &default_stty) < 0)
                return -2;

        bcopy(&default_stty, &stty, sizeof(struct termios));

        stty.c_lflag &= ~(ECHO | CLOCAL);  // suppress echo

        if (ioctl(fd, TCSETS, &stty) < 0)
                return -3;

        close(fd);
        return 0;
}

int make_echo( void )
{
        int fd;

        fd = open(tty_path(), O_RDWR);
        if (fd < 0)
                return -1;

        // just return to our previous tty state
        if (ioctl(fd, TCSETS, &default_stty) < 0)
                return -2;

        close(fd);
        return 0;
}

This just toggles the echo bit for your current tty

char *tty_path( void )
{
        static char buf[128];
        memset(&buf,0,128);

        if (readlink("/proc/self/fd/0", buf,128) == -1)
                return NULL;

        return buf;
}
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
yahelbAuthor Commented:
explain:
this answers is not solve my problem i'm moving on to look how to do that the curses way.

so i gave points to you all.

g0rath was the closest to what i looking for, but the problem is that getchar only breaks on '\n'.


thanks/
0
Kent OlsenData Warehouse Architect / DBACommented:

You can break on any character that you want by changing these lines in main():

        while ((ch = getchar()) != '\n')
        {
                if (i > 127)
                        break;
                else
                        buf[i++] = ch;
        }

to:

while (1)
{
  ch == getchar();
  /*
      break on Carriage Return, Line Feed, ESC, or '*'.
      '*' is simply to demonstrate that you can break on any character(s) that you choose
  */
  if (ch == '\n' || ch == '\r' || ch == 27 || ch == '*' || ch > 127)
    break;
  buf[i++] = ch;
}


Kent
0
g0rathCommented:
the getchar() is setup so that you can use any char, I gave code as example, you can modify it to whatever you need...the real meat of the issue was turning off the echo at the tty...everything else is up to you
0
g0rathCommented:
well your line
ch == getchar();
should be
ch = getchar();

and your the last arg of your if()

ch > 127
should be
i > 127

there is only 128 bytes in that buffer, and with you logic you could have a buffer over run
it's > 127 since it's zero based counting.
0
Kent OlsenData Warehouse Architect / DBACommented:

Sorry g0rath,

I was trying to point out to the original poster that your solution could, in fact, break on any character the he chose.

It's certainly not the first time that my head and fingers were going in opposite directions.  ;)'


Kent
0
g0rathCommented:
oh no problem, it just appeard that the poster was just cut and pasting code rather then attempting to modify.... :)
0
yahelbAuthor Commented:
maybe i just dont undestand you.

here is a source code.

#include <stdio.h>

int main(){

        char ch;

        puts("enter somthing: ");
        ch = getchar();

        printf("char is: %c", ch);
        return 0;
}

now what i am saying is that getchar() wont return until the user will press <enter>.

Kent: i pasted your code. it acts really wierd infinit loop.
         did you tested it ?


g0rath: i took your code and replaced the char '\n' with 'a' and it just dont work for me.
           its not stops at 'a'.
           it always stops at '\n'.

did you tested it ?


if you 2 tested the code and it works for you what can cause it not to work here ?


0
g0rathCommented:
first all of this I/O is buffered and these functions work on NEWLINES, so it blocks until the char '\n' is sent, then the whole block is sent through this section, and it'll repeat until the buffer has been emptied.

You said this was for a password box, so on enter is usually what happens...if you want it to stop on the 'a' then you must go to unbuffered I/O...

So that getch never blocks and it aways goes throught he check.

If you want this, then the whole newline buffering doesn't work since the terminal is in raw mode rather then normal...then your at the mercy of the emulator your working with, etc....

basically at this point you're rewriting curses and should have been using it in the first place.

if you want to go down this route, you'll have to do this:

struct termios orig_term;
struct termios raw_term;

if (tcgetattr(0, &orig_term) != 0) return 1; //save terminal settings
if (tcsetattr(0, TCSANOW, &raw_term) != 0) return 1; //set to raw
fcntl(0, F_SETFL, O_NONBLOCK);

// do the old while loop
while (1)
{
        ch = getc(stdin);
        // in unbuffered mode 255 gets sent, and for sanity
        // 3 is equal to CTRL-C
        if (ch == 255 || ch == 3)
                continue;
        // maybe you'll recognize CR and LF, which my terminal
        // doesn't map correctly in raw mode
        if (ch == 'a' || ch == 13 || ch == 10)
                break;
        printf("(%c) (%d) (0x%x)\n", ch, ch, ch);
        if (!isalpha(ch))
                continue;
        if (i > 127)
                break;
        else
                buf[i++] = ch;
}

if (tcsetattr(0, TCSANOW, &orig_term) != 0) return 1; //restore

Now this will work as unbuffered I/O, but Enter no longer maps in my terminal, only CTRL-ENTER....

None of the answers here are 'C' Grade level since they are all far above the simple "Just use curses answer"
0
g0rathCommented:
all of my code works and was tested except the Raw Terminal I/O code
0
yahelbAuthor Commented:
1. thanks you g0raph and Kent.
2. i will use the curses way. i just thout ANSI have a function that do that(apperenly not).
3. g0raph: when i gave you 'C' grade i did that cause none of the answers was helpful for me (in fact but the curses way nothing was new! ).
which means: "Avarage".

now i do think you and kent should get more then 'C' grade or more points.
Can I do that ?

thanks again
yahel.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.