getoptを使う

# はじめに
getoptの使い方を説明します。
getoptはmain関数の引数argc / argvを解析します。
argv[1]~argv[argc-1]の文字列を**argument list**といいます。

from Wikipedia
> getopt is a C library function used to parse command-line options.

簡略化のためcommand-line optionsを**option**と表記します。

後半ではgetopt_longを説明します。

---
# optionとは
"-"で始まる文字列はoptionです。いくつか例を示します。
commandはプログラム名です。

```bash
$ ./command -a
$ ./command -b optarg
$ ./command -b optarg nonopt
```

"-a" "-b"はoptionです。

"-" の後ろの1文字は**option character**です。
"a" "b"はoption-characterです。

optionには**option argument**を指定できます。
"optarg"は"-b"のoption argumentです。

optionでもoption-argumentでもないargument-listの要素は
**non-option argument**です。"nonopt"はnon-option argumentです。

**option characterとoption argumentは空白なし連続して指定できます。**
例えば次のように指定できます。

```bash
$ ./command -boptarg   # -b optarg と同じ
$ ./command -aboptarg  # -a -b optarg と同じ
```

---

# getopt
getoptの関数プロトタイプと変数は次の通りです。

```c
int getopt(
  int argc,
  char* const argv[],
  const char* optstring
);

extern char* optarg;
extern int optind, opterr, optopt;
```

argc, argvはmain関数の引数です。

## optstring
optstringはoption characterを表す文字列です。
例を示します。

```c
const char* optstring = "ab:";
```

"a" "b"はoption characterです。
"b"はoption-argumentを持ちます。":"でそのことを示します。

```bash
$ ./command -a     # OK
$ ./command -a bbb # OK bbb is non-option argument
$ ./command -accc  # error invalid option
$ ./command -abbb  # OK b treated as "-b". bb is option argument
$ ./command -b     # error option requires an argument
$ ./command -b bbb # OK bbb is option argument
$ ./command -bbbb  # OK bbb is option argument
$ ./command -c     # error invalid option
```

## optarg
getoptがoption argumentを持つoptionを解析すると、
optargにoption argumentへのポインタが設定されます。
optargを参照することでoption argumentを取得できます。

## optind
optindはgetoptが次に処理するargv配列のindexです。初期値は1です。
よってgetoptの最初の呼び出しはargv[1]から解析をはじめます。
index = 1に設定することで再度argv[1]から解析を実行することができます。

## opterr
opterrはエラーログの出力を制御します。
opterr != 0 : エラーログを出力する(初期値)
opterr == 0 : エラーログを出力しない
opterrに0を設定することでエラーログ出力を無効にできます。

## optopt
optoptはエラーが発生したoption characterを保持します。

## getoptの処理
getoptは成功するとoption characterを返します。
option-argumentありの場合、option-argumentをoptargに設定します。

エラーが発生すると'?'を返します。エラー要因は次の通りです。

 - "-"で始まる文字列だがoption characterがない
 - option-argumentを持つoptionであるがoption-argumentがない

次に処理するoptionがなくなれば-1を返します。
-1を返した後のoptindにはnon-option argumentがあればそれを示し、
なければargcと一致します。

## 例
option "-a" "-b"(option argument付き)を受けつける例を示します。
whileを-1で抜けた後、non-option argumentを処理します。

```c:command.c
#include <stdio.h>
#include <unistd.h>

static void print_arg(int argc, char* argv[])
{
#if 0
    int i;
    for (i=0; i<argc; i++) {
        printf("argv[%d]=%s ", i, argv[i]);
    }
#endif
}

int main(int argc, char* argv[])
{
    int c;
    const char* optstring = "ab:" ; // optstringを定義します

    opterr = 0; // disable error log

    // non-option or end of argument list or error('?')までloop
    while ((c=getopt(argc, argv, optstring)) != -1) {
        print_arg(argc, argv);
        printf("opt=%c ", c);
        if (c == 'a') {
            // without option argument
        } else if (c == 'b') {
            // with option argument
            printf("optarg=%s ", optarg);
        } else {
            // '?'
            printf("optopt=%c ", optopt);
            if (optopt == 'b') {
                // no option argument
            } else {
                // unknown option character
            }
            printf("\n");
            return -1; // error
        }
        printf("\n");
    }

    // non-option or end of argument list
    while (optind < argc) {
        print_arg(argc, argv);
        printf("non-opt=%s \n", argv[optind]);
        optind++;
    }
    return 0;
}
```

## argument listの並び替え
optionとnon-option argumentの順番を入れ替えても動作するようにするために、
argument listの並び替えを行う場合があります。
この動作は**実装依存**です。実装によっては並び替えしないgetoptもあります。

並び替えできる実装では(1)はOKになります。Ubuntu14.04ではOKでした。
並び替えできない実装では(1)はerrorになります。MinGWではerrorでした。

```bash
# const char* optstring = "a";
$ ./command bbb -a # (1)
```

# getopt_long
getopt_longを説明します。この関数はlong optionを扱います。

## long optionとは
"--"で始まる文字列はlong optionです。いくつか例を示します。

```bash
$ ./command --create
$ ./command --cre
$ ./command --delete value
$ ./command --delete=value
```

"--create" "--delete"はlong optionです。

long optionは**一意に判定できる限り短くできます**。
getopt_longは"--cre"は"--create"であると認識します。

long optionにはoption argumentを指定できます。
"value"は"--delete"のoption argumentです。

option argumentは"="指定できます。
"--delete=value"と"--delete value"は同じ意味です。

## getopt_long

```c
int getopt_long(
  int argc,
  char * const argv[],
  const char *optstring,
  const struct option *longopts,
  int *longindex
);

struct option {
  const char *name;
  int         has_arg; // no_argument or required_argument
  int        *flag;
  int         val;
};
```

argc, argv, optstringはgetoptと全く同じ意味です。
つまり**getopt_longはgetoptの機能をすべて含んでいます**。

longoptsとlongindexがlong option用の引数です。

longoptsはstruct optionの配列です。配列末尾はnull terminateされます。

getopt_longは見つけたlong optionに対応するlongopts配列のindexを
longindexに設定して返します。

次にlongoptsの例を示します。

```c
const struct option longopts[] = {
  //{    *name,           has_arg, *flag, val },
    {  "create",       no_argument,     0, 'c' },
    {  "delete", required_argument,     0, 'd' },
    { "setflag",       no_argument, &flag,  1  },
    { "clrflag",       no_argument, &flag,  0  },
    {         0,                 0,     0,  0  }, // termination
};
int longindex = 0;
```

nameはlong optionです "create"であれば"--create"を示します。
has_argはlong optionがoption argumentを持つかどうかを示します。

*flagが0であれば
nameが見つかるとgetopt_longはvalに指定した文字を返します。

*flagが0でなければ
nameが見つかるとgetopt_longは0を返します。
その時にflagのアドレスにvalで指定した値を設定します。

つまり上記例では
"--setflag"が指定されるとflag = 1
"--clrflag"が指定されるとflag = 0  となります。

## 例

```c:command.c
#include <stdio.h>
#include <getopt.h>

static void print_arg(int argc, char* argv[])
{
#if 0
    int i;
    for (i=0; i<argc; i++) {
        printf("argv[%d]=%s ", i, argv[i]);
    }
#endif
}

int main(int argc, char* argv[])
{
    int c, flag = 2;
    const char* optstring = "ab:" ; // optstringを定義します
    const struct option longopts[] = {
      //{    *name,           has_arg, *flag, val },
        {  "create",       no_argument,     0, 'c' },
        {  "delete", required_argument,     0, 'd' },
        { "setflag",       no_argument, &flag,  1  },
        { "clrflag",       no_argument, &flag,  0  },
        {         0,                 0,     0,  0  }, // termination
    };
    int longindex = 0;

    opterr = 0; // disable error log

    // non-option or end of argument list or error('?')までloop
    while ((c=getopt_long(argc, argv, optstring, longopts, &longindex)) != -1) {
        print_arg(argc, argv);
        if (c == 0 || c == 'c' || c == 'd') {
            const struct option* longopt = &longopts[longindex];
            printf("longopt=%s ", longopt->name);
            if (longopt->has_arg == required_argument) {
                printf("optarg=%s ", optarg);
            }
            if (longopt->flag) {
                printf("*flag=%d ", *longopt->flag);
            }
        } else {
            printf("opt=%c ", c);
            if (c == 'a') {
                // without option argument
            } else if (c == 'b') {
                // with option argument
                printf("optarg=%s ", optarg);
            } else {
                // '?'
                printf("optopt=%c ", optopt);
                if (optopt == 'b') {
                    // no option argument
                } else {
                    // unknown option character
                }
                printf("\n");
                return -1; // error
            }
        }
        printf("\n");
    }

    // non-option or end of argument list
    while (optind < argc) {
        print_arg(argc, argv);
        printf("non-opt=%s \n", argv[optind]);
        optind++;
    }
    return 0;
}
```

# reference
https://www.gnu.org/software/libc/manual/html_node/Getopt.html