티스토리 뷰

1. rm.cinclude한 헤더 파일 위치 파악


저번 주에 coreutils를 실행하기 위한 작업을 진행하였다. 그 결과 git clonecoreutils 디렉터리에 실행에 필요한 여러 파일이 추가되어 디렉터리 내용이 많이 바뀌었다. 그중 rm.c 파일과 이와 연관된 파일만 분석하였다. 위 사진은 rm.c 파일의 include 부분을 캡처한 것이다.

 

C언어 표준 라이브러리 stdio.h, assert.h, sys/types.h
/coreutils/lib config.h, argmatch.h, error.h, root-dev-ino.h, yesno.h, priv-set.h
/coreutils/src system.h, die.h, remove.h
/gcc/include
(별도 파일)
getopt.h

 

 

 

2. 옵션 개발 등록

본 프로젝트에서는 짧은 옵션 -b와 긴 옵션 --except-files 중 짧은 옵션을 먼저 구현한 이후에 긴 옵션을 개발할 예정이다. 따라서 현재 보고서는 짧은 옵션만을 고려하여 작성하였다.

 

A. rm_options 구조체에 옵션 등록

static void
rm_option_init (struct rm_options *x)
{
  x->ignore_missing_files = false;
  x->interactive = RMI_SOMETIMES;
  x->one_file_system = false;
  x->remove_empty_directories = false;
  x->recursive = false;
  x->root_dev_ino = NULL;
  x->preserve_all_root = false;
  x->stdin_tty = isatty (STDIN_FILENO);
  x->verbose = false;

  /* Since this program exits immediately after calling 'rm', rm need not
     expend unnecessary effort to preserve the initial working directory.  */
  x->require_restore_cwd = false;
}

위 코드는 rm.c 파일에서 rm_options 구조체의 멤버 변수를 초기화하는 rm_option_init 함수이다. 예를 들어 x->recursive-r 옵션 관련 구조체 멤버변수로, r이 입력되었다면 main() 함수에서 멤버 변수 값이 true로 바뀐다. , rm_options_init의 멤버 변수들로 입력한 옵션을 제어하므로 본 프로젝트에서 개발할 일부 파일을 제외하고 입력받은 디렉터리 내 모든 파일을 삭제하는 옵션을 위한 구조체 멤버 변수가 필요하다. 따라서 rm_options 구조체에 remove_except_files 멤버 변수를 추가하였다.

 

struct rm_options
{
  /* If true, ignore nonexistent files.  */
  bool ignore_missing_files;

  /* If true, query the user about whether to remove each file.  */
enum rm_interactive interactive;

...

  // mine
  /* if true, exclude some files when removing. */
  bool remove_except_files;
}

rm_options 구조체는 /coreutils/src/remove.h에 정의되어 있다. 위 코드는 rm_options 구조체 정의의 일부를 나타낸다. 아래쪽 // mine부터 직접 멤버 변수를 등록하고자 추가한 코드이다.

 

B. 구조체 멤버변수 초기화 함수 수정

rm_options 구조체 정의에 remove_except_files 멤버 변수를 추가한 후 /coreutils/src/rm.c에서 구조체 멤버변수를 초기화 작업을 진행하였다.

 

static void
rm_option_init (struct rm_options *x)
{
  x->ignore_missing_files = false;
  x->interactive = RMI_SOMETIMES;
  x->one_file_system = false;
  x->remove_empty_directories = false;
  x->recursive = false;
  x->root_dev_ino = NULL;
  x->preserve_all_root = false;
  x->stdin_tty = isatty (STDIN_FILENO);
  x->verbose = false;

  /* Since this program exits immediately after calling 'rm', rm need not
     expend unnecessary effort to preserve the initial working directory.  */
  x->require_restore_cwd = false;

  // mine
  x->remove_except_files = false;
}

위 코드는 A에서 언급된 구조체 멤버 변수를 초기화하는 함수이다. 함수의 가장 아래쪽 // mine 부분에 직접 remove_except_files 멤버 변수를 초기화하는 코드를 추가하였다. 한편 기본적으로 모든 멤버 변수는 false로 초기화되고, 옵션을 사용할 때만 true로 값이 바뀌어 해당 옵션 기능을 수행하는 방식이다.

 

C. main() 함수 수정

int main (int argc, char **argv) {
  bool preserve_root = true;
  struct rm_options x;
  bool prompt_once = false;
  int c;

  initialize_main (&argc, &argv);
  set_program_name (argv[0]);
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdin);

  rm_option_init (&x);

  /* Try to disable the ability to unlink a directory.  */
  priv_set_remove_linkdir ();

while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
{ ... }

  if (argc <= optind) { ... }

  if (x.recursive && preserve_root) { ... }

  uintmax_t n_files = argc - optind;
  char **file =  argv + optind;

  if (prompt_once && (x.recursive || 3 < n_files))
    {
      fprintf (stderr,
                (x.recursive
                ? ngettext ("%s: remove %"PRIuMAX" argument recursively? ",
                            "%s: remove %"PRIuMAX" arguments recursively? ",
                            select_plural (n_files))
                : ngettext ("%s: remove %"PRIuMAX" argument? ",
                            "%s: remove %"PRIuMAX" arguments? ",
                            select_plural (n_files))),
                program_name, n_files);
      if (!yesno ())
        return EXIT_SUCCESS;
    }

  enum RM_status status = rm (file, &x);
  assert (VALID_STATUS (status));
  return status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS;
}

위 코드는 /coreutils/src/rm.cmain() 함수이다. rm_options 구조체의 구조체 변수인 x가 선언되고, rm_option_init(&x)로 멤버 변수가 초기화 함수를 호출하는 코드가 포함되어 있다.

 

switch (c) {
   case 'd':
     x.remove_empty_directories = true;
     break;
...
}

내용이 생략 처리된 while문에는 위와 같은 switch문이 들어 있는데, switch문은 입력한 옵션에 따라 구조체 멤버 변수 값을 바꾸는 역할이다.

 

  while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
    {
      switch (c)
        {
          // mine
        case 'b' :
          x.remove_except_files = true;
          break;
        ...
        }
    }

따라서 switch문 안에 remove_except_files 멤버 변수 값을 제어하는 case문을 추가하였다. b 옵션을 입력하면 디렉터리 삭제 시 특정 파일을 삭제에서 제외하고 싶다는 의미이므로, b 옵션을 사용하기 위해 remove_except_files 멤버변수 값을 true로 바꾼다.

 

  if (x.remove_except_files)
    {
      fprintf (stderr, "If you want to stop inputting files, you input '!no'");
      fprintf (stderr, (x.recursive ("Which files to exclude from deletion?: ")));
      if (!yesno ())
        return EXIT_SUCCESS;
    }

main() 함수 코드에서 2번째 빨간 박스를 보면, x.recursive라는 특정 구조체 멤버 변수의 값에 따라 if문이 실행된다. if문을 충족할 경우 터미널 창에해당 파일을 삭제하겠냐는 질문이 출력된다. 이처럼 -b 옵션을 입력하면 터미널 창에 어떤 파일을 삭제에서 제외하고 싶냐는 문장이 출력되어야 하므로, 위 코드를 추가하였다. 이 코드는 반복적으로 질문하고, 사용자의 입력 파일을 찾는 등의 과정은 일단 생략하고, -b 입력 시 fprintf 문구가 출력되는지를 테스트하는 용도이다. , -b 입력하면 b가 잘 인식되고 remove_except_files 구조체 멤버변수 값이 잘 바뀌는지 테스트하기 위함이다.

 

D. make 및 테스트

수정한 소스 파일 내용을 반영하기 위해 터미널창에 make를 입력하여 다시 컴파일하였다. 위 캡쳐 화면과 같이, 별다른 문제없이 컴파일이 완료되었다.

 

rm 바이너리 파일로 -b 옵션을 사용해 test2.c 파일을 삭제하는 명령어를 입력하였다. 그 결과는 실패였다. fprintf“If you want to stop inputting files, you input '!no'”는 출력되리라 기대하였지만, -b 옵션을 인식하지조차 못하는 듯하다.

 

다음주에는 -b 옵션을 인식하도록 수정하는 과정부터 진행하도록 하겠다.

 

 

3. 진행상황 GitHub에 업로드

https://github.com/YejinHwang-D/rm-execpt-files/tree/main/coreutils_rm

rm과 관련된 /coreutils/lib 디렉터리에 있던 헤더 파일과 /coreutils/src 디렉터리에 있던 헤더 파일을 구분하여 업로드하였다. remove.h rm.c 파일은에는 2. 옵션 개발 등록 과정에서 추가된 코드가 반영되어 있다.

한편 /lib 디렉터리 내 일부 파일은 바로가기 파일로 자동 변경되어 파일명 앞에 언더바_를 추가하였다.

 

728x90