티스토리 뷰
1. --except-files 등록하기
A. rm.c 파일에 Long type 옵션 등록
Short type option인 -b가 ‘invalid option’으로 인식되는 문제를 해결하지 못하였지만, 일단 Long type option인 --except-files를 등록하였다.
struct option
{
const char *name;
/* has_arg can't be an enum because some compilers complain about
type mismatches in all the code that assumes it is an int. */
int has_arg;
int *flag;
int val;
};
/coreutils/lib/getopt-ext.h 파일에는 option 구조체가 정의되어 있다.
static struct option const long_opts[] =
{
// mine
{"except-files", no_argument, NULL, 'b'},
{"force", no_argument, NULL, 'f'},
{"interactive", optional_argument, NULL, INTERACTIVE_OPTION},
그리고 해당 구조체는 위 코드처럼 /coreutils/src/rm.c 파일에서 사용된다. 구조체의 멤버 변수 값을 할당하기 위해 name에는 except-files라는 옵션 이름을 주었다. 이 옵션을 사용하는 데 argument는 필요 없으므로 no_argument를, flag도 NULL로 주었다. val은 Long type 옵션과 동일한 기능을 하는 Short type 옵션을 입력하는 듯하여 ‘b’를 입력하였다. 왜냐하면 강제 삭제 Long type 옵션인 ‘--force'의 val로 Short type 옵션인 ‘-f’가 왔기 때문이다.
B. 테스트
make로 컴파일을 진행한 후 터미널 창에 /home/kmi0817/coreutils/src/rm --except-files test를 입력하였다.
그 결과 0419 보고서 2-C) rm.c 파일의 main() 함수 수정에서 추가한 아래 코드가 출력되었다.
// mine
if (x.remove_except_files)
{
fprintf(stderr, "If you want to stop inputting files, you input '!no'\n\n");
fprintf (stderr, "Which files to exclude from deletion?: ");
if (!yesno ())
return EXIT_SUCCESS;
}
즉, --except-files가 인식되었다는 의미이다. 실제 삭제가 진행되지는 않고, 위 문구만 단순 출력한다. 그리고 삭제에서 제외할 파일을 반복해서 묻지도 않는다. 위 코드는 단순히 -b나 --except-files 옵션이 인식되는지 테스트하기 위한 용도였기 때문이다.
솔직히 그동안 -b 옵션이 인식되지 않았기 때문에 구조체 val 멤버변수 값으로 b를 할당한 --except-files 옵션이 작동할 것이라고 예상하지 못하였었다. 일단 옵션이 인식되는 --except-files로 옵션 기능 구현을 시작하고 -b 옵션 인식 문제 해도 함께 진행할 예정이다.
2. -b 인식 문제 원인 파악 시도: rm 옵션 처리 과정 따라가기
새로 등록한 -b 옵션을 인식하지 못하는 이유를 밝히기 위해서는 기존 rm 명령어의 옵션이 어떻게 처리되는지 흐름을 파악해야 한다고 생각하였다. 따라서 /coreutils 디렉터리 내 파일에 테스트 문구를 출력하여 옵션 처리 흐름을 알아보았다.
A. rm.c 파일의 main() 함수: 옵션 인식 과정
int main(int argc, char **argv) {
bool preserve_root = true;
struct rm_options x;
/* TEST */
fprintf(stderr, "1) x 생성\n");
bool prompt_once = false;
int c;
initialize_main(&argc, &argv);
/* TEST */
fprintf(stderr, "2) initialize main\n");
set_program_name(argv[0]);
/* TEST */
fprintf(stderr, "3) set_program_name\n");
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
atexit(close_stdin);
rm_option_init(&x);
/* TEST */
fprintf(stderr, "4) rm_option_init\n");
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir();
/* TEST */
fprintf(stderr, "5) before while...\n");
while ((c = getopt_long(argc, argv, "dfirvIR", long_opts, NULL)) != -1)
{
/* TEST */
fprintf(stderr, "6) in while...\n");
위 코드는 /coreutils/src/rm.c 파일에서 main() 함수 부분이다. Invalid option이 출력되는 부분이 어디인지 알아내기 위해 main() 함수에서 fprintf(stderr, “…”)를 추가하였다.
그 결과 테스트 문구 5번과 6번 사이에서 invalid option 문구가 출력되었다. 따라서 while문의 조건문에서 문제가 발생함을 알 수 있었다. 저번 주 보고서에서는 무작정 -b가 부적절한 옵션이라고 출력되는 원인이 getopt_long()에 있다고 생각하였지만, 이번에는 getopt_long()의의 반환 값이 문제라는 확실한 증거를 찾아냈다.
B. getopt1.h 파일의 getopt_long() 함수
int
getopt_long (int argc, char *__getopt_argv_const *argv, const char *options,
const struct option *long_options, int *opt_index)
{
return _getopt_internal (argc, (char **) argv, options, long_options,
opt_index, 0, 0);
}
getopt_long() 함수는 /coreutils/lib 디렉터리에 위치하였다. 이 함수는 _getopt_internal()이라는 함수를 반환한다.
C. getopt.c 파일의 _getopt_internal() 함수
int
_getopt_internal (int argc, char **argv, const char *optstring,
const struct option *longopts, int *longind, int long_only,
int posixly_correct)
{
int result;
getopt_data.optind = optind;
getopt_data.opterr = opterr;
result = _getopt_internal_r (argc, argv, optstring, longopts,
longind, long_only, &getopt_data,
posixly_correct);
optind = getopt_data.optind;
optarg = getopt_data.optarg;
optopt = getopt_data.optopt;
return result;
}
/coreutils/lib/getopt.c 파일에 _getopt_internal() 함수가 정의되어 있다.
이 함수에서 사용되는 getopt_data는 _getopt_data 구조체의 인스턴스이다. _getopt_data 구조체는 getopt_int.h 파일에서 정의되어 있다.
_getopt_internal_r() 함수의 반환 값이 result 변수에 할당되는데, _getopt_internal_r() 함수는 _getopt_internal() 함수와 동일한 파일(getopt.c)에 정의되어 있다. 아래 코드가 _getopt_internal_r() 함수 내용이며 길이가 매우 길다. 코드를 다 분석하지는 못하였다.
int _getopt_internal_r (int argc, char **argv, const char *optstring,
const struct option *longopts, int *longind,
int long_only, struct _getopt_data *d, int posixly_correct)
{
int print_errors = d->opterr;
if (argc < 1)
return -1;
d->optarg = NULL;
// d의 optind가 없거나, 초기화되지 않았을 경우
if (d->optind == 0 || !d->__initialized)
optstring = _getopt_initialize (argc, argv, optstring, d, posixly_correct);
else if (optstring[0] == '-' || optstring[0] == '+')
optstring++;
if (optstring[0] == ':')
print_errors = 0;
/* Test whether ARGV[optind] points to a non-option argument. */
#define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')
if (d->__nextchar == NULL || *d->__nextchar == '\0')
{
/* Advance to the next ARGV-element. */
/* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
moved back by the user (who may also have changed the arguments). */
if (d->__last_nonopt > d->optind)
d->__last_nonopt = d->optind;
if (d->__first_nonopt > d->optind)
d->__first_nonopt = d->optind;
if (d->__ordering == PERMUTE)
{
/* If we have just processed some options following some non-options,
exchange them so that the options come first. */
if (d->__first_nonopt != d->__last_nonopt
&& d->__last_nonopt != d->optind)
exchange (argv, d);
else if (d->__last_nonopt != d->optind)
d->__first_nonopt = d->optind;
/* Skip any additional non-options
and extend the range of non-options previously skipped. */
while (d->optind < argc && NONOPTION_P)
d->optind++;
d->__last_nonopt = d->optind;
}
/* The special ARGV-element '--' means premature end of options.
Skip it like a null option,
then exchange with previous non-options as if it were an option,
then skip everything else like a non-option. */
if (d->optind != argc && !strcmp (argv[d->optind], "--"))
{
d->optind++;
if (d->__first_nonopt != d->__last_nonopt
&& d->__last_nonopt != d->optind)
exchange (argv, d);
else if (d->__first_nonopt == d->__last_nonopt)
d->__first_nonopt = d->optind;
d->__last_nonopt = argc;
d->optind = argc;
}
/* If we have done all the ARGV-elements, stop the scan
and back over any non-options that we skipped and permuted. */
if (d->optind == argc)
{
/* Set the next-arg-index to point at the non-options
that we previously skipped, so the caller will digest them. */
if (d->__first_nonopt != d->__last_nonopt)
d->optind = d->__first_nonopt;
return -1;
}
/* If we have come to a non-option and did not permute it,
either stop the scan or describe it to the caller and pass it by. */
if (NONOPTION_P)
{
if (d->__ordering == REQUIRE_ORDER)
return -1;
d->optarg = argv[d->optind++];
return 1;
}
/* We have found another option-ARGV-element.
Check whether it might be a long option. */
if (longopts)
{
if (argv[d->optind][1] == '-')
{
/* "--foo" is always a long option. The special option
"--" was handled above. */
d->__nextchar = argv[d->optind] + 2;
return process_long_option (argc, argv, optstring, longopts,
longind, long_only, d,
print_errors, "--");
}
/* If long_only and the ARGV-element has the form "-f",
where f is a valid short option, don't consider it an
abbreviated form of a long option that starts with f.
Otherwise there would be no way to give the -f short
option.
On the other hand, if there's a long option "fubar" and
the ARGV-element is "-fu", do consider that an
abbreviation of the long option, just like "--fu", and
not "-f" with arg "u".
This distinction seems to be the most useful approach. */
if (long_only && (argv[d->optind][2]
|| !strchr (optstring, argv[d->optind][1])))
{
int code;
d->__nextchar = argv[d->optind] + 1;
code = process_long_option (argc, argv, optstring, longopts,
longind, long_only, d,
print_errors, "-");
if (code != -1)
return code;
}
}
/* It is not a long option. Skip the initial punctuation. */
d->__nextchar = argv[d->optind] + 1;
}
/* Look at and handle the next short option-character. */
{
char c = *d->__nextchar++;
const char *temp = strchr (optstring, c);
/* Increment 'optind' when we start to process its last character. */
if (*d->__nextchar == '\0')
++d->optind;
if (temp == NULL || c == ':' || c == ';') {
if (print_errors)
fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c);
d->optopt = c;
return '?';
}
/* Convenience. Treat POSIX -W foo same as long option --foo */
if (temp[0] == 'W' && temp[1] == ';' && longopts != NULL)
{
/* This is an option that requires an argument. */
if (*d->__nextchar != '\0')
d->optarg = d->__nextchar;
else if (d->optind == argc)
{
if (print_errors)
fprintf (stderr,
_("%s: option requires an argument -- '%c'\n"),
argv[0], c);
d->optopt = c;
if (optstring[0] == ':')
c = ':';
else
c = '?';
return c;
}
else
d->optarg = argv[d->optind];
d->__nextchar = d->optarg;
d->optarg = NULL;
return process_long_option (argc, argv, optstring, longopts, longind,
0 /* long_only */, d, print_errors, "-W ");
}
if (temp[1] == ':')
{
if (temp[2] == ':')
{
/* This is an option that accepts an argument optionally. */
if (*d->__nextchar != '\0')
{
d->optarg = d->__nextchar;
d->optind++;
}
else
d->optarg = NULL;
d->__nextchar = NULL;
}
else
{
/* This is an option that requires an argument. */
if (*d->__nextchar != '\0')
{
d->optarg = d->__nextchar;
/* If we end this ARGV-element by taking the rest as an arg,
we must advance to the next element now. */
d->optind++;
}
else if (d->optind == argc)
{
if (print_errors)
fprintf (stderr,
_("%s: option requires an argument -- '%c'\n"),
argv[0], c);
d->optopt = c;
if (optstring[0] == ':')
c = ':';
else
c = '?';
}
else
/* We already incremented 'optind' once;
increment it again when taking next ARGV-elt as argument. */
d->optarg = argv[d->optind++];
d->__nextchar = NULL;
}
}
return c;
}
}
'개발 > 오픈소스기여' 카테고리의 다른 글
[오픈소스기여] 중간보고서: rm 명령어 옵션, 디렉터리 삭제 시 삭제에서 제외할 파일 (0) | 2022.05.13 |
---|---|
[오픈소스기여] 4월 20일 - 4월 26일 활동 보고서 (0) | 2022.05.13 |
[오픈소스기여] 4월 13일 - 4월 19일 활동 보고서 (0) | 2022.05.13 |
[오픈소스기여] 4월 6일 - 4월 12일 활동 보고서 (0) | 2022.04.18 |
[오픈소스] GNU rm 명령어에 새로운 옵션 추가하기 (0) | 2022.04.18 |