Linux下more命令C语言实现实践

转载自:http://blog.csdn.net/qianlv_/article/details/24462595?utm_source=tuicool&utm_medium=referral



1. more第一版

实现基础功能,显示每一页固定24行文本,“q Enter”退出, “Enter” 下一行, “space Enter”下一页。
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /************************************************************************* 
  2.   > File Name: more01.c 
  3.   > Author: qianlv 
  4.   > Mail: qianlv7@qq.com  
  5.   > Created Time: 2014年04月24日 星期四 14时58分25秒 
  6.   > more01.c - version 0.1 of more 
  7.   > read and print 24 lines then pause for a few special commands 
  8.  ************************************************************************/  
  9.   
  10. #include<stdio.h>  
  11. #include <stdlib.h>  
  12. #define PAGELEN 24  
  13. #define LINELEN 514  
  14. void do_more(FILE *);  
  15. int see_more();  
  16. int main(int ac, char *av[])  
  17. {  
  18.     FILE *fp;  
  19.     if(ac==1)  
  20.       do_more(stdin);// more 后无参数,读取stdin  
  21.     else  
  22.     {  
  23.         while(--ac)  
  24.           if( (fp = fopen(* ++av, "r")) != NULL) // 打开文件   
  25.           {  
  26.               printf("%s\n", *av);  
  27.               do_more(fp);  
  28.               fclose(fp);  
  29.           }  
  30.           else  
  31.             exit(1);  
  32.     }  
  33.     return 0;  
  34. }  
  35.   
  36. void do_more(FILE *fp)  
  37. {  
  38.     char line[LINELEN];  
  39.     //int see_more();  
  40.     int reply;  
  41.     int number_line = 0;  
  42.     while(fgets(line, LINELEN, fp) != NULL)  
  43.     {  
  44.         if(number_line == PAGELEN)  
  45.         {  
  46.             reply = see_more();  
  47.             if(reply == 0)   
  48.               break;  
  49.             number_line -= reply;  
  50.         }  
  51.         if( fputs(line, stdout) == EOF)  
  52.           exit(1);  
  53.         number_line ++;  
  54.     }  
  55. }  
  56.   
  57. int see_more()  
  58. {  
  59.     int c;  
  60.     printf("\033[7m more? \033[m");  
  61.     while( (c = getchar()) != EOF )  
  62.     {  
  63.         if(c == 'q')  
  64.           return 0;  
  65.         if(c == ' ')  
  66.           return PAGELEN;  
  67.         if(c == '\n')  
  68.           return 1;  
  69.     }  
  70.     return 0;  
  71. }  

2.more第二版

解决上一个版本“ls -l /etc |  ./more01”, “ls -l /etc” 输出重定向为“./more01”  输入时 由于see_more() 函数中getchar()与do_more(FILE *fp)中读取都是stdin中的数据,时输出一页后不回暂停等待命令。
解决方法是: see_more()改为通过/dev/tty(键盘与显示设备的描述文件),读取键。
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /************************************************************************* 
  2.     > File Name: more02.c 
  3.     > Author: qianlv 
  4.     > Mail: qianlv7@qq.com  
  5.     > Created Time: 2014年04月24日 星期四 15时39分51秒 
  6.     > more02.c - version 0.2 of more 
  7.     > read and print 24 lines the pause for a few special commands 
  8.     > feature of version 0.2: reads form /dev/tty for commands 
  9.  ************************************************************************/  
  10.   
  11. #include<stdio.h>  
  12. #include <stdlib.h>  
  13. #define PAGELEN 24  
  14. #define LINELEN 514  
  15. void do_more(FILE *);  
  16. int see_more(FILE *);  
  17. int main(int ac, char *av[])  
  18. {  
  19.     FILE *fp;  
  20.     if(ac==1)  
  21.       do_more(stdin);  
  22.     else  
  23.     {  
  24.         while(--ac)  
  25.           if( (fp = fopen(* ++av, "r")) != NULL)  
  26.           {  
  27.               do_more(fp);  
  28.               fclose(fp);  
  29.           }  
  30.           else  
  31.             exit(1);  
  32.     }  
  33.     return 0;  
  34. }  
  35.   
  36. void do_more(FILE *fp)  
  37. {  
  38.     char line[LINELEN];  
  39.     //int see_more();  
  40.     int reply;  
  41.     int number_line = 0;  
  42.     FILE *fp_tty;  
  43.     fp_tty = fopen("/dev/tty""r");//打开/dev/tty设备文件  
  44.     if(fp_tty == NULL)  
  45.       exit(1);  
  46.     while(fgets(line, LINELEN, fp) != NULL)  
  47.     {  
  48.         if(number_line == PAGELEN)  
  49.         {  
  50.             reply = see_more(fp_tty);  
  51.             if(reply == 0)   
  52.               break;  
  53.             number_line -= reply;  
  54.         }  
  55.         if( fputs(line, stdout) == EOF)  
  56.           exit(1);  
  57.         number_line ++;  
  58.     }  
  59. }  
  60.   
  61. int see_more(FILE *cmd)  
  62. {  
  63.     int c;  
  64.     printf("\033[7m more? \033[m");  
  65.     while( (c = getc(cmd)) != EOF ) //此处的getchar()从stdin读取数据,getc(cmd)从文件cmd(/dev/tty)中读入数据  
  66.     {  
  67.         if(c == 'q')  
  68.           return 0;  
  69.         if(c == ' ')  
  70.           return PAGELEN;  
  71.         if(c == '\n')  
  72.           return 1;  
  73.     }  
  74.     return 0;  
  75. }  

3. more第三版

通过修改终端属性,无需输入回车,立即响应输入字符命令

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /************************************************************************* 
  2.     > File Name: more04.c 
  3.     > Author: qianlv 
  4.     > Mail: qianlv7@qq.com  
  5.     > Created Time: 2014年04月25日 星期五 10时23分22秒 
  6.         > 添加键入字符立即响应程序 
  7.  ************************************************************************/  
  8.   
  9. #include<stdio.h>  
  10. #include <stdlib.h>  
  11. #include <unistd.h>  
  12. #include <fcntl.h>  
  13. #include <termios.h>  
  14. #define PAGELEN 24  
  15. #define LINELEN 514  
  16. void do_more(FILE *);  
  17. int see_more(FILE *);  
  18. int main(int ac, char *av[])  
  19. {  
  20.     FILE *fp;  
  21.     if(ac==1)  
  22.       do_more(stdin);  
  23.     else  
  24.     {  
  25.         while(--ac)  
  26.           if( (fp = fopen(* ++av, "r")) != NULL)  
  27.           {  
  28.               do_more(fp);  
  29.               fclose(fp);  
  30.           }  
  31.           else  
  32.             exit(1);  
  33.     }  
  34.     return 0;  
  35. }  
  36.   
  37. void do_more(FILE *fp)  
  38. {  
  39.     char line[LINELEN];  
  40.     int reply;  
  41.     int number_line = 0;  
  42.     FILE *fp_tty_in, *fp_tty_out;  
  43.     fp_tty_in = fopen("/dev/tty""r");  
  44.     fp_tty_out = fopen("/dev/tty""w");  
  45.     struct termios initial_settings, new_settings;  
  46.     tcgetattr(fileno(fp_tty_in), &initial_settings);//获取当前终端的属性。  
  47.     new_settings = initial_settings;  
  48.     new_settings.c_lflag &= ~ICANON;//设置终端为非标准模式  
  49.     //new_settings.c_lflag &= ~ECHO; //设置终端不回显  
  50.     //设置读入一个字符,立即返回字符。  
  51.     new_settings.c_cc[VMIN] = 1;   
  52.     new_settings.c_cc[VTIME] = 0;  
  53.     if(tcsetattr(fileno(fp_tty_in), TCSANOW, &new_settings) != 0) { // 重新配置终端接口  
  54.         fprintf(stderr, "could not set attributes\n");  
  55.     }  
  56.     while(fgets(line, LINELEN, fp) != NULL)  
  57.     {  
  58.         if(number_line == PAGELEN)  
  59.         {  
  60.               
  61.             reply = see_more(fp_tty_in);  
  62.             if(reply == 0)   
  63.               break;  
  64.             number_line -= reply;  
  65.         }  
  66.         if( fputs(line, fp_tty_out) == EOF)  
  67.         {  
  68.             tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings); // 恢复终端接口的配置  
  69.              exit(1);  
  70.         }  
  71.         number_line ++;  
  72.     }  
  73.     tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings);// 恢复终端接口的配置  
  74. }  
  75. int see_more(FILE *cmd)  
  76. {  
  77.     int c;  
  78.     printf("\033[7m more? \033[m");  
  79.     do {  
  80.         c = fgetc(cmd);  
  81.         if(c == 'q')  
  82.           return 0;  
  83.         if(c == ' ')  
  84.           return PAGELEN;  
  85.         if(c == '\n')  
  86.           return 1;  
  87.     }while(1);  
  88.     return 0;  
  89. }  

4. more第四版

  解决"more?"重复出现的问题,已经每页行数根据终端大小动态决定,由于每次读取一行文本不等于终端行数,所以存在bug。显示文件已经显示占总文件的百分比,显示多个文件时,分别显示出文件名。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /************************************************************************* 
  2.   > File Name: more04.c 
  3.   > Author: qianlv 
  4.   > Mail: qianlv7@qq.com  
  5.   > Created Time: 2014年04月25日 星期五 14时31分07秒 
  6.   > 解决"more?"重复出现的问题,已经每页行数根据终端大小动态决定,由于 
  7.   > 文件的一行可能占2行以上终端行数,所有有小bug,显示出已经显示的文 
  8.   > 件占文件总大小的百分比,如果显示的是多个文件,那么显示出当前读取 
  9.   > 的文件的文件名。 
  10.  ************************************************************************/  
  11.   
  12.   
  13. #include<stdio.h>  
  14. #include <stdlib.h>  
  15. #include <unistd.h>  
  16. #include <fcntl.h>  
  17. #include <termios.h>  
  18. #include <curses.h>  
  19. #include <term.h>  
  20. #include <string.h>  
  21. #include <sys/stat.h>  
  22. #define LINELEN 514  
  23. static unsigned long filesize = 0; // 文件的总字节数  
  24. static unsigned long input_filesize = 0; // 已经显示的字节数  
  25. static FILE *out_stream = (FILE *) 0;  
  26. static int filesum = 0; // 当前要显示的文件个数  
  27. void clear_more(intintFILE *);   //“el”删除一行到行尾,用于清除“more?”.  
  28. int cols_more(FILE *fp);  // 获取终端列数  
  29. int lines_more(FILE *);  // 获取终端行数  
  30. int char_to_terminal(int ); // 一个与putchar函数有相同的参数和返回值的函数,用于tputs函数的调用。   
  31. void do_more(FILE *, char *filename);  
  32. int see_more(FILE *,intint);  
  33. unsigned long get_filesize(FILE *);  
  34. int main(int ac, char *av[])  
  35. {  
  36.     FILE *fp;  
  37.     filesum = ac - 1;  
  38.     if(ac==1)  
  39.       do_more(stdin,(char *)0);  
  40.     else  
  41.     {  
  42.         while(--ac)  
  43.           if( (fp = fopen(* ++av, "r")) != NULL)  
  44.           {  
  45.               filesize = input_filesize = 0; //清空前一个文件的大小。  
  46.               filesize = get_filesize(fp);  
  47.               do_more(fp, *av);  
  48.               fclose(fp);  
  49.           }  
  50.           else  
  51.             exit(1);  
  52.     }  
  53.     return 0;  
  54. }  
  55.   
  56. void do_more(FILE *fp, char *filename)  
  57. {  
  58.     char line[LINELEN];  
  59.     int reply;  
  60.     int number_line = 0;  
  61.     FILE *fp_tty_in, *fp_tty_out;  
  62.     fp_tty_in = fopen("/dev/tty""r");  
  63.     fp_tty_out = fopen("/dev/tty""w");  
  64.     struct termios initial_settings, new_settings;  
  65.     tcgetattr(fileno(fp_tty_in), &initial_settings);//获取当前终端的属性。  
  66.     new_settings = initial_settings;  
  67.     new_settings.c_lflag &= ~ICANON;//设置终端为非标准模式  
  68.     new_settings.c_lflag &= ~ECHO; //设置终端不回显  
  69.     //设置读入一个字符,立即返回字符。  
  70.     new_settings.c_cc[VMIN] = 1;   
  71.     new_settings.c_cc[VTIME] = 0;  
  72.     if(tcsetattr(fileno(fp_tty_in), TCSANOW, &new_settings) != 0) { // 重新配置终端接口  
  73.         fprintf(stderr, "could not set attributes\n");  
  74.     }  
  75.     int Pagelen = lines_more(fp_tty_in) - 1; //终端行数  
  76.     int PageCol = cols_more(fp_tty_in);  //终端列数  
  77.     int add_line;  
  78.     if(filesum > 1) //显示的文件个数 > 1 那么把文件名也显示出来。  
  79.     {  
  80.         fprintf(fp_tty_out, "-------> %s <-------\n",filename);  
  81.         number_line = 1;  
  82.     }  
  83.     while(fgets(line, LINELEN, fp) != NULL)  
  84.     {  
  85.         if(number_line >= Pagelen) //输出的行数大于终端行数时,即为一页,原因是每次读取文件的一行,可能占用终端2行以上。  
  86.         {  
  87.   
  88.             reply = see_more(fp_tty_in,Pagelen, add_line);  
  89.             int prePage = Pagelen;  
  90.             Pagelen = lines_more(fp_tty_in) - 1;  //终端行数  
  91.             PageCol = cols_more(fp_tty_in);   //终端列数  
  92.             if(prePage < Pagelen)  
  93.                 clear_more(Pagelen-1, 0, fp_tty_out);   //移动游标至"more?"这一行最前面,然后清除此行。  
  94.             else  
  95.                 clear_more(Pagelen, 0, fp_tty_out);  
  96.             if(reply == 0)   
  97.               break;  
  98.             if(number_line != Pagelen && reply == 1)  // 当终端变大时,且按下时回车“enter”,应把number_line改为终端倒数第二行。  
  99.                 number_line = Pagelen -1;  
  100.             else  
  101.                 number_line -= reply;  
  102.   
  103.         }  
  104.         if( fputs(line, fp_tty_out) == EOF)  
  105.         {  
  106.             tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings); // 恢复终端接口的配置  
  107.             exit(1);  
  108.         }  
  109.         int line_len = strlen(line);  
  110.         input_filesize += (unsigned long)line_len;//叠加字节个数。  
  111.         add_line = (line_len + PageCol - 1)/PageCol;  
  112.         number_line += add_line; //文档的行数不等于终端显示的行数,因为一行字符串可能占据2行以上。   
  113.         //number_line ++;  
  114.         //fprintf(te, "%d: %d - %d %s",nll++,number_line, add_line,line);  
  115.     }  
  116.     tcsetattr(fileno(fp_tty_in), TCSANOW, &initial_settings);// 恢复终端接口的配置  
  117. }  
  118. void clear_more(int posx,int posy,FILE *fp)  
  119. {  
  120.     char *el;  
  121.     char *cursor;  
  122.     out_stream = fp;  
  123.     cursor = tigetstr("cup");   
  124.     el = tigetstr("el");  
  125.     tputs(tparm(cursor, posx, posy), 1, char_to_terminal); //移动关闭至(posx,posy)处。  
  126.     sleep(1);  
  127.     tputs(el, 1,  char_to_terminal);//清除此行至行尾。  
  128.   
  129. }  
  130. int see_more(FILE *cmd,int Pagelen, int add_line)  
  131. {  
  132.     int c;  
  133.     if(filesize > 0 ) // 如果重定向的输入无法获取大小,则不要显示百分比。  
  134.         printf("\033[7m more? \033[m %lu%%",input_filesize*100/filesize);  
  135.     else  
  136.         printf("\033[7m more? \033[m ");  
  137.           
  138.     do {  
  139.         c = fgetc(cmd);  
  140.         if(c == 'q')  
  141.           return 0;  
  142.         if(c == ' ')  
  143.         {  
  144.             return Pagelen;  
  145.         }  
  146.         if(c == '\n' || c == '\r'//非标准模式下,默认回车和换行之间的映射已不存在,所以检查回车符'\r'。  
  147.           return add_line;  
  148.     }while(1);  
  149.     return 0;  
  150. }  
  151. int char_to_terminal(int char_to_write)  
  152. {  
  153.     if(out_stream) putc(char_to_write,out_stream);  
  154.     return 0;  
  155. }  
  156. int lines_more(FILE *fp)  
  157. {  
  158.     int nrows;  
  159.     setupterm(NULL, fileno(fp), (int *)0);  
  160.     nrows = tigetnum("lines");  
  161.     return nrows;  
  162. }  
  163. int cols_more(FILE *fp)  
  164. {  
  165.     int ncols;  
  166.     setupterm(NULL, fileno(fp), (int *)0);  
  167.     ncols = tigetnum("cols");  
  168.     return ncols;  
  169. }  
  170. unsigned long get_filesize(FILE *fp)  
  171. {  
  172.     struct stat buf;  
  173.     if(fstat(fileno(fp), &buf) < 0)  
  174.         return (unsigned long) 0;  
  175.     return (unsigned long) buf.st_size;  
  176. }  
 

5. 参考书籍

  1. 《Linux 程序设计 第四版》 by Neil Matthew,Richard Stones 人民邮电出版社。
  2. Unix/Linux 编程实践教程  by Bruce Molay 清华大学出版社。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值