긴 이름의 옵션을 파싱하는 C함수, getopt_long()

긴 이름 옵션 파싱함수

getopt() 함수는 비교적 사용이 쉬운데, 보통 명령행 유틸리티들은 옵션을 보다 명확하게 인지할 수 있는 긴 이름의 옵션들을 지원한다. 예를 들어 출력파일을 결정할 때 -o outfile로 쓰는 대신 --output outfile로 쓰게끔 허용하는 것이다. 이 기능은 getopt_long()이라는 함수를 통해 지원할 수 있다. 긴 이름 옵션은 하나의 문자열에 망라하기 어려우므로 별도의 구조체를 사용하여 옵션 정보를 마련해두고 이 구조체의 배열을 사용하는 방식으로 처리한다. 그러면서 인터페이스는 좀 달라지지만, 사용하는 방식은 거의 유사하다고 보면 된다.

option 구조체

각각의 옵션 정보를 담고 있는 구조체가 필요하다. 이 구조체는 다음의 정보를 포함한다.

  • const char * name : 옵션의 이름
  • int has_arg : 옵션이 추가적인 파라미터를 갖는지 여부에 대한 플래그. 이는 따로 정의된 상수 no_arguement, required_arguement, optional_argument를 쓴다.
  • int *flag : 옵션 값이 저장될 위치
  • int val : 옵션값

getopt_long()

이 함수의 원형은 다음과 같다.

int getopt_long(
    int argc,
    char * const *argv,
    const char *shortopts,
    const struct option* longopts,
    int *indexptr
    );

먼저 이 함수는 짧은 옵션을 만난 경우, getopt()와 동일한 동작을 하면서 파싱된 옵션 값을 리턴한다. 그리고 긴 옵션을 만나는 경우에는 option 구조체의 정의에 따라서 다음과 같은 동작을 한다. 이 때 가장 중요한 구조체의 멤버는 flagval이다.
flag가 널포인터이면, getopt_long() 함수는 val 값을 리턴한다. 플래그가 널포인터가 아니면 플래그값이 val로 세팅된다. 그리고 이 경우 함수는 0을 리턴하게 된다. *indexptr에는 구조체 배열 중 몇 번째 option의 값이 검출되었는지를 알려주기 위해서 이 값이 함수 내에서 업데이트된다.

구현 전략

구현 전략은 다음과 같다.

  1. 짧은 옵션들을 먼저 정의한다.
  2. 긴 옵션명을 추가로 정의한다.
  3. 긴 옵션명 중에서 짧은 옵션명과 동일한 옵션인 것과 그렇지 않은 것을 구분한다.
  4. 짧은 옵션과 다른 긴 옵션의 경우 결과를 저장할 flag가 될 변수를 미리 준비한다.
  5. 1~4의 내용으로 옵션 구조체 배열을 정의한다. 이 배열은 {0, 0, 0, 0}의 센티넬로 마무리한다.
  6. getopt_long() 함수를 루프 속에서 처리한다.
  7. -1이 리턴될 때까지 반복한다.

샘플코드

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
/* Flag set by ‘--verbose’. */
static int verbose_flag;
int main (int argc, char **argv)
{
    int c;
    while (1)
    {
        static struct option long_options[] =
        {
            // --verbose 옵션을 만나면 "verbose_flag = 1"이 세팅된다.
            {"verbose", no_argument,       &verbose_flag, 1},
            {"brief",   no_argument,       &verbose_flag, 0},
            // 이 아래로는 짧은 옵션과 동일한 옵션들
            // 예를 들어 --add 는 -a와 동일한 동작을 한다.
            {"add",     no_argument,       0, 'a'},
            // 널포인터의 val이 'a'이면 'a'를 리턴할 것이다.
            {"append",  no_argument,       0, 'b'},
            {"delete",  required_argument, 0, 'd'},
            {"create",  required_argument, 0, 'c'},
            {"file",    required_argument, 0, 'f'},
            {0, 0, 0, 0} // 옵션 배열은 {0,0,0,0} 센티넬에 의해 만료된다.
        };
        // !!! 옵션 인덱스를 저장하는 변수
        int option_index = 0;
        // 옵션 파싱
        c = getopt_long (argc, argv, "abc:d:f:",
                long_options, &option_index);
        // getopt()와 동일하게 모든 옵션 파싱을 마치면 -1을 리턴합니다.
        if (c == -1)
            break;
        switch (c)
        {
            case 0:
                // 긴 이름 옵션을 만났을 때
                if (long_options[option_index].flag != 0)
                    break;
                printf ("option %s", long_options[option_index].name);
                if (optarg)
                    printf (" with arg %s", optarg);
                printf ("\n");
                break;
            // 이하는 짧은 옵션과 동일한 긴 이름 옵션을 만났을 때
            case 'a':
                // `-a`를 만났거나 `--add`를 만났다.
                puts ("option -a\n");
                break;
            case 'b':
                puts ("option -b\n");
                break;
            case 'c':
                printf ("option -c with value `%s'\n", optarg);
                break;
            case 'd':
                // `--delete`, `-d`를 만난 경우, 추가 옵션이 필요하며 이는 `optarg`에 저장되어 있다.
                printf ("using short option.\n");
                printf ("option -d with value `%s'\n", optarg);
                break;
            case 'f':
                printf ("option -f with value `%s'\n", optarg);
                break;
            case '?':
                /* getopt_long already printed an error message. */
                break;
            default:
                abort ();
        }
    }
    // verbose_flag가 1로 설정되었을 때(--verbose)
    if (verbose_flag)
        puts ("verbose flag is set");
    /* Print any remaining command line arguments (not options). */
    if (optind < argc)
    {
        printf ("non-option ARGV-elements: ");
        while (optind < argc)
            printf ("%s ", argv[optind++]);
        putchar ('\n');
    }
    exit (0);
}

Read more

워드프레스에서 고스트로 이전

워드프레스에서 고스트로 이전

이 글을 쓰면서도 믿기 힘든 사실인데, 블로그라는 걸 처음 시작한지가 20년이 되었습니다. 이글루스에서 처음 시작했다가, SK컴즈가 인수한다고 발표함과 동시에 워드프레스로 플랫폼을 옮겼죠. 워드프레스오 옮긴 이후에는 호스팅 환경을 이리 저리 옮기긴 했지만 거의 18년 가까이 워드프레스를 사용해온 것 같습니다. 그 동안 워드프레스는 블로깅 툴에서 명실상부한 범용CMS로 발전했습니다. 사실 웬만한 홈페이지들은 이제

By sooop
띄어쓰기에 대한 생각

띄어쓰기에 대한 생각

업무 메일을 쓸 때 가장 많이 쓰는 말 중에 하나가 메일 말미에 ‘업무에 참고 부탁 드립니다.‘인데요, 어느 날부터 아웃룩에서 이 ‘부탁 드립니다’가 틀렸다고 맞춤법 지적을 하기 시작했습니다. 맞는 말은 ‘부탁드립니다’라고 붙여 쓰는 거라고. 사실 아래아한글 시절부터 이전의 MS워드까지, 워드프로세서들의 한국어 맞춤법 검사 실력은 거의 있으나 마나 한

By sooop

구글 포토에서 아이클라우드로 탈출한 후기

한 때 구글 포토가 백업 용량을 무제한으로 제공해 주겠다고해서, 구글 포토를 사용해서 사진을 백업해왔습니다. 물론 이 이야기의 결말은 저나 이 글을 읽고 있는 여러분이나 모두 알고 있습니다. 사실 AI에게 학습 시킬 이미지 데이터를 모으기 위한 것일 뿐이라거나 하는 이야기는 그 당시에도 있었습니다만, 에이 그래도 구글인데 용량은 넉넉하게 주겠지…하는 순진한

By sooop

Julia의 함수 사용팁

연산자의 함수적 표기 Julia의 연산자는 기본적으로 함수이며, 함수 호출 표기와 같은 방식으로 호출하는 것이 가능합니다. 또한 그 자체로 함수이기 때문에 filter(), map() 과 같이 함수를 인자로 받는 함수에도 연산자를 그대로 적용하는 것이 가능합니다. 특히 + 연산자는 sum() 함수와 같이 여러 인자를 받아 인자들의 합을 구할 수 있습니다. 2 + 3 # = 5 +(2,

By sooop