System Programming(more)

전창우·2024년 10월 20일

system programming

목록 보기
6/9

more

리눅스 시스템에서 파일 내용을 확인하는 명령어들 중에 하나인 'more'를 구현해보자.

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 flow

more 명령어의 흐름은 다음과 같다.

  1. 기존 terminal Setting을 저장
  2. tcgetattr, tcsetattr을 이용하여,non-canonical, non-echo mode로 터미널 설정을 변경
  3. ioctl을 이용하여, 출력 터미널의 size(row, col)를 구하기
  4. 최초 실행의 경우, row 크기만큼 출력.
  5. 이후에는, 터미널로부터 'q', ' ' '\n'을 입력 받으며 출력 크기를 제어
  6. 파일을 다 읽었다면, 기존 터미널 setting으로 백업

more implementation

#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;
}

0개의 댓글