l i n u x - u s e r s - g r o u p - o f - d a v i s
Next Meeting:
February 3: Social gathering
Next Installfest:
Latest News:
Jan. 2: Happy new year! LUGOD turns 16!
Page last updated:
2003 Apr 18 20:15

The following is an archive of a post made to our 'vox-tech mailing list' by one of its subscribers.

Report this post as spam:

(Enter your email address)
Re: [vox-tech] a better scanf?? (C-programming question)
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [vox-tech] a better scanf?? (C-programming question)

--On Friday, April 18, 2003 04:48:50 -0700 Andy Campbell <atcampbell@ucdavis.edu> wrote:

  Non-blocking with select/poll/busy-looping, reading into a
buffer, followed by sscanf when a return key is found will certainly
handle the second case... examples if that's what you want.
  Otherwise you will have to go with some curses or raw mode input
to get character by character input from a user, and opens a whole
can of worms...

I am trying to implement this but am unsuccessfull. Could you show me
where  some examples are?? Thank you!!
When you're developing for unix, you have two different sets of functions for doing file I/O:

* The unix operating system provides a set of functions which use file descriptors: open(), close(), read(), write(), etc. These functions are generally provided by the operating system kernel.

* The C language standard defines a different set of functions that use file handles (not descriptors): fopen(), fclose(), printf(), etc. These are commonly called stdio or "standard I/O". These functions are normally implemented as library functions (i.e. code running outside of the kernel, just like the functions you write) which call the operating-system routines in order to perform actual I/O.

stdin, stdout, and stderr are file handles, not descriptors. They're strictly used with the stdio functions, not the OS functions. Further, they're generally already open when your program starts up so it isn't necessary to open() or fopen() them.

Now let's talk about ttys. If your program is interacting with a human than it's probably through a tty device. This is a unix OS construct that provides things like the backspace key and killing a program by typing ^C. If you want to do non-blocking reads from a tty you have to understand a bit about how ttys work.

ttys have three operating modes: line-at-a-time (or "cooked"), cbreak, or raw. In raw mode, every character typed by the user is immediately available to the program. cbreak mode is much like raw mode, but some characters receive special handling, e.g. ^C. In cooked mode, the tty driver buffers data typed by the user (and handles line-editing such as backspace, ^U, etc.) until the user hits return or ^D. When the user hits one of these, the entire line of input becomes available for the program to read.

You generally don't use stdio routines to do nonblocking input. The stdio library doesn't provide any standard means to see if data is waiting to be read without blocking. For example, getchar() always returns either a character or an EOF indication. There's no getchar return value that means "not EOF but not valid, either". Similarly, when you use fgets() to read a line, fgets will block until it reads an entire line. Even reading from a tty in cbreak mode, with characters dribbling in as the user types them, fgets() won't return control until it gets that newline character.

So if you want to do non-blocking reads you have to use the OS routines to do it, rather than the stdio routines. The os routine is called read(), but it also blocks until data becomes available. There are two ways around that:

1) Use select() or poll() to test the file descriptor first; perform the read() only if select/poll indicates you can do it without blocking.

2) Set the file descriptor to nonblocking at open() time or by using fcntl(). Then, if you call read() and no data is waiting, then read() will return -1 and errno will be set to EWOULDBLOCK.

Either way, every time you read in some data, you'll need to save it in a buffer somewhere until you've read a newline (or whatever marks the end of the input). Then you can call sscanf() to parse the buffer contents if you like.

Since you'll be using read() and select()/poll(), you'll need to know what file descriptor to use. You can do any of three things:

1) call fileno(stdin). fileno() returns the file descriptor associated with a file handle.

2) Just use 0. File descriptors are just numbers. 0 is standard input, 1, is standard output, and 2 is standard error.

3) Forget about using stdin. Call open("/dev/tty", O_RDWR) instead. This avoids the question of what to do if standard input isn't actually a tty device for some reason (e.g., because the user redirected input when he launched the program).

By now you're probably wishing there were an easier way. There are at least three alternatives to doing all of this:

1) Use curses/ncurses. Curses is a library for doing fancy operations on terminals, such as positioning the cursor or printing boldface text. Among its features, it includes a set of functions for doing nonblocking character input.

2) Use separate processes. Call fork() and let the child process perform the operations that shouldn't be interrupted while the parent process interacts with the user.

3) Use separate threads. A thread is your execution context, i.e. the part of the program being executed at any given time and its local variables. Under the classic program model that you're probably used to, a running program only has one thread, but it's possible to start additional threads. The benefit is that a single thread can perform an operation that blocks (for example, using stdio to read a line from the user) without preventing other threads from executing.

Threads have a reputation for being difficult to work with. It's true there's a bit of a learning curve, but personally I've never had much trouble with them. They're definitely the wave of the future; java contains sophisticated thread support, and most complex GUI-driven programs are multithreaded. If you aspire to do serious programming you'll probably end up learning them anyway; better to do it now before you develop a lot of bad programming habits.

Kenneth Herron Kherron@newsguy.com 916-366-7338
vox-tech mailing list

LUGOD Group on LinkedIn
Sign up for LUGOD event announcements
Your email address:
LUGOD Group on Facebook
'Like' LUGOD on Facebook:

Hosting provided by:
Sunset Systems
Sunset Systems offers preconfigured Linux systems, remote system administration and custom software development.

LUGOD: Linux Users' Group of Davis
PO Box 2082, Davis, CA 95617
Contact Us

LUGOD is a 501(c)7 non-profit organization
based in Davis, California
and serving the Sacramento area.
"Linux" is a trademark of Linus Torvalds.

Sponsored in part by:
Appahost Applications
For a significant contribution towards our projector, and a generous donation to allow us to continue meeting at the Davis Library.