monoの開発ブログ

mkdir -p のような関数

mkdir -pのように途中のディレクトリを作ってくれるmkdir_p関数をCで書きました。こんな基本的な処理のオレオレ実装を作るのはアレだと思うのですが、C言語を取り巻く環境がさっぱりわからないんですよね……。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

static int is_directory(const char *pathname) {
  struct stat st;
  int ret;
  ret = stat(pathname, &st);
  return ret == 0 && S_ISDIR(st.st_mode);
}

static int mkdir_(const char *pathname, mode_t mode) {
  int ret;

  ret = mkdir(pathname, mode);
  if (ret == 0) {
    return 0;
  }
  else {
    if (is_directory(pathname)) {
      return 0;
    }
    return -1;
  }
}

static int mkdir_p(const char *pathname, mode_t mode) {
  int ret, n;
  char *pathname_, *p;

  if (is_directory(pathname)) {
    return 0;
  }

  ret = mkdir_(pathname, mode);
  if (ret == 0) {
    return 0;
  }

  n = strlen(pathname);
  if (pathname[n - 1] == '/') {
    --n;
  }
  pathname_ = malloc(sizeof(char) * (n + 2));
  if (pathname_ == NULL) {
    return -1;
  }
  memcpy(pathname_, pathname, sizeof(char) * n);
  pathname_[n] = '/';
  pathname_[n + 1] = '\0';

  p = pathname_;
  if (*p == '/') {
    ++p;
  }
  while (p = strchr(p, '/')) {
    *p = '\0';

    ret = mkdir_(pathname_, mode);
    if (ret < 0) {
      break;
    }

    *p = '/';
    ++p;
  }

  free(pathname_);
  return ret;
}

int main() {
  int ret = mkdir_p("foo/bar/baz/", 0777);
  fprintf(stderr, "%d\n", ret);
  return ret;
}

パスの区切りである / を一時的に \0 に置き換えて各ディレクトリのパス文字列を作成し、順番にmkdirを試行しています。

それにしてもさっぱり意味の分からないコードですね……。Cつらい。