Mono.Unix.Native.Syscall.readdir_rを使ってディレクトリを再帰的に走査する。
// gmcs DirectoryEnumeration.cs /r:Mono.Posix.dll
// mono DirectoryEnumeration.exe ./
using System;
using Mono.Unix.Native;
public class DirectoryEnumeration {
private enum DirentType : byte {
DT_UNKNOWN = 0,
DT_FIFO = 1,
DT_CHR = 2,
DT_DIR = 4,
DT_BLK = 6,
DT_REG = 8,
DT_LNK = 10,
DT_SOCK = 12,
DT_WHT = 14
};
static void Main(string[] args)
{
Enumerate(args[0], 0);
}
private static void Enumerate(string dir, int depth)
{
IntPtr dirp = IntPtr.Zero;
string indent = new string(' ', depth);
try
{
dirp = Syscall.opendir(dir);
// ディレクトリを開けなかった場合
if (IntPtr.Zero.Equals(dirp)) return;
for (; ; )
{
IntPtr result;
Dirent dirent = new Dirent();
if (0 != Syscall.readdir_r(dirp, dirent, out result)) break;
// すべてのエントリを読んだ場合
if (IntPtr.Zero.Equals(result)) break;
// 削除されたエントリだった場合
if (0 == dirent.d_ino) continue;
// エントリの種類を調べる
switch((DirentType)dirent.d_type)
{
case DirentType.DT_DIR:
// ディレクトリ
Console.WriteLine("{0}{1}/", indent, dirent.d_name);
if ("." == dirent.d_name)
{
// 自分自身
}
else if (".." == dirent.d_name)
{
// 親ディレクトリ
}
else
{
string subdir = dir;
if (!subdir.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) subdir += System.IO.Path.DirectorySeparatorChar;
subdir += dirent.d_name;
// 子ディレクトリを再帰的に走査する
Enumerate(subdir, depth + 1);
}
break;
case DirentType.DT_REG:
// 普通のファイル
Console.WriteLine("{0}{1}", indent, dirent.d_name);
break;
default:
// それ以外(シンボリックリンク、ブロックデバイス、ソケット等)
Console.WriteLine("{0}{1}", indent, dirent.d_name);
break;
}
}
}
finally
{
if (!IntPtr.Zero.Equals(dirp))
{
Syscall.closedir(dirp);
}
}
}
}