1. more第一版
实现基础功能,显示每一页固定24行文本,“q Enter”退出, “Enter” 下一行, “space Enter”下一页。
/************************************************************************* > File Name: more01.c > Author: qianlv > Mail: [email protected] > Created Time: 2014年04月24日 星期四 14时58分25秒 > more01.c - version 0.1 of more > read and print 24 lines then pause for a few special commands ************************************************************************/ #include<stdio.h> #include <stdlib.h> #define PAGELEN 24 #define LINELEN 514 void do_more(FILE *); int see_more(); int main(int ac, char *av[]) { FILE *fp; if(ac==1) do_more(stdin);// more 后无参数,读取stdin else { while(--ac) if( (fp = fopen(* ++av, "r")) != NULL) // 打开文件 { printf("%s\n", *av); do_more(fp); fclose(fp); } else exit(1); } return 0; } void do_more(FILE *fp) { char line[LINELEN]; //int see_more(); int reply; int number_line = 0; while(fgets(line, LINELEN, fp) != NULL) { if(number_line == PAGELEN) { reply = see_more(); if(reply == 0) break; number_line -= reply; } if( fputs(line, stdout) == EOF) exit(1); number_line ++; } } int see_more() { int c; printf("\033[7m more? \033[m"); while( (c = getchar()) != EOF ) { if(c == ‘q‘) return 0; if(c == ‘ ‘) return PAGELEN; if(c == ‘\n‘) return 1; } return 0; }
2.more第二版
解决上一个版本“ls -l /etc | ./more01”, “ls -l /etc” 输出重定向为“./more01” 输入时 由于see_more() 函数中getchar()与do_more(FILE *fp)中读取都是stdin中的数据,时输出一页后不回暂停等待命令。
解决方法是: see_more()改为通过/dev/tty(键盘与显示设备的描述文件),读取键。
/************************************************************************* > File Name: more02.c > Author: qianlv > Mail: [email protected] > Created Time: 2014年04月24日 星期四 15时39分51秒 > more02.c - version 0.2 of more > read and print 24 lines the pause for a few special commands > feature of version 0.2: reads form /dev/tty for commands ************************************************************************/ #include<stdio.h> #include <stdlib.h> #define PAGELEN 24 #define LINELEN 514 void do_more(FILE *); int see_more(FILE *); int main(int ac, char *av[]) { FILE *fp; if(ac==1) do_more(stdin); else { while(--ac) if( (fp = fopen(* ++av, "r")) != NULL) { do_more(fp); fclose(fp); } else exit(1); } return 0; } void do_more(FILE *fp) { char line[LINELEN]; //int see_more(); int reply; int number_line = 0; FILE *fp_tty; fp_tty = fopen("/dev/tty", "r");//打开/dev/tty设备文件 if(fp_tty == NULL) exit(1); while(fgets(line, LINELEN, fp) != NULL) { if(number_line == PAGELEN) { reply = see_more(fp_tty); if(reply == 0) break; number_line -= reply; } if( fputs(line, stdout) == EOF) exit(1); number_line ++; } } int see_more(FILE *cmd) { int c; printf("\033[7m more? \033[m"); while( (c = getc(cmd)) != EOF ) //此处的getchar()从stdin读取数据,getc(cmd)从文件cmd(/dev/tty)中读入数据 { if(c == ‘q‘) return 0; if(c == ‘ ‘) return PAGELEN; if(c == ‘\n‘) return 1; } return 0; }
3. more第三版
通过修改终端属性,无需输入回车,立即响应输入字符命令
/************************************************************************* > File Name: more04.c > Author: qianlv > Mail: [email protected] > Created Time: 2014年04月25日 星期五 10时23分22秒 > 添加键入字符立即响应程序 ************************************************************************/ #include<stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #define PAGELEN 24 #define LINELEN 514 void do_more(FILE *); int see_more(FILE *); int main(int ac, char *av[]) { FILE *fp; if(ac==1) do_more(stdin); else { while(--ac) if( (fp = fopen(* ++av, "r")) != NULL) { do_more(fp); fclose(fp); } else exit(1); } return 0; } void do_more(FILE *fp) { char line[LINELEN]; int reply; int number_line = 0; FILE *fp_tty_in, *fp_tty_out; fp_tty_in = fopen("/dev/tty", "r"); fp_tty_out = fopen("/dev/tty", "w"); struct termios initial_settings, new_settings; tcgetattr(fileno(fp_tty_in), &initial_settings);//获取当前终端的属性。 new_settings = initial_settings; new_settings.c_lflag &= ~ICANON;//设置终端为非标准模式 //new_settings.c_lflag &= ~ECHO; //设置终端不回显 //设置读入一个字符,立即返回字符。 new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; if(tcsetattr(fileno(fp_tty_in), TCSANOW, &new_settings) != 0) { // 重新配置终端接口 fprintf(stderr, "could not set attributes\n"); } while(fgets(line, LINELEN, fp) != NULL) { if(number_line == PAGELEN) { reply = see_more(fp_tty_in); if(reply == 0) break; number_line -= reply; } if( fputs(line, fp_tty_out) == EOF) { tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings); // 恢复终端接口的配置 exit(1); } number_line ++; } tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings);// 恢复终端接口的配置 } int see_more(FILE *cmd) { int c; printf("\033[7m more? \033[m"); do { c = fgetc(cmd); if(c == ‘q‘) return 0; if(c == ‘ ‘) return PAGELEN; if(c == ‘\n‘) return 1; }while(1); return 0; }
4. more第四版
解决"more?"重复出现的问题,已经每页行数根据终端大小动态决定,由于每次读取一行文本不等于终端行数,所以存在bug。显示文件已经显示占总文件的百分比,显示多个文件时,分别显示出文件名。
/************************************************************************* > File Name: more04.c > Author: qianlv > Mail: [email protected] > Created Time: 2014年04月25日 星期五 14时31分07秒 > 解决"more?"重复出现的问题,已经每页行数根据终端大小动态决定,由于 > 文件的一行可能占2行以上终端行数,所有有小bug,显示出已经显示的文 > 件占文件总大小的百分比,如果显示的是多个文件,那么显示出当前读取 > 的文件的文件名。 ************************************************************************/ #include<stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <curses.h> #include <term.h> #include <string.h> #include <sys/stat.h> #define LINELEN 514 static unsigned long filesize = 0; // 文件的总字节数 static unsigned long input_filesize = 0; // 已经显示的字节数 static FILE *out_stream = (FILE *) 0; static int filesum = 0; // 当前要显示的文件个数 void clear_more(int, int, FILE *); //“el”删除一行到行尾,用于清除“more?”. int cols_more(FILE *fp); // 获取终端列数 int lines_more(FILE *); // 获取终端行数 int char_to_terminal(int ); // 一个与putchar函数有相同的参数和返回值的函数,用于tputs函数的调用。 void do_more(FILE *, char *filename); int see_more(FILE *,int, int); unsigned long get_filesize(FILE *); int main(int ac, char *av[]) { FILE *fp; filesum = ac - 1; if(ac==1) do_more(stdin,(char *)0); else { while(--ac) if( (fp = fopen(* ++av, "r")) != NULL) { filesize = input_filesize = 0; //清空前一个文件的大小。 filesize = get_filesize(fp); do_more(fp, *av); fclose(fp); } else exit(1); } return 0; } void do_more(FILE *fp, char *filename) { char line[LINELEN]; int reply; int number_line = 0; FILE *fp_tty_in, *fp_tty_out; fp_tty_in = fopen("/dev/tty", "r"); fp_tty_out = fopen("/dev/tty", "w"); struct termios initial_settings, new_settings; tcgetattr(fileno(fp_tty_in), &initial_settings);//获取当前终端的属性。 new_settings = initial_settings; new_settings.c_lflag &= ~ICANON;//设置终端为非标准模式 new_settings.c_lflag &= ~ECHO; //设置终端不回显 //设置读入一个字符,立即返回字符。 new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; if(tcsetattr(fileno(fp_tty_in), TCSANOW, &new_settings) != 0) { // 重新配置终端接口 fprintf(stderr, "could not set attributes\n"); } int Pagelen = lines_more(fp_tty_in) - 1; //终端行数 int PageCol = cols_more(fp_tty_in); //终端列数 int add_line; if(filesum > 1) //显示的文件个数 > 1 那么把文件名也显示出来。 { fprintf(fp_tty_out, "-------> %s <-------\n",filename); number_line = 1; } while(fgets(line, LINELEN, fp) != NULL) { if(number_line >= Pagelen) //输出的行数大于终端行数时,即为一页,原因是每次读取文件的一行,可能占用终端2行以上。 { reply = see_more(fp_tty_in,Pagelen, add_line); int prePage = Pagelen; Pagelen = lines_more(fp_tty_in) - 1; //终端行数 PageCol = cols_more(fp_tty_in); //终端列数 if(prePage < Pagelen) clear_more(Pagelen-1, 0, fp_tty_out); //移动游标至"more?"这一行最前面,然后清除此行。 else clear_more(Pagelen, 0, fp_tty_out); if(reply == 0) break; if(number_line != Pagelen && reply == 1) // 当终端变大时,且按下时回车“enter”,应把number_line改为终端倒数第二行。 number_line = Pagelen -1; else number_line -= reply; } if( fputs(line, fp_tty_out) == EOF) { tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings); // 恢复终端接口的配置 exit(1); } int line_len = strlen(line); input_filesize += (unsigned long)line_len;//叠加字节个数。 add_line = (line_len + PageCol - 1)/PageCol; number_line += add_line; //文档的行数不等于终端显示的行数,因为一行字符串可能占据2行以上。 //number_line ++; //fprintf(te, "%d: %d - %d %s",nll++,number_line, add_line,line); } tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings);// 恢复终端接口的配置 } void clear_more(int posx,int posy,FILE *fp) { char *el; char *cursor; out_stream = fp; cursor = tigetstr("cup"); el = tigetstr("el"); tputs(tparm(cursor, posx, posy), 1, char_to_terminal); //移动关闭至(posx,posy)处。 //////////////// sleep(1); tputs(el, 1, char_to_terminal);//清除此行至行尾。 } int see_more(FILE *cmd,int Pagelen, int add_line) { int c; if(filesize > 0 ) // 如果重定向的输入无法获取大小,则不要显示百分比。 printf("\033[7m more? \033[m %lu%%",input_filesize*100/filesize); else printf("\033[7m more? \033[m "); do { c = fgetc(cmd); if(c == ‘q‘) return 0; if(c == ‘ ‘) { return Pagelen; } if(c == ‘\n‘ || c == ‘\r‘) //非标准模式下,默认回车和换行之间的映射已不存在,所以检查回车符‘\r‘。 return add_line; }while(1); return 0; } int char_to_terminal(int char_to_write) { if(out_stream) putc(char_to_write,out_stream); return 0; } int lines_more(FILE *fp) { int nrows; setupterm(NULL, fileno(fp), (int *)0); nrows = tigetnum("lines"); return nrows; } int cols_more(FILE *fp) { int ncols; setupterm(NULL, fileno(fp), (int *)0); ncols = tigetnum("cols"); return ncols; } unsigned long get_filesize(FILE *fp) { struct stat buf; if(fstat(fileno(fp), &buf) < 0) return (unsigned long) 0; return (unsigned long) buf.st_size; }
5. 参考书籍
- 《Linux 程序设计 第四版》 by Neil Matthew,Richard Stones 人民邮电出版社。
- Unix/Linux 编程实践教程 by Bruce Molay 清华大学出版社。
设计模式之6大原则(6)开闭原则,码迷,mamicode.com
时间: 2024-10-14 05:44:22