ファイル入出力に現れるminGWとCygwinの設計思想の違い

Windows環境で書かれたC言語のプログラムをレビューする[^1]機会があり、そこでファイル入出力に関しての面白い知見が得られたので記録を残しておく。

[^1]: 要はTAとして採点作業しただけである。ものは言いよう。

# 前提

- 筆者自身はMacユーザであり、Windowsには明るくない

# 環境

- Parallels Desktop 15上の仮想環境として動作させたWindows 10 Pro (Ver. 1909)
- MSYS2 (20200602.0.0)
- Cygwin (3.1.5)
- 上記2つはchocolatey経由でインストール

# TL;DR

MinGWはWindows環境の実装としてファイル入出力時にテキストモードとバイナリモードを区別する一方で、CygwinはLinux環境の再現としてファイル入出力時に2つのモードを区別しない。

# ファイル入出力時の動作の違い

- 検証用のコード

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

int main(int argc, char **argv) {
  int count = 0;
  char c;
  FILE *fp;

  int binary_mode = 0;
  if (argc > 1 && strcmp(argv[1], "-b") == 0) {
    binary_mode++; // "-b"オプションでバイナリモードでのファイル読み込みを指定
  }
  
  int i;
  for (i = 1 + binary_mode; i < argc; i++) {
    if ((fp = fopen(argv[i], (binary_mode > 0) ? "rb" : "r")) == NULL) {
      printf("Error reading file");
      return 1;
    }
    
    count = 0;
    while ((c = fgetc(fp)) != EOF) {
      count++;
      if (c == '\r') {
        printf("%6d: <CR>\n", count);
      } else if (c == '\n') {
        printf("%6d: <LF>\n", count);
      } else {
        printf("%6d: %c\n", count, c);
      }
    }
  
    fclose(fp);

    printf("[%s] contains %d characters\n", argv[i], count);
  }
  
  return 0;
}
```

- 検証用ファイル

`<CR>`はキャリッジリターン（0x0D）、`<LF>`はラインフィード（0x0A）を表す。

```:cr.txt
CR<CR>
```

```lf.txt
LF<LF>
```

```crlf.txt
CRLF<CR><LF>
```

## MinGW（MSYS2）上の挙動

```
$ gcc -o test-mingw test.c
$ ./test-mingw.exe cr.txt lf.txt crlf.txt
     1: C
     2: R
     3: <CR>
[cr.txt] contains 3 characters
     1: L
     2: F
     3: <LF>
[lf.txt] contains 3 characters
     1: C
     2: R
     3: L
     4: F
     5: <LF>
[crlf.txt] contains 5 characters

$ ./test-mingw.exe -b cr.txt lf.txt crlf.txt
     1: C
     2: R
     3: <CR>
[cr.txt] contains 3 characters
     1: L
     2: F
     3: <LF>
[lf.txt] contains 3 characters
     1: C
     2: R
     3: L
     4: F
     5: <CR>
     6: <LF>
[crlf.txt] contains 6 characters
```

MinGWではファイル読み込みにテキストモードを指定した場合とバイナリモードを指定した場合で結果が異なる。
テキストモードを指定した場合、`<CR>`単体や`<LF>`単体はそのまま1文字として読み込まれる一方で、`<CR><LF>`は`<LF>`1文字に変換されて読み込まれる。

## Cygwin上の挙動

```
$ gcc -o test-cygwin test.c
$ ./test-cygwin.exe cr.txt lf.txt crlf.txt
     1: C
     2: R
     3: <CR>
[cr.txt] contains 3 characters
     1: L
     2: F
     3: <LF>
[lf.txt] contains 3 characters
     1: C
     2: R
     3: L
     4: F
     5: <CR>
     6: <LF>
[crlf.txt] contains 6 characters

$ ./test-cygwin.exe -b cr.txt lf.txt crlf.txt
     1: C
     2: R
     3: <CR>
[cr.txt] contains 3 characters
     1: L
     2: F
     3: <LF>
[lf.txt] contains 3 characters
     1: C
     2: R
     3: L
     4: F
     5: <CR>
     6: <LF>
[crlf.txt] contains 6 characters
```

テキストモードかバイナリモードかにかかわらず、1バイト単位で文字が読み込まれる。

# MinGWとCygwinの設計思想

[MinGWのWebサイト](http://www.mingw.org/)には以下の記述がある：
> MinGW, a contraction of "Minimalist GNU for Windows", is a minimalist development environment for native Microsoft Windows applications.
> （中略）
> **MinGW**, being Minimalist, **does not, and never will, attempt to provide a POSIX runtime environment for POSIX application deployment on MS-Windows.**

つまり、MinGWはあくまでもWindowsネイティブアプリケーションのための開発環境を提供しているため、Windows特有のファイルモードに対応しているといえる。

一方で、[CygwinのWebサイト](https://www.cygwin.com/)には以下の記述がある：
> Cygwin is:
> 
> - a large collection of GNU and Open Source tools which provide functionality similar to a Linux distribution on Windows.

したがって、CygwinはLinux環境をWindows上で再現するものであるため、Windows特有のファイルモードに対応せずにLinux環境同様すべてのファイルを一元的に扱っているといえる。

# 参考文献

[初心者のためのポイント学習Ｃ言語 第17章 ファイル入出力](http://www9.plala.or.jp/sgwr-t/c/sec17.html)