/* more02b.c - version 0.2b of more * read and print 24 lines then pause for a few special commands * v02b: reads user control cmds from /dev/tty using read * and uses tcgetattr tcsetattr to change input processing * prob: if interrupted or killed, does not reset terminal * note: run this under dash to get the correct effect (bash is too smart) */ #include #include #include #include #include #include #include #include #define PAGELEN 23 /* leave one for the prompt */ #define ERROR 1 #define SUCCESS 0 #define has_more_data(x) (!feof(x)) #define CTL_DEV "/dev/tty" /* source of control commands */ int do_more(FILE *, int); int how_much_more(int); void print_one_line(FILE *); int setup_tty(int); int reset_tty(int); void ouch(int); void ouch(int s) { printf("ouch!\n"); exit(1); } int main( int ac , char *av[] ) { FILE *fp; /* stream to view with more */ int result = SUCCESS; /* return status from main */ int term_fd; /* fd for keyboard */ // signal(SIGINT, ouch); if ( (term_fd = open(CTL_DEV, O_RDONLY)) == -1 ){ perror(CTL_DEV); return ERROR; } if ( setup_tty(term_fd) == -1 ) /* set program mode */ return ERROR; if ( ac == 1 ) result = do_more( stdin, term_fd ); else while ( result == SUCCESS && --ac ) if ( (fp = fopen( *++av , "r" )) != NULL ) { result = do_more( fp, term_fd) ; fclose( fp ); } else result = ERROR; if ( reset_tty(term_fd) == -1 || close(term_fd) == -1 ) return ERROR; return result; } /* do_more -- show a page of text, then call how_much_more() for instructions * args: FILE * opened to text to display * rets: SUCCESS if ok, ERROR if not */ int do_more( FILE *fp , int term_fd ) { int space_left = PAGELEN ; /* space left on screen */ int reply; /* user request */ while ( has_more_data( fp ) ) { /* more input */ if ( space_left <= 0 ) { /* screen full? */ reply = how_much_more(term_fd); /* ask user */ if ( reply == 0 ) /* n: done */ break; space_left = reply; /* reset count */ } print_one_line( fp ); space_left--; /* count it */ } return SUCCESS; /* EOF => done */ } /* print_one_line(fp) -- copy data from input to stdout until \n or EOF */ void print_one_line( FILE *fp ) { int c; while( ( c = getc(fp) ) != EOF && c != '\n' ) putchar( c ) ; putchar('\n'); } /* how_much_more -- ask user how much more to show * args: none * rets: number of additional lines to show: 0 => all done * note: space => screenful, 'q' => quit, '\n' => one line */ int how_much_more(int term_fd) { char c; int nread; while( 1 ) { printf("\033[7m more? \033[m"); /* reverse on a vt100 */ fflush(stdout); nread = read(term_fd, &c, 1); printf("\r \r"); /* clear more? */ if ( nread != 1 ) /* error or EOF */ return 0; if ( c == 'q' ) /* q -> N */ return 0; if ( c == ' ' ) /* ' ' => next page */ return PAGELEN; /* how many to show */ if ( c == '\n' ) /* Enter key => 1 line */ return 1; } } /* * terminal mode control: setup_tty, reset_tty * uses a static copy of the original settings to reset to what they were */ static struct termios orig_settings; static int have_orig = 0; /* is orig_settings valid */ /* * setup_tty: get settings, change them for program mode, return fd or -1 */ int setup_tty(int fd) { struct termios settings; if ( tcgetattr(fd, &settings) == -1 ){ perror("cannot get terminal settings"); return -1; } orig_settings = settings; /* make copy */ have_orig = 1; settings.c_lflag &= ~ICANON; /* no buffering */ settings.c_lflag &= ~ECHO; /* no echoing please */ settings.c_cc[VMIN] = 1; /* read chars 1-by-1 */ if ( tcsetattr(fd, TCSANOW, &settings) == -1 ){ perror("cannot set terminal settings"); return -1; } return fd; } /* * reset_tty: restore original driver settings if stored, * rets: 0 for ok, -1 for error */ int reset_tty(int fd) { if ( have_orig && tcsetattr(fd, TCSANOW, &orig_settings) != -1 ) return 0; return -1; }