C言語 envpからPATHの値を配列に格納する

myshell を作ってみる機会があって、その時書いたものをまとめます。

# やりたいこと
実行ファイルに渡される環境変数に入っている、コマンドの在り処を `char *` 型の配列に入れます。

# はじめに
環境変数が入っている `envp[]` はどんな状態なのかを調べました。

```show_all.c
int show_all(char *envp[])
{
  while(*envp)
      printf("%s\n",*envp++);

  return 0;
}
```

こんな感じの関数を実行すると、 `envp[18]` に次のような値が入っていました。


```logos
PATH=/Library/Frameworks/Python.framework/Versions/3.5/bin:/Users/rild/.pyenv/bin:/Users/rild/.rbenv/shims:/opt/local/bin:/opt/local/sbin:/opt/local/bin:/opt/local/sbin:/Users/rild/.nvm/versions/node/v5.5.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/TeX/texbin:/Library/Frameworks/Python.framework/Versions/3.5/bin:/Users/rild/.pyenv/bin:/Users/rild/.rbenv/shims:/opt/local/bin:/opt/local/sbin:/Users/rild/.sdkman/candidates/kotlin/current/bin:/Users/rild/.sdkman/candidates/gradle/current/bin:/Users/rild/Library/Android/sdk/platform-tools:/Users/rild/Documents/android/apktool.jar:/usr/local/share/python:/Users/rild/Library/Android/sdk/platform-tools:/Users/rild/Documents/android/apktool.jar:/usr/local/share/python
```


ここから、 `:` で区切られた、コマンドのパスを取り出したかったのです。

# 目標
こんな感じにします。

```logos
path[0]=/Library/Frameworks/Python.framework/Versions/3.5/bin
path[1]=/Users/rild/.pyenv/bin
path[2]=/Users/rild/.rbenv/shims
path[3]=/opt/local/bin
path[4]=/opt/local/sbin
path[5]=/opt/local/bin
path[6]=/opt/local/sbin
path[7]=/Users/rild/.nvm/versions/node/v5.5.0/bin
path[8]=/usr/local/bin
path[9]=/usr/bin
path[10]=/bin
path[11]=/usr/sbin
path[12]=/sbin
path[13]=/opt/X11/bin
path[14]=/Library/TeX/texbin
path[15]=/Library/Frameworks/Python.framework/Versions/3.5/bin
path[16]=/Users/rild/.pyenv/bin
path[17]=/Users/rild/.rbenv/shims
path[18]=/opt/local/bin
path[19]=/opt/local/sbin
path[20]=/Users/rild/.sdkman/candidates/kotlin/current/bin
path[21]=/Users/rild/.sdkman/candidates/gradle/current/bin
path[22]=/Users/rild/Library/Android/sdk/platform-tools
path[23]=/Users/rild/Documents/android/apktool.jar
path[24]=/usr/local/share/python
path[25]=/Users/rild/Library/Android/sdk/platform-tools
path[26]=/Users/rild/Documents/android/apktool.jar
path[27]=/usr/local/share/python
```

# コード

```envp.c
#include <string.h>
#include <stdio.h>

// split funtion with `:`---> start
int iscolon(char p)
{
  return p == ':';
}

void
split_wthcolon(int *path_c, char *path[], char *p)
{
    *path_c = 0;

    for(;;) {
      while (iscolon(*p))
          p++;
      if (*p == '\0') return;
      path[(*path_c)++] = p;

      while (*p && !iscolon(*p)) {
        p++;
      }
      if (*p == '\0')
        return;
      *p++ = '\0';
    }
}
// split funtion <--- end

// search "PATH=" index --->
int get_path_index(char *envp[])
{
  int envc = 0;

  // find char * pointing "PATH=..."
  while(*envp) {
    if (!strncmp(*envp, "PATH=", 5)) {
      return envc;
    }
    envc++;
    envp++;
  }
  return 0;
}
// search "PATH=" index <--- end

// split to path array
int get_path_from_buf(int * path_c, char *path[], char *buf)
{
  int i;
  for (i = 0; i < 5; i++) buf++; // rm "PATH="
  split_wthcolon(path_c, path, buf);

  return 0;
}

int main(int argc, char *argv[], char *envp[])
{
  char buf[1024]; // PATH contains '762 words'

  strcpy(buf, envp[get_path_index(envp)]);
  fprintf(stderr, "%d\n", get_path_index(envp)); // 18
  fprintf(stderr, "%s\n", buf); // envp[18]

  char *path[32]; // PATH contatins '27' colon ':'
  int path_c; // path count
  get_path_from_buf(&path_c, path, buf);

  int i;
  for (i = 0; i < path_c; i++) {
    fprintf(stderr, "%s\n", path[i]);
  }

  return 0;
}
```

最初、 `get_path_from_buf` に直接 `envp[get_path_index(envp)]` を渡していたのですが、中身が書き換わってしまうため、値をコピーして引数に渡してみました。

# 最後に
`for (i = 0; i < 5; i++) buf++; // rm "PATH="` のあたりとかすごくいけてないので、もっといい方法があるはず。。。

# 参考
- [strncmp function](http://www9.plala.or.jp/sgwr-t/lib/strncmp.html)

# 追記
`getenv`関数にPATHを指定すれば、get_path_indexとかはいらなかったですね。
http://www.c-lang.net/general29/index.html