#include <stdio.h>
#include <stdlib.h>
#define PAGELEN 24
#define LINELEN 512
void do_more(FILE*);
int see_more();
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 num_of_lines = 0;
int see_more(), reply;
while (fgets(line, LINELEN, fp)) {
if (num_of_lines == PAGELEN) {
reply = see_more();
if (reply == 0) break;
num_of_lines -= reply;
}
if (fputs(line, stdout) == EOF) exit(1);
num_of_lines++;
}
}
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;
}
分析上述代码的不足:
从上述第11行代码可以知道,若只有一个命令行参数,则会从标准输入读取数据。
若使用如下的管道重定向和输入重定向的话,more01这个程序就只有一个命令行参数,就不是从标准输入读入数据,而是分别从ls /bin这个命令输出、more01.c这个文件,来读取数据:
从上述代码的第45行我们可以知道,当程序运行到第45行代码时会请求我们输入,但若我们使用的是管道重定向或输入重定向的话,就不会请求我们输入了,因为getchar()这个函数是读取标准输入的数据,现在我们用管道重定向或输入重定向将标准输入重定向了,所以getchar()函数会直接读取ls /bin这个命令输出或more01.c这个文件中的数据。因此,当使用
ls /bin | ./more01
或
./more01 < more01.c
这两个命令的时候,会直接输出全部的内容,而不会像下面这幅图一样进行询问:
(这幅图是使用命令./more01 more01.c后得到的结果)
解决方案:
从标准输入中读入要分页的数据,从键盘读入用户的输入。
上图中/dev/tty这个文件是键盘和显示器的设备描述文件,即当你向这个文件写入数据的时候,这些数据会直接在显示器上打印出来;当你向这个文件读入数据的时候,就相当于从键盘获取用户的输入(即向用户请求输入,用户输入后的数据会直接被读走)。这个就可以保证,即使程序的输入/输出被重定向,程序还可以通过这个文件和终端交换数据。
修改后的源程序可以在more02.c文件中看到。
其它不足之处:
用户输入空格键或字符'q'后,还得按回车键程序才会动作,并且输入的空格键或字符'q'还会显示出来,而实际的more命令是不需要额外的回车,并且输入的字符也不会回显。