Re: [Hla-stdlib-talk] non-buffered 'stdin.getc' function
Brought to you by:
evenbit
From: Frank K. <fbk...@ve...> - 2008-02-15 09:21:32
|
Nathan Baker wrote: ... > > w.ReadFile( hStdIn, &InputBuffer, 1, InputIndex, 0 ); > > Who "InputIndex"? I'm thinking we want an address here (? remember I > don't do Windows). Perhaps that's what you've got... > > I cribbed InputBuffer and InputIndex from the original getc() -- again, > I am guessing they are defined in "../include/stdinunit.hhf" -- probably > need to call stdin.read() { the original calls readln() but that won't > work here }. Could that be a problem? Or an opportunity? ... > "Leaking handles" is a pretty bad problem. Other than that, it looks > good to me! > > I don't think so. To my understanding, the 'standard I/O' handles are > created (by the OS) for each process when it starts, and are closed > automatically when it terminates. The 'GetStdHandle' doesn't actually > create a new handle... it just asks the OS for the handle that was > already created when the process started. I think closing it > prematurely might be a bad thing. I'll check the book to make sure. Yeah... or see what GetStdHandle(-10) returns on repeated calls. > What I've got for Linux does a similar thing... we "get" a whole > bleedin' structure for "current mode" - but only clear two bits, and > "set" to that. It is not strictly "right". If the pesky user suspends > the program with control-z, and restarts it with "fg", we're back > reading one key... but waiting for "Enter" again! :( A similar problem > may occur if the window size is changed (in an Xterm). We could fix > that > by blocking control-c/control-z entirely - handling it ourselves, > perhaps. Or we can "catch" these signals and do the appropriate > cleanup/restoration of our "tweak". (I'm not sure how to do that one). > > Could you post the code? Or is it already at Linux-nasm-users? Might be... in the form of "Beth's Game". Here's a "simplified" version - as-is, needs-work! And a C program (since you like C compilers these days) which does something a little more - kills more "processing" and checks for control-c. The way he does "check-for-key" doesn't impress me. Rather than make the handle non-blocking, we can use sys__newselect for that. There are several interesting points: size of termios is not what I'm using (!), different tweak for Ubuntu (!), and the different bits in termios that he tweaks and I leave alone... Best, Frank begin Nasmcode; global _start ; misc. equates %define NL 10 %define STDIN 0 %define STDOUT 1 ; sys_calls %define SYS_EXIT 1 %define SYS_READ 3 %define SYS_WRITE 4 %define SYS_IOCTL 54 section .text _start: nop .top: call getc cmp al, 'q' jz exit call putc call showalhex mov al, 10 call putc jmp short .top exit: mov ebx, eax mov eax, 1 int 80h ;------------------- ;------------------ showalhex: push eax mov ah, al shr al, 4 cmp al, 0Ah sbb al, 69h das call putc mov al, ah and al, 0Fh cmp al, 0Ah sbb al, 69h das call putc pop eax ret ;----------------------- ;--------------------------- putc: push edx push ecx push ebx push eax mov eax, 4 mov ebx, 1 mov ecx, esp mov edx, 1 int 80h pop eax pop ebx pop ecx pop edx ret ;----------------------------- ;----------------------------- ; ioctl subfunctions %define TCGETS 0x5401 ; tty-"magic" %define TCSETS 0x5402 ; flags for 'em %define ICANON 2 ;.Do erase and kill processing. %define ECHO 8 ;.Enable echo. struc termios alignb 4 .c_iflag: resd 1 ; input mode flags .c_oflag: resd 1 ; output mode flags .c_cflag: resd 1 ; control mode flags .c_lflag: resd 1 ; local mode flags .c_line: resb 1 ; line discipline .c_cc: resb 19 ; control characters endstruc ;--------------------------------- getc: push ebp mov ebp, esp sub esp, termios_size ; make a place for current kbd mode push edx push ecx push ebx mov eax, SYS_IOCTL ; get current mode mov ebx, STDIN mov ecx, TCGETS lea edx, [ebp - termios_size] int 80h ; monkey with it and dword [ebp - termios_size + termios.c_lflag], ~(ICANON | ECHO) mov eax, SYS_IOCTL mov ebx, STDIN mov ecx, TCSETS lea edx, [ebp - termios_size] int 80h xor eax, eax push eax ; this is the buffer to read into mov eax, SYS_READ mov ebx, STDIN mov ecx, esp ; character goes on the stack mov edx, 1 ; just one int 80h ; do it ; restore normal kbd mode or dword [ebp - termios_size + termios.c_lflag], ICANON | ECHO mov eax, SYS_IOCTL mov ebx, STDIN mov ecx, TCSETS lea edx, [ebp - termios_size] int 80h pop eax ; get character into al pop ebx ; restore caller's regs pop ecx pop edx mov esp, ebp ; leave pop ebp ret ;------------------------- end Nasmcode; begin Ccode; /* *************************************************************************** * * Copyright 1992-2005 by Pete Wilson All Rights Reserved * 50 Staples Street : Lowell Massachusetts 01851 : USA * <a href="http://www.pwilson.net/">http://www.pwilson.net/</a> pete at pwilson dot net +1 978-454-4547 * * This item is free software: you can redistribute it and/or modify it as * long as you preserve this copyright notice. Pete Wilson prepared this item * hoping it might be useful, but it has NO WARRANTY WHATEVER, not even any * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * *************************************************************************** */ /* *************************************************************************** * * KBHIT.C * * Based on the work of W. Richard Stevens in "Advanced Programming in * the Unix Environment," Addison-Wesley; and of Floyd Davidson. * * Contains these functions: * * To set the TTY mode: * tty_set_raw() Unix setup to read a character at a time. * tty_set_cooked() Unix setup to reverse tty_set_raw() * * To read keyboard input: * kb_getc() keyboard get character, NON-BLOCKING. If a char * has been typed, return it. Else return 0. * kb_getc_w() kb get char with wait: BLOCKING. Wait for a char * to be typed and return it. * * How to use: * tty_set_raw() set the TTY mode to read one char at a time. * kb_getc() read chars one by one. * tty_set_cooked() VERY IMPORTANT: restore cooked mode when done. * * Revision History: * * DATE DESCRIPTION * ----------- -------------------------------------------- * 12-jan-2002 new * 20-aug-2002 cleanup * 24-nov-2003 Fixed kb_getc() so that it really is non blocking(JH) * 10-sep-2006 Let kb_getc() work right under certain Unix/Linux flavors * *************************************************************************** */ #ifdef __cplusplus extern "C" { #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termios.h> #include <unistd.h> #include <errno.h> #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif extern int errno; static struct termios termattr, save_termattr; static int ttysavefd = -1; static enum { RESET, RAW, CBREAK } ttystate = RESET; /* *************************************************************************** * * set_tty_raw(), put the user's TTY in one-character-at-a-time mode. * returns 0 on success, -1 on failure. * *************************************************************************** */ int set_tty_raw(void) { int i; i = tcgetattr (STDIN_FILENO, &termattr); if (i < 0) { printf("tcgetattr() returned %d for fildes=%d\n",i,STDIN_FILENO); perror (""); return -1; } save_termattr = termattr; termattr.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); termattr.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); termattr.c_cflag &= ~(CSIZE | PARENB); termattr.c_cflag |= CS8; termattr.c_oflag &= ~(OPOST); termattr.c_cc[VMIN] = 1; /* or 0 for some Unices; <a href="#note1"> see note 1</a> */ termattr.c_cc[VTIME] = 0; i = tcsetattr (STDIN_FILENO, TCSANOW, &termattr); if (i < 0) { printf("tcsetattr() returned %d for fildes=%d\n",i,STDIN_FILENO); perror(""); return -1; } ttystate = RAW; ttysavefd = STDIN_FILENO; return 0; } /* *************************************************************************** * * set_tty_cbreak(), put the user's TTY in cbreak mode. * returns 0 on success, -1 on failure. * *************************************************************************** */ int set_tty_cbreak() { int i; i = tcgetattr (STDIN_FILENO, &termattr); if (i < 0) { printf("tcgetattr() returned %d for fildes=%d\n",i,STDIN_FILENO); perror (""); return -1; } save_termattr = termattr; termattr.c_lflag &= ~(ECHO | ICANON); termattr.c_cc[VMIN] = 1; termattr.c_cc[VTIME] = 0; i = tcsetattr (STDIN_FILENO, TCSANOW, &termattr); if (i < 0) { printf("tcsetattr() returned %d for fildes=%d\n",i,STDIN_FILENO); perror (""); return -1; } ttystate = CBREAK; ttysavefd = STDIN_FILENO; return 0; } /* *************************************************************************** * * set_tty_cooked(), restore normal TTY mode. Very important to call * the function before exiting else the TTY won't be too usable. * returns 0 on success, -1 on failure. * *************************************************************************** */ int set_tty_cooked() { int i; if (ttystate != CBREAK && ttystate != RAW) { return 0; } i = tcsetattr (STDIN_FILENO, TCSAFLUSH, &save_termattr); if (i < 0) { return -1; } ttystate = RESET; return 0; } /* *************************************************************************** * * kb_getc(), if there's a typed character waiting to be read, * return it; else return 0. * 10-sep-2006: kb_getc() fails (it hangs on the read() and never returns * until a char is typed) under some Unix/Linux versions: ubuntu, suse, and * maybe others. To make it work, please uncomment two source lines below. * *************************************************************************** */ unsigned char kb_getc(void) { int i; unsigned char ch; ssize_t size; /* termattr.c_cc[VMIN] = 0; */ /* uncomment if needed */ i = tcsetattr (STDIN_FILENO, TCSANOW, &termattr); size = read (STDIN_FILENO, &ch, 1); /* termattr.c_cc[VMIN] = 1; */ /* uncomment if needed */ i = tcsetattr (STDIN_FILENO, TCSANOW, &termattr); if (size == 0) { return 0; } else { return ch; } } /* *************************************************************************** * * kb_getc_w(), wait for a character to be typed and return it. * *************************************************************************** */ unsigned char kb_getc_w(void) { unsigned char ch; size_t size; while (1) { usleep(20000); /* 1/50th second: thanks, Floyd! */ size = read (STDIN_FILENO, &ch, 1); if (size > 0) { break; } } return ch; } #define TEST #ifdef TEST void echo(unsigned char ch); static enum { CH_ONLY, CH_HEX } how_echo = CH_ONLY; int main(int argc, char * argv[]) { unsigned char ch; printf("Test Unix single-character input.\n"); printf("termios size %d\n", sizeof(struct termios)); set_tty_raw(); /* set up character-at-a-time */ while (1) /* wait here for a typed char */ { usleep(20000); /* 1/50th second: thanks, Floyd! */ ch = kb_getc(); /* char typed by user? */ if (0x03 == ch) /* might be control-C */ { set_tty_cooked(); /* control-C, restore normal TTY mode */ return 1; /* and get out */ } echo(ch); /* not control-C, echo it */ } } void echo(unsigned char ch) { switch (how_echo) { case CH_HEX: printf("%c,0x%x ",ch,ch); break; default: case CH_ONLY: printf("%c", ch); break; } fflush(stdout); /* push it out */ } #endif /* test */ #ifdef __cplusplus } #endif end Ccode; :) |