自己编写more命令

more命令可以实现的功能是很丰富的,包括模式查找。今天我们只是实现它的一个基本功能:

  1. more  文件列表
  2. 在屏幕正文显示已读的百分比
  3. 按下回车显示下一行
  4. 按下空格显示下一屏
  5. 按下q退出
  6. 按回车、空格、q时没有回显

这里有几个问题:

  1. 要在屏幕正文显示已读的百分比,那我们必须先获取文件列表中各个文件的长度,这个可以使用系统调用lstat
    #include<sys/stat.h>
    struct stat *buf;
    lstat("filename",buf) //获取文件状态
    filesize+=buf->st_size; //获取文件大小 
  2. 要显示整屏内容,我们就需要知道一屏可以显示多少行,然后从文件中读出这么多行显示在屏幕上。

    #include<termios.h>
    struct winsize *ws;
    ws=(struct winsize*)malloc(sizeof(struct winsize));
    memset(ws,0x00,sizeof(struct winsize));
    ioctl(STDIN_FILENO,TIOCGWINSZ,ws);
    int width=ws->ws_col;
    int height=ws->ws_row;

  3. 禁用回显

    #include<termios.h>
    struct termios ts; //终端属性
    tcgetattr(STDIN_FILENO,&ts); //获取终端属性 
    ts.c_lflag &= (~ECHO); //阻止回显
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts); //设置终端的新属性

  4. 从终端读取单个字符后立即返回,不需要按回车
    #include<termios.h>
    struct termios ts; //终端属性
    tcgetattr(STDIN_FILENO,&ts); //获取终端属性 
    ts.c_lflag &= (~ICANON); //设置终端为非标准模式
    ts.c_cc[VMIN]=1; //这两行当需要从终端获取字符时立即返回,不需要按回车键
    ts.c_cc[VTIME]=0;
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts); //设置终端的新属性 

完整代码:

#include<stdio.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<string.h>
#include<termios.h>
void gettermsize(int *w,int *h);     //获取终端尺寸
void do_more(FILE *);   //根据see_more获取的用户输入,显示更多的文件内容或退出
int see_more();   //捕获用户的输入指令
void settermattr();    //设置终端属性
int width;      //终端屏幕宽度
int height;     //终端屏幕高度
int filesize;   //文件的大小
int readsize;   //已经显示的内容长度
struct termios ts,ots;     //终端属性
int main(int argc,char *argv[]){
    gettermsize(&width,&height);
    settermattr();
    FILE *fp;
    struct stat *buf;
    filesize=0;
    readsize=0;
    buf=(struct stat*)malloc(sizeof(struct stat));
    memset(buf,0x00,sizeof(struct stat));
    if(argc==1)     //如果没有指定文件,则从标准输入读取内容
        do_more(stdin);
    else{        //从文件列表中读取内容
        int i;
        for(i=1;i<argc;i++){       //more命令后面可能跟多个文件
            if((fp=fopen(*(argv+i),"r"))!=0){     //以只读形式打开文件
                if(lstat((const char*)*(argv+i),buf)==0){   //获取文件状态
                    filesize+=buf->st_size;     //获取文件大小
                    printf("fs=%ld\n",buf->st_size);
                }
                else{
                    exit(1);
                }
                fclose(fp);      //关闭文件
            }
            else{        //打开文件失败,退出程序
                exit(1);
            }
        }
        printf("size=%d\n",filesize);
        for(i=1;i<argc;i++){       //more命令后面可能跟多个文件
            if((fp=fopen(*(argv+i),"r"))!=0){     //以只读形式打开文件
                do_more(fp);        //显示文件内容
                fclose(fp);      //关闭文件
            }
            else{        //打开文件失败,退出程序
                exit(1);
            }
        }
    }
    tcsetattr(STDIN_FILENO,TCSANOW,&ots);
    return 0;
}
void gettermsize(int *w,int *h){
    struct winsize *ws;
    ws=(struct winsize*)malloc(sizeof(struct winsize));
    memset(ws,0x00,sizeof(struct winsize));
    ioctl(STDIN_FILENO,TIOCGWINSZ,ws);
    *w=ws->ws_col;
    *h=ws->ws_row;
}
void settermattr(){
    tcgetattr(STDIN_FILENO,&ts);    //获取终端属性 
    ots=ts;         //备份终端属性
    ts.c_lflag &= (~ECHO);    //阻止回显
    ts.c_lflag &= (~ICANON);    //设置终端为非标准模式
    ts.c_cc[VMIN]=1;        //这两行当需要从终端获取字符时立即返回,不需要按回车键
    ts.c_cc[VTIME]=0;
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts);    //设置终端的新属性。TCSAFLUSH表示输出队列空了以后才生效,生效之前的输出队列被flush
}
void do_more(FILE *fp){
    int linesize=width;
    char line[linesize];
    int num_of_lines=0;     //记录本次读了多少行
    int reply;      //记录see_more()的返回值
    FILE *fp_tty;
    while(fgets(line,linesize,fp)){    //从文件中读取一行内容
        readsize+=strlen(line);     //读出的内容长度
        if(num_of_lines==height){   //如果满屏了
            reply=see_more(); //从键盘获取用户输入的命令
            if(reply==0){        //用户不需要显示更多内容了,要退出
                tcsetattr(STDIN_FILENO,TCSANOW,&ots);
                exit(0);
            }
            num_of_lines-=reply;
        }
        if(fputs(line,stdout)==EOF){ //把这行内容显示在用户屏幕上
            tcsetattr(STDIN_FILENO,TCSANOW,&ots);        //TCSANOW表示修改立即生效
            perror("fputs");
            exit(1);
        }
        num_of_lines++;
    }
}
int see_more(){
    int c;
    printf("\033[7m--more--(%2.0f%%)\033[m",(double)readsize/filesize*100);
    while((c=getc(stdin))!=EOF){            //注意加括号,赋值操作符是右结合的。如果不加括号会把getchar()!=EOF的结果赋给c
        if(c=='q'){
            return 0;
        }
        if(c==' ')      //空格
            return height;    
        if(c=='\n')     //回车
            return 1;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值