리눅스 시스템에서 파일 내용을 확인하는 명령어들 중에 하나인 'more'를 구현해보자.
리눅스 환경에서 more를 실행시켜보면, 해당 명령어가 Non-canonical, Non-echo mode로 실행된다는 것을 알 수 있다.
우리는 이러한 터미널 환경 설정을 tcgetattr, tcsetattr을 이용하여 구현할 수 있다.
terminal setting back-up
우리는 More를 위해 Non-canonical, Non-echo mode로 설정했다. 하지만 'more'을 실행이 끝나면, 원래 설정으로 되돌려야 하므로, 본래 터미널 설정을 저장해놓는 termios struct가 필요하다.
void set_no_cr_echo()
{
struct termios ttyinfo;
if(tcgetattr(0, &ttyinfo)==-1)
{
perror("tcgetattr");
exit(EXIT_FAILURE);
}
ttyinfo.c_lflag &= ~ICANON;
ttyinfo.c_lflag &= ~ECHO;
if(tcsetattr(0, TCSANOW,&ttyinfo)==-1)
{
perror("tcsetattr");
exit(EXIT_FAILURE);
}
}
void tty_backup(int how)
{
static struct termios orig_mode;
//static int orig_flags;
if(how==0)
{
tcgetattr(0, &orig_mode);
//orig_flags=fcntl(0, F_GETFL);
}
else if(how==1)
{
tcsetattr(0,TCSANOW,&orig_mode);
//fcntl(0,F_SETFL,orig_flags);
}
}
more 명령어의 흐름은 다음과 같다.
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <assert.h>
#include <fcntl.h>
#include <termio.h>
void find_size(int* row, int* col); //find i/o size
void do_more(FILE* file, int row, int col);
int see_more(FILE* file, int row, int col);
void tty_backup(int how); // 기존 설정 백업
void set_no_cr_echo(); //canonical, echo 해제
int main(int ac, char* av[])
{
if(ac!=2)
{
printf("[Usage] ./hw04_more [filename]\n");
exit(EXIT_FAILURE);
}
tty_backup(0); // 기존의 터미널 설정을 벡업에 저장
//set_non_blocking_mode(); //선택
set_no_cr_echo();
int row, col;
find_size(&row, &col);
FILE* fp = fopen(av[1],"r");
if(fp==NULL)
{
perror("fopen");
exit(1);
}
do_more(fp, row, col);
fclose(fp);
tty_backup(1);
}
void tty_backup(int how)
{
static struct termios orig_mode;
if(how==0)
{
tcgetattr(0, &orig_mode);
}
else if(how==1)
{
tcsetattr(0,TCSANOW,&orig_mode);
}
}
void set_no_cr_echo()
{
struct termios ttyinfo;
if(tcgetattr(0, &ttyinfo)==-1)
{
perror("tcgetattr");
exit(EXIT_FAILURE);
}
ttyinfo.c_lflag &= ~ICANON;
ttyinfo.c_lflag &= ~ECHO;
if(tcsetattr(0, TCSANOW,&ttyinfo)==-1)
{
perror("tcsetattr");
exit(EXIT_FAILURE);
}
}
void find_size(int* row, int* col)
{
struct winsize wbuf;
if(ioctl(0,TIOCGWINSZ, &wbuf)!=-1)
{
*row=wbuf.ws_row;
*col=wbuf.ws_col;
}
else{
perror("ioctl");
exit(EXIT_FAILURE);
}
}
void do_more(FILE* file, int row, int col)
{
char line[col];
int num_of_line=0;
int reply =0;
FILE* fp_tty = fopen("/dev/tty","r"); //현재 터미널 Device open
if(fp_tty==NULL)
{
perror("fopen");
exit(1);
}
ssize_t n;
while (fgets(line, col, file)!=NULL) //파일을 한 줄 씩 읽음
{
if (num_of_line == row)
{
reply = see_more(fp_tty,row, col);
if (reply == 0)
break;
num_of_line -= reply;
}
if(fputs(line, stdout)==EOF)
exit(1);
num_of_line++;
}
}
int see_more(FILE* file, int row, int col)
{
char c=0;
printf("\033[7m more?(%d x %d) \033[m",row,col);
while((c=getc(file))!=EOF)
{
if(c=='q')
return 0;
else if(c==' ')
return row;
else if(c=='\n')
return 1;
}
return 0;
}