linux下自实现简易more命令

/*
1、可以以命令行参数形式顺序浏览多个文件
2、支持: q退出, 回车键输出一行,空格键输出正页
3、带百分比等提示功能
4、目前无其他功能,有待改进
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#define NUMLINE 47
#define NUMCHAR 1000

struct termios old;
/*
    恢复终端设置
*/
void recover_terminal_attr(void)
{
    tcsetattr(fileno(stdin), TCSANOW, &old);
}
/*
    修改终端,为使其接收单个字符,而不需要回车键
*/
void set_terminal_attr(void)
{
    
    struct termios new;
    
    /*保存旧终端,以备程序结束时恢复*/
    tcgetattr(fileno(stdin), &old);
    new = old;
    new.c_lflag &= ~(ECHO|ICANON);
    //一次读取一个字符
    new.c_cc[VMIN] = 1;
    //Timeout in deciseconds for noncanonical read(TIME)
    new.c_cc[VTIME] = 0;
    tcsetattr(fileno(stdin), TCSAFLUSH, &new);
    atexit(recover_terminal_attr);    
}
/*
    输入出 “more01”及百分比提示字样
    文件结尾返回1, 否则返回0
*/
int print_more(FILE *fp, long end)
{    
    long cur;
    cur = ftell(fp);
    if (cur != end) {
        /*反白输出字符*/
        printf("\033[7m --more01--(%d%%)\033[m", (int)(cur*100/end));
        return 0;
    }
    return 1;
}
/*
    获得文件长度,并返回,单位字节。
*/
long getsize(FILE *fp)
{
    long cur, end;
    cur = ftell(fp);
    fseek(fp, 0, SEEK_END);
    end = ftell(fp);
    fseek(fp, cur, SEEK_SET);
    return end;
}
/*
    以行为单位输出fp文件内容,size为文件大小,num为输出行数
    文件结尾返回1, 否则返回0
*/
int print(FILE *fp, int size, int num)
{

    char buf[NUMCHAR];
    int i, wnum;
    
    /*擦除提示信息, \r return本行起始位置 " "输出空格将之前信息覆盖*/
    printf("\r                                    \r");
    for (i = 0; i < num;i++) {
        if (fgets(buf, NUMCHAR, fp) == NULL) 
            return 1;
        wnum = fputs(buf, stdout);
        if (wnum == EOF)
            exit(4);
    }
    if (print_more(fp, size) == 0)
        return 0;
    else
        return 1;
}

int main(int argc, char* argv[])
{
    
    FILE *fp;
    char buf[NUMCHAR], cmd;
    int num, size, flag, n;
    long end;
    
    set_terminal_attr();
    for (n = 1, num = NUMLINE; n < argc; n++, flag = 0) {
        fp = fopen(argv[n], "r");
        if (fp == NULL) {
            perror(argv[n]);
            continue;
        } else 
            if (n != 1) {
                printf("\033[7m --more01--(Next File: %s)\033[m", argv[n]);
                goto getcmd;
        
        }
        size = getsize(fp);
        while (1) {
            flag = print(fp, size, num);
getcmd:
            if (flag == 1 )
                break;
            while (1) {
                cmd = getchar();
                if (cmd == 'q' || cmd == '\n'||cmd ==32)
                    break;
            }
            switch (cmd) {
                case '\n': num = 1; break;
                case ' ': num = NUMLINE; break;
                case 'q': printf("\r                            \r");
                    exit(0);
            }
        }
        fclose(fp);
    }
    exit(0);
}

总结:

1、在程序运行期间,需要输入单个字符及实现命令的响应(不需要回车键发送)。修改终端参数,并通过atexit函数调用恢复函数,在程序退出时,恢复之前设置。

2、若在程序中调用system函数执行,shell命令等。执行程序的进程会单独创建子进程用于该命令的实现,并将结果输出到标准输出。

3、

#include <stdio.h>
int fileno(FILE *fp);

文件指针作为参数,返回描述符

4、非缓冲文件I/O:open read write, 参数为指向文件指针;缓冲I/O:fopen fgets fputs(行缓冲),参数为文件描述符。

判断fgets是否接收为空:if (fgets(...) == NULL)

5、

#include <stdio.h>
long ftell(FILE *fp);
//Returns: current file position indicator if OK, 1L on error

int fseek(FILE *fp, long offset, int whence);
//Returns: 0 if OK, nonzero on error

void rewind(FILE *fp);


ftell 返回当前偏移量,fseek制定当前偏移到制定位置,rewind返回到文件起始处。

6、

#include <stdio.h>
void perror(const char *msg);

The perror function produces an error message on the standard error, based on the current value of errno, and returns.
It outputs the string pointed to by msg, followed by a colon and a space, followed by the error message corresponding to the value of errno, followed by a newline.

from APUE

7、magci number

In computer programming, the term magic number has multiple meanings. It could refer to one or more of the following:

  • A constant numerical or text value used to identify a file format or protocol; for files, see List of file signatures

  • Distinctive unique values that are unlikely to be mistaken for other meanings (e.g., Globally Unique Identifiers)

  • Unique values with unexplained meaning or multiple occurrences which could (preferably) be replaced with named constants

from wiki

8、调用exit函数,若不包含stdlib.h则会警告兼容问题,但可通过,加上之后就OK了。

exit 或 return返回值可通过shell内的 echo $?查看。


待解决问题:

1、若程序以一个管道或重定向作为输入,首先应该判断stdin是否为空,目前未找到判断stdin为空的方法。

2、写程序感觉能写出来,但是细节不确定,很容易被一些细节卡住。另外程序的大体思路不清晰,总是用一种自己感觉不是最优的算法在解决,等解决完了,再修改程序。

3、若以管道或重定向修改进程了的stdin,则进程无法通过终端接受键盘发送的指令(stdin已被修改),此时需要重新打开输入终端。具体可参照《Unix/Linux编程实践教程》,more02例。

你可能感兴趣的:(linux下自实现简易more命令)