GIDForums  

Go Back   GIDForums > Computer Programming Forums > C Programming Language
User Name
Password
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
 
Thread Tools Search this Thread Rate Thread
  #1  
Old 03-Jul-2007, 06:48
pisuke pisuke is offline
New Member
 
Join Date: May 2007
Posts: 19
pisuke is on a distinguished road

breaking an infinite loop through stdin


Hi there,

Some of the thinks I've thought many times is how to break an infinite loop by an instruction coming from stdin.

Let's suppose the following code:

CPP / C++ / C Code:
int main() {

	int value = 0;

	while (1) {
		printf("working...\n");
		sleep(1);

		scanf("%f", &value);

		if (value == 1) break;	

	} 

	return 0;
	
}		// end main

As all of you may know the loop gets stop at scanf. It would be nice if the loop could keep on iterating and at the same time listening for "1", if it arrives the loop is broken.

A couple of days ago (better late than never ;-) ) I got to know about fork(), and I started to think about forking to solve the previous problem.

I wanted to ask the experienced people which use to post here, if I should use fork or there's an easier, better approach. In case of forking, which one should be the parent process and which one the child. Without previous experience I would say that the parent process should listen for a signal and the child one execute the loop.

There's somewhere a good tutorial about that stuff, something as good as what Howard has posted about pointers...

Thanks!
  #2  
Old 03-Jul-2007, 07:43
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 5,310
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: breaking an infinite loop through stdin


Quote:
Originally Posted by pisuke
Hi there,

Some of the thinks I've thought many times is how to break an infinite loop by an instruction coming from stdin.

The fact is that there are no standard C library functions (C++ either) that do what you would like. Input from stdin on all implementations with I am familiar is buffered. That is, when the user types something on the keyboard, characters are put into a buffer (hidden from the program) nothing is offered to the program's scanf or getchar or any other standard library function until the user presses the 'Enter' key. Sorry.

Some compilers supply a function called kbhit() that is used for exactly what you have in mind. I believe taht Windows API functions to get chars one at a time also exist. Some compilers and operating systems allow low level manipulation of input device functions to set the keyboard into a mode that supplies characters one at a time as they are entered.

I know that it is sometimes very desirable to have the behavior that you desire, and, in fact, some applications might require it. However, there are no standard (portable) solutions. Getting to know about fork() and understanding its uses may be a Good Thing, but won't help in your particular problem.

Regards,

Dave

Footnote: The fork() function is also not a standard C library function, and not all operating systems/compilers will support it.
  #3  
Old 03-Jul-2007, 08:03
pisuke pisuke is offline
New Member
 
Join Date: May 2007
Posts: 19
pisuke is on a distinguished road

Re: breaking an infinite loop through stdin


I see...

And it is possible to catch a signal, like Ctrl+C and finish the program gracily?
  #4  
Old 03-Jul-2007, 08:09
jim mcnamara jim mcnamara is offline
Junior Member
 
Join Date: May 2007
Location: NM
Posts: 38
jim mcnamara will become famous soon enough

Re: breaking an infinite loop through stdin


If you are in Linux/UNIX (assumed because of fork() ) -

Look into termios, which allows you to set the tty driver to canonical/non-canonical mode.

canonical = read line after return key pressed,
non-canonical = read each keystroke

You can google for example code.

Also in UNIX, there usually is a character that is defined as EOF for stdin. The defacto standard is ctrl-D for eof. In your .profile (or .bashrc) or in /etc/profile there will be an entry like
CPP / C++ / C Code:
stty eof "^D"
which does this.

Regardless of which terminal mode you are in, calls to read stdin after a ctrl-D will return no characters ( ie, getchar returns EOF fgets returns NULL) and feof(stdin) returns non-zero once you have cleaned out the input buffer.

This is preferable to ctl-c (SIGINT) because you would have to establish a signal handler to exit gracefully and not lose data.
  #5  
Old 03-Jul-2007, 09:07
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 5,310
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: breaking an infinite loop through stdin


Quote:
Originally Posted by pisuke
I see...

And it is possible to catch a signal, like Ctrl+C and finish the program gracily?
Gracefully???

You haven't told us what compiler and operating system you are using. The point of my post is that various methods are available depending on what you have.


If you are using Microsoft or Borland compilers on Windows XP, then kbhit() and getch() functions usually found in <conio.h> may be the ticket.

If you have the equivalent of kbhit() and getch(), then all keystrokes can be intercepted so that Ctrl-c has no special effect---in other words your program handles it. Of course other desirable features such as command-line editing (backspace to delete previous character, handling of esc or tab or other control keys, for example) are also eliminated, so that your usual programmed input methods must necessarily be more complicated. Also, mixing "normal" getchar or scanf or fgets with the special functions may require some experimenting to get the desired effects. But that's always the case when you introduce non-standard I/O functions.

If you would tell us exactly what you are working with, perhaps someone can give specific help.

For example GNU/gcc on Linux? GNU/gcc/cygwin on Windows XP? I have examples of functions that are more-or-less equivalent to kbhit() and getch(). They require a little setup, but other than that, they fit into Borland or Microsoft compatible source code, and can give equivalent results. (At least I have some stuff that works for me.)

Regards,

Dave

Footnote: Since the vast majority of questions that I see here are from less-experienced programmers, I usually recommend that people get a thorough grounding in use of standard C library functions and a somewhat higher level of competence in using C programs in problem-solving before going off onto tangents regarding more exotic input/output functions.

The point being: get very familiar with things that are universal (or at least things that have greatest chance of portability) first. When fancy I/O becomes a requirement, you will probably abandon the command-line programs for some graphics interface that can actually be made attractive and can be a showcase for your artistic creativity. After all, no matter how you "dress up" your command-line program, it's still far below the standards that users have rightfully come to expect.

However, I do understand that there are places in command-line programs where functions like kbhit() and getch() are desirable.
  #6  
Old 03-Jul-2007, 09:20
pisuke pisuke is offline
New Member
 
Join Date: May 2007
Posts: 19
pisuke is on a distinguished road

Re: breaking an infinite loop through stdin


Hi guys,

Yes, you are right... sorry. I'm using linux (FC or SUSE).

Dave: could you post for us some of the examples you mention in your post?
  #7  
Old 03-Jul-2007, 09:36
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 5,310
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: breaking an infinite loop through stdin


Quote:
Originally Posted by pisuke
Dave: could you post for us some of the examples you mention in your post?

In case I forgot to mention it: IWFM---YMMV (It Works For Me---Your Mileage May Vary):

CPP / C++ / C Code:
/*
 * Demonstration of two functions that act more-or-less like
 * the traditional dos getch() and kbhit() functions.
 *
 * Have been tested with gcc 3.4.x and 4.x on various
 * Linux systems.
 *
 * Also tested on Windows XP/cygwin with gcc 3.4.x
 *
 * They work for me, but Your Mileage May Vary.
 *
 * davekw7x
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <termios.h>

#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif


struct termios tty_set_raw();
void tty_restore(struct termios oldstuff);
int linux_getch(void);
int linux_kbhit(void);


int main()
{
    unsigned ticknumber = 0;
    int kb_char;
    time_t now;
    struct termios save_attr;
    int i;

    printf(
     "\n\n  This is a test of two functions that (more-or-less) emulate the\n"
     "  classic DOS \"getch()\"  and \"kbhit()\" functions.\n\n"
     "  The keyboard routine is set so that ctlr-d is EOF.\n\n"
     "  Just to prove that it isn't really hanging while waiting for user \n"
     "  input, the program prints out a marker every second."
     "\n\n");

    now = time(0);

    i = 0;
    save_attr = tty_set_raw();

    while (1) {
        usleep(1);            /* keep from hogging cpu time  */
        if (time(0) != now) { /* show something every second */
            now = time(0);
            printf("Tick %u\n", ++ticknumber);
        }
        else if (linux_kbhit()) {
            kb_char = linux_getch();
            if (kb_char == EOF) {
                printf("EOF\n");
                break;
            }
            if (kb_char == 0x03) {
                printf("Ctrl-C breaks out of the loop\n");
                break;
            }
            if (isprint(kb_char)) {
                printf("'%c'", kb_char);
            }
            else {
                printf("   ");
            }
            printf(" (%02x hex)\n", kb_char);
        }
    }
    tty_restore(save_attr);
    return 0;
}

int linux_kbhit()
{
    struct timeval tv = {0,0}; /* set one microsecond timeout */
    fd_set rdfds;
    int retval;

    FD_ZERO(&rdfds);
    FD_SET(STDIN_FILENO, &rdfds);

    if (select(STDIN_FILENO + 1, &rdfds, NULL, NULL, &tv) == -1) {
        perror("select");
        exit(1);
    }
    if (FD_ISSET(STDIN_FILENO, &rdfds)) {
        retval = 1;
    }
    else {
        retval = 0;
    }
    return retval;
}

struct termios tty_set_raw()
{
    struct termios oldstuff;
    struct termios newstuff;

    tcgetattr(STDIN_FILENO, &oldstuff);/* save old attributes          */
    newstuff = oldstuff;
    /* 
     * Resetting these flags will set the terminal to raw mode.
     * Note that ctrl-c won't cause program exit, so there
     * is no emergency panic escape. (If your calling program
     * doesn't handle things properly, you will have to kill
     * the process externally.)
     */
    newstuff.c_lflag &= ~(ICANON | ECHO | IGNBRK);

    tcsetattr(STDIN_FILENO, TCSANOW, &newstuff);/* set new attributes  */
    return oldstuff;
}

void tty_restore(struct termios oldstuff)
{
    tcsetattr(STDIN_FILENO, TCSANOW, &oldstuff);/* restore old attributes */
}

#define END_FILE_CHARACTER 0x04 /* ctrl-d is unix-style eof input key */

/* 
 * Waits until a key has been pressed, then reads.
 * Since this is "raw" mode, EOF is a special case
 *
 * This can be used in the same program where the tty has been
 * set to "no-wait".
 *
 */
int linux_getch(void)
{
    char inch;
    int num_chars;

    do {
        num_chars = read(0, &inch, 1);
    } while (num_chars < 1);

    if (inch == END_FILE_CHARACTER) {
        inch = EOF;
    }

    return inch;
}

Compile "normally": gcc -Wall -W -pedantic test_kbhit_getch.c -o test_kbhit_getch


Break out of the loop with ctrl-c or ctrl-d.

Output (where I pressed 'a', then esc then c then ctrl-c)
Code:
This is a test of two functions that (more-or-less) emulate the classic DOS "getch()" and "kbhit()" functions. The keyboard routine is set so that ctlr-d is EOF. Just to prove that it isn't really hanging while waiting for user input, the program prints out a marker every second. Tick 1 'a' (61 hex) Tick 2 Tick 3 (1b hex) Tick 4 'c' (63 hex) Tick 5 Tick 6 Tick 7 Tick 8 Ctrl-C breaks out of the loop


Regards,

Dave

Footnote: The traditional dos getch() and kbhit() functions do not handle ctrl-c. That is, the program is terminated by the operating system "ungracefully," without allowing the program to handle it and proceed.

The functions that I show here allow graceful exit upon detecting ctrl-c. I don't know how to do that with Borland or Microsoft compilers on Windows XP. Then again, I hardly ever need these functions anyhow; it's just an academic exercise as a rebuttal to some who have claimed that certain things "can't be done on Windows," or, "can't be done on Linux," or whatever...
  #8  
Old 03-Jul-2007, 10:15
Howard_L Howard_L is offline
Regular Member
 
Join Date: Apr 2007
Location: Maryland/PA, USA
Posts: 846
Howard_L is a jewel in the roughHoward_L is a jewel in the roughHoward_L is a jewel in the rough

Re: breaking an infinite loop through stdin


Wow Dave! That looks interesting....
I don't have much to add except that I found myself wondering about this too a while back while exploring ncurses and win32 console programming. I don't remember if I ended up with a good answer or if it went on the 'another day' heap.... I can't pull anything out of there (the brain) right now....
Anyhow , as has been pointed out there are keyboard modes and, as I remember it (ha), you can set and unset these modes as the situation calls for and can assign ctrl-(x) and alt-(x) combinations to signal certain things etc.. Since your linux, I found good info on ncurses here:
http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/
win32 ??? msdn 'knowedge base'..... (yuk on dialup and '9 (you gotta be kidding me... smileys)
I think you might find some hints in the right direction. ncurses is nice and fairly easy to implement. It has getch() and just about everything else you could need. (those linux guys...)
But, as Dave said, this really gets into another world and you can really get bogged down with 'pretty presentation' issues that last forever.... I needed to build a better foundation first. (and still do!)
Howard;
  #9  
Old 03-Jul-2007, 10:43
davekw7x davekw7x is offline
Outstanding Member
 
Join Date: Feb 2004
Location: Left Coast, USA
Posts: 5,310
davekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to beholddavekw7x is a splendid one to behold

Re: breaking an infinite loop through stdin


Quote:
Originally Posted by Howard_L
ncurses is nice and fairly easy to implement. It has getch() and just about everything else you could need.

There have been a number of threads on gidforums with ncurses application information (use the "Search" pull-down thingie).

The ncurses library has getch(), but, as far as I know, it does not have kbhit(), which was the subject of the original question. (At least it is one answer to the original question.) However, I seem to recall that you can set no-delay mode on the tty input and then getch() returns ERR if no key has been pressed. This can result in an effective kbhit() function if people are really interested. (In the default mode on the tty input, getch() will wait for a key to be pressed.) Check the documentation.

Regards,

Dave
  #10  
Old 03-Jul-2007, 20:19
Howard_L Howard_L is offline
Regular Member
 
Join Date: Apr 2007
Location: Maryland/PA, USA
Posts: 846
Howard_L is a jewel in the roughHoward_L is a jewel in the roughHoward_L is a jewel in the rough

Re: breaking an infinite loop through stdin


I remebered something!
In ncurses I WAS forking a child process and then kill()ing it with keystroke or mouseclick in parent.
I think I was going to have a dot go back and forth until I pressed (x) which would kill() that child. I forget how successful I was with that though... somewhat , I believe.
Then I got a glimpse of using pthreads instead of fork() but didn't go into it much.
That seemed the favored route these days.
And that was about the extent of ncurses and I went over to learn some win32 console, but haven't done anything with child processes there. Don't know when I might get back to it. Lost in linked lists for the time being!
Howard;
 
 

Recent GIDBlogVista ?Widgets? on Windows XP by LocalTech

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Text-Based Roulette Game mfm1983 C++ Forum 5 29-Nov-2006 12:20
C++ Aparently on infinite loop, but where? Link C++ Forum 7 08-Sep-2006 08:40
Array 1 dimensional help please asap lion123 C Programming Language 10 18-Feb-2005 21:53
messy loop help please sammacs C Programming Language 6 26-Nov-2004 15:18
Nested for loop with function Tori C++ Forum 11 08-Nov-2004 13:02

Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The

All times are GMT -6. The time now is 13:15.


vBulletin, Copyright © 2000 - 2010, Jelsoft Enterprises Ltd.