Terminal Control - Utah Valley University

Download Report

Transcript Terminal Control - Utah Valley University

operating
systems
Terminal Control
operating
systems
Terminal Control may seem like an arcane subject,
but …
It illustrates the relationship between
devices and files
The terminal driver is an easy device
driver to work with
Consider the following code:
#include <stdio.h>
int main ( )
{
int c, n = 0;
while( ( c = getchar( ) ) != ‘Q’ )
printf ( “char %3d is %c code %d\n”, n++, c, c);
return 0;
}
Run it ..... (listchars.c)
Some Observations
The program does not process any data until the Enter key is hit.
The enter key generates a Carriage Return character, 13
but the program receives the character code 10, New Line.
Some Observations
Moreover, when the program outputs a New Line character,
it outputs as a Carriage Return. New Line only move the cursor
down one line, it does not move the cursor to the left margin!
operating
systems
The Terminal Driver
Terminal
Driver
Application
The Terminal driver is doing some manipulation of the
data as it flows between the terminal and your program!
operating
systems
The Terminal Driver
Terminal
Driver
Application
attributes
These manipulations are controlled by terminal attributes
that can be changed by a process.
Terminal Attributes
To see the current set of terminal attributes
stty
stty -a
$ stty -a
speed 9600 baud; 24 rows; 80 columns;
...
convert cr to nl
iflags: -istrp icrnl -inlcr . . .
- the flag is turned off
input flags: what the driver does with characters
coming from the terminal
man stty to see more detail
You can alter setting with the stty command
stty -echo
This turns off echo mode
operating
systems
Terminal Modes
Canonical or cooked mode
The terminal driver stores incoming
characters in a buffer
Sends characters to the application a
line at a time
Handles basic editing functions
(delete, backspace, …)
operating
systems
The Terminal Driver
canonical mode
Terminal
Driver
Application
buffer
lines
characters
Process
Backspace, etc
operating
systems
Terminal Modes
Non-Canonical or cr-break mode
The terminal driver does not buffer
keyboard input
Sends characters to the application a
character at a time, as it is entered
Does some character processing
* Ctrl-C (interrupt)
* carriage return to newline
operating
systems
The Terminal Driver
non-canonical mode
Terminal
Driver
Application
characters
characters
Process
Ctrl-C, CR-NL
operating
systems
Terminal Modes
Raw mode
The terminal driver does not buffer
keyboard input.
Sends characters to the application a
character at a time
Does no character processing
operating
systems
The Terminal Driver
raw mode
Terminal
Driver
Application
The Terminal Driver
operating
systems
You control the Terminal driver by manipulating
A set of terminal attributes stored in the device driver.
Terminal
Driver
Application
driver attributes
tcgetattr( )
tcsetattr( )
operating
systems
Reading Attributes from the
Terminal Driver
#include <termios.h>
#include <unistd.h>
int result = tcgetattr(int fd, struct termios* attrs);
returns 0 if successful
-1 if an error
zero for stdin
address of a
termios struct
operating
systems
Setting Attributes in the
Terminal Driver
#include <termios.h>
#include <unistd.h>
int result = tcsetattr(int fd, int when, struct termios* attrs);
returns 0 if successful
-1 if an error
Note: Some shells will reset these
attributes in an effort to protect
you from yourself.
zero for stdin
when to
apply the
settings
address of a
termios struct
TCSANOW – immediately
TCSADRAIN – after draining queued data
TCSAFLUSH – after draining + flush input
operating
systems
The termios struct
struct termios
{
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t
c_cc[NCCS];
speed_t c_ispeed;
speed_t c_ospeed;
};
/* input mode flags */
/* output mode flags */
/* control mode flags */
/* local mode flags */
/* control characters */
/* input speed */
/* output speed */
operating
systems
flags
All of these flags have symbolic constants. To
see all of the fields in the termios structure see
termios man pages.
c_lflag
ECHO – enable echo
ICANON – canonical mode
ISIG – enable signals
operating
systems
Masking
operating
systems
Remember your boolean logic?
Bitwise Operators
&
|
~
a
b
1
1
0
0
1
0
1
0
a&b
1
0
0
0
and
or
complement
a
b
a|b
1
1
0
0
1
0
1
0
1
1
1
0
operating
systems
To Set a flag
flag = flag | mask
0’s except for the bits to be set in the flag
flag
mask
flag | mask
1
1
0
0
1
0
1
0
1
1
1
0
this bit got set
operating
systems
To Clear a flag
flag = flag & ~mask
1’s except for the bits to be
cleared in the flag
flag
mask
1
1
0
0
1
0
1
0
~ mask flag & ~mask
0
1
0
1
0
1
0
0
this bit got cleared
operating
systems
To Test a flag
if ( flag & mask )
if ( flag == flag & mask )
set the bits in the mask
that you want to test in
the flag
flag
1
1
0
0
mask flag & mask
1
0
1
0
1
0
0
0
Remember that in C,
0 is false and non-zero
is true.
Example
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
int main ( )
{
struct termios info;
int rv;
operating
systems
Examine Echo flag
file descriptor
is 0 for stdin
if ( (rv = tcgetattr( 0, &info ) ) == -1 )
{
perror(“tcgetattr error”);
exit ( 1 );
}
if ( info.c_lflag & ECHO)
printf(“ echo flag is set\n”);
else
printf(“ echo flag is not set\n”);
test the flag
}
return 0;
Run ... echostate.c
Example
operating
systems
set the flag
clear the flag
Change Echo flag
if ( (rv = tcgetattr( 0, &info ) ) == -1 )
{
perror(“tcgetattr error”);
exit ( 1 );
}
if ( argv[1][0] == ‘y’ )
info.c_lflag |= ECHO;
else
info.c_lflag &= ~ECHO;
if ( tcsetattr ( 0, TCSANOW, &info ) == -1)
{
perror(“tcsetattr error”);
exit ( 1 );
}
Run ... setecho.c
operating
systems
Writing a Terminal Program
Molay chapter 6
do some work
ask if user
wants to do
more
get
response
no
yes
operating
systems
Because the terminal is in canonical mode,
The user has the following problems:
1. The user has to press the enter key before
the program can act on the input.
2. The user can type something other than
‘y’ or ‘n’ and nothing happens.
do some work
operating
systems
turn off
canonical
mode
ask if user
wants to do
more
get response
turn on
canonical
mode
yes
response
?
no
operating
systems
This works great, but replying to every
illegal keystroke is annoying!
The solution: Turn off echo mode and
ignore illegal keystrokes.
operating
systems
What if this program was running in an
atm machine, and the person walked away
without typing ‘y’ or ‘n’?
Someone else could walk up and run a
transaction using this person’s account.
operating
systems
Blocking vs Non-blocking I/O
read
do something
The normal read operation waits
until the read operation is satisfied
before going on to the next instruction.
operating
systems
Blocking vs Non-blocking I/O
read
do something
It is possible to use fcntl or open to
change the properties of a file so that
when the file is read, the program
does not wait for the read to finish,
but goes on immediately to the next
instruction.
The read instruction returns a 0 if no
data has been read.
Using fcntl( )
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int fcntl(int filedes, int cmd, … /* int arg */);
F_DUPFD
F_GETFD
F_SETFD
F_GETFL
F_SETFL
...
- duplicate the file descriptor
- get the file descriptor flags
- set the file descriptor flags
- get the file status flags
- set the file status flags
File status flags
O_RDONLY
O_WRONLY
O_RDWR
open file for reading only
open file for writing only
open for reading and writing
O_APPEND
O_NONBLOCK
O_SYNC
O_ASYNC
append on each write
non-blocking mode
wait for writes to complete
asynchronous I/O (bsd and 4.3)
void set_nodelay_mode( )
{
int termflags;
}
termflags = fcntl(0, F_GETFL); get the file descriptor flags
set non-blocking flag
termflags |= NONBLOCK;
fcntl(0, F_SETFL, termflags); save the new flags
operating
systems
int get response(char* question, int maxtries)
{
int input;
print the question
printf(“%s (y/n)?”, question);
flush (stdout);
while (1)
{
sleep(SLEEPTIME);
input = tolower(get_ok_char( ) );
if ( input == ‘y’ ) return 1;
if ( input == ‘n’ ) return 0;
if ( maxtries -- == 0) return 2;
}
}
operating
systems
int get response(char* question, int maxtries)
{
int input;
printf(“%s (y/n)?”, question);
The o/s won’t flush the
flush (stdout);
buffer until it sees a newline or
while (1)
until the program tries to read.
{
sleep(SLEEPTIME);
and the read doesn’t
input = tolower(get_ok_char( ) );
happen until here.
if ( input == ‘y’ ) return 1;
if ( input == ‘n’ ) return 0;
if ( maxtries -- == 0) return 2; timeout
}
}
operating
systems
Drops out if EOF is returned.
Note that when read is nonblocking an EOF is returned
when there is no data.
char get_ok_char( )
{
int c:
while ( ( c = getchar( ) ) != EOF &&
strchr(“YyNn”, c) == NULL);
}
return c;
In this case, the EOF ( 0 ) gets returned.
operating
systems
The strchr( str1, c) function looks for the
character c in the string str1. It returns a
pointer to the character or NULL if the
character is not found.
char get_ok_char( )
{
int c:
while ( ( c = getchar( ) ) != EOF &&
strchr(“YyNn”, c) == NULL);
}
return c;
Or it drops out when one of
‘Y’, ‘y’, ‘N’, or ‘n’ is typed.
In this case, the typed
character is returned.
Your more command should work as follows:
Your program will display the first 23 lines of the file. On the last line of
the screen, it will then display the file name of the file being displayed,
and the percentage of the file which has been displayed.
It then will wait for user input. The user has three choices at this point:
If the user types a 'q', then your program exits.
If the user types a space, then your program displays the next
23 lines of the file.
If the user presses the ENTER key, then just the next line of
the file is displayed.
Note that invalid keystrokes are ignored and keystrokes are not echoed
back to the screen.
In the last two cases, your program should just display the percentage
of the file that has been displayed. It only displays the file name on the
first screen displayed. Don’t allow the file name and percentage to scroll
up the screen as new lines of data from the file are displayed.