Patricio Poblete (ppoblete@dcc.uchile.cl) |
El terminal posee características que hacen necesario darle un tratamiento especial, con un control más específico que lo que provee el modelo general de entrada/salida visto hasta ahora.
Antiguamente, esto se hacía con la función ioctl, pero esto ha sido reemplazado en POSIX.
En Unix, un terminal equivale a lo que sería una puerta asíncrona serial en un computador real.
TERMINAL --- MODEM --- SIST. TELEFÓNICO --- MODEM --- HOST
La transmisión en Unix es full duplex. Esto significa que tanto el terminal como el host pueden transmitir simultáneamente. A menudo, esto también significa que por cada caracter que el host recibe, lo envía de inmediato de vuelta hacia el terminal como eco.
Esto último no es obligatorio. En lugar de eso:
Se hace mediante una estructura de tipo
struct termios
Las operaciones permitidas son:
Uso de tcgetattr:
#include <termios.h> #include <unistd.h> status=tcgetattr(fd, p); /* status==0 ==> OK, -1 ==> error fd es un file descriptor abierto, debe ser un tty p es un puntero a un struct termios */
¿Cómo saber si un fd es un tty?
#include <unistd.h> s=ttyname(fd); /* retorna un puntero al nombre del terminal, o bien NULL si no es un tty */
Uso de tcsetattr:
status=tcsetattr(fd, option, p); /* option puede ser TCSANOW: el cambio es inmediato TCSADRAIN: cuando se termine de transmitir el output ya escrito TCSAFLUSH: igual a DRAIN + ignorar input pendiente */
Estructura termios:
Ejemplo: Lectura de password.
password.c |
#include <termios.h> #include <stdio.h> #include<sys/types.h> #include <unistd.h> int getpw(char *buf, unsigned size) /* retorna número de caracteres leídos, o -1 si hubo error */ { struct termios attr; int n; printf("Password: "); fflush(stdout); /* apagar eco */ if(tcgetattr(STDIN_FILENO, &attr)!=0) return -1; attr.c_lflag &= ~ECHO; if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr)!=0) return -1; n=read(STDIN_FILENO, buf, size); buf[n-1]='\0'; /* encender eco */ attr.c_lflag |= ECHO; if(tcsetattr(STDIN_FILENO, TCSANOW, &attr)!=0) return -1; return n; } #define MAXPW 100 main() { char pw[MAXPW+1]; sleep(5); if(getpw(pw, MAXPW)>=0) printf("\n\tpassword=%s\n", pw); }
IGNPAR | Ignora bytes con error de paridad |
INPCK | Activa chequeo de paridad |
ISTRIP | Borra bit de paridad |
INLCR | NL==>CR |
IGNCR | Ignora CR |
ICRNL | CR==>NL |
IXON | El terminal controla el flujo con XON/XOFF (^S/^Q) |
IXOFF | El computador controla el flujo |
CLOCAL | Ignorar líneas de status del modem |
CREAD | Habilitar recepción |
CSIZE | CS5, ..., CS8 (bits/byte) |
CSTOPB | 0==>enviar 1 stop bit, 1==>enviar 2 stop bits (raro) |
HUPCL | Colgar la línea al logout o cuando todos los procesos mueren |
PARENB | Activar paridad (generación y chequeo) |
PARODD | 0==>even, 1==>odd (sólo si PARENB está activo) |
ECHO | Hacer eco |
ECHOE | ERASE borra el último carácter |
ECHOK | KILL (^U) borra la última línea |
ECHONL | Hacer echo de \n incluso si estamos sin eco |
ICANON | Activa procesamiento de ERASE y KILL |
ISIG | Genera signals con caracteres INTR, QUIT, SUSP |
TOSTOP | Envía señal SIGTTOU al proceso que intenta escribir en este terminal y no está en el foreground |
char c_cc[NCCS];
Si ICANON está activo:
Subíndice | |
VEOF | EOF char (^D) |
VEOL | End of line (^J) |
VERASE | ERASE (^H o DEL) |
VINTR | Interrupt (^C o DEL, genera SIGINT) |
VKILL | Kill char (^U) |
VQUIT | Quit char (^\, genera core?) |
VSUSP | ^Z |
VSTART | ^Q |
VSTOP | ^S |
Si ICANON no está activo:
Subíndice | |
VINTR | Interrupt (^C o DEL, genera SIGINT) |
VMIN | VTIME==0 ==> número de caracteres a leer
VTIME!=0 ==> timeout después de VTIME/10 segundos |
VQUIT | Quit char (^\, genera core?) |
VTIME | VMIN==0 ==> el read se satisface con el primer caracter,
o bien timeout después de VTIME/10
VMIN!=0 ==> timeout VMIN==0 && VTIME==0 ==> read retorna lo que haya, sin esperar |
VSTART | ^Q |
VSTOP | ^S |
VSUSP | ^Z |
speed_t cfgetispeed(p) speed_t cfgetospeed(p) /* p es de tipo "struct termios *" */ status=cfsetispeed(p, speed); status=cfsetospeed(p, speed); /* speed= B50, B75, ..., B19200, B38400 */ /* speed==B0 ==> hangup */
tcsendbreak(fd, duracion); /* duracion==0 ==> 250-500 ms */ tcdrain(fd); /* Espera hasta que todos los datos escritos via write ya hayan salido */ /* Si se usa fprintf, hay que hacer fflush primero */ tcflush(fd, option); /* Descarta datos en entrada/salida */ /* option= TCIFLUSH, TCOFLUSH, TCIOFLUSH */