rm(Remove)

rmコマンドに似せたものを作りました.
再帰を利用しています. 後に詳しく説明をします.

/* Pseudo rm command
 * Author: Jin
 * Ver: 1.0.3
 * ****************************************/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<dirent.h>
#include<fcntl.h>
#include<unistd.h>
#include<getopt.h>

static struct option long_options[] = {
    {"recursive", required_argument, 0, 'r'},
    {"help",      no_argument      , 0, 'h'},
    {"version",   no_argument      , 0, 'v'},
    {0, 0, 0, 0},
};

static char *name = "rm";

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

static void help(const char *path);
static void version(void);
static void do_remove(const char *path);
static void recursive_rm(const char *path);
static void rm_err(const char *path);

int main(int argc, char **argv)
{
    if(argc < 2) {
        help(argv[0]);
        exit(0);
    }

    int i;
    int opt, option_index = 0;
    int f_ver, f_help, f_rec;
    f_rec  = 0;
    f_help = 0;
    f_ver  = 0;

    while((opt = getopt_long(argc, argv, "vhrRia:", long_options, &option_index)) != EOF) {
        switch(opt) {
            case 'r':
                f_rec = 1;
                break;
            case 'R':
                f_rec = 1;
                break;
            case 'h':
                f_help = 1;
                break;
            case 'v':
                f_ver = 1;
                break;
            case '?':
                f_help = 1;
                break;
            default:
                f_help = 1;
                break;
        }
    }

    if(f_help || f_ver) {
        if(f_help) help(argv[0]);
        if(f_ver) version();
        exit(0);
    }

    else if(f_rec) {
        if(argc == 2)
            rm_err(name);
        for(i = 2; i < argc; i++) {
            recursive_rm(argv[i]);
        }
    }

    if(f_rec == 0 && f_help == 0 && f_ver == 0)
        for(i = 1; i < argc; i++)
            do_remove(argv[i]);

    return 0;
}

static void help(const char *path) {
    printf("Usage: %s [Option] [Filename]...\n", path);
    printf("ファイルを削除します.(ディレクトリは無効)\n\n");
    printf("\t-r, -R, --recursive: ディレクトリを含む全てを削除\n");
    printf("\t-h,          --help: ヘルプを表示\n");
    printf("\t-v,       --version: バージョンを表示\n\n");
    printf("%sではディレクトリを削除できません. ディレクトリを含め全てを削除したい場合は`--recursive`,(もしくは, -r -R)をオプションで指定してください.\n\n", name);
}

static void version(void) {
    printf("Version: 1.0.3\n");
}

static void do_remove(const char *path) {
    if(unlink(path) == EOF) {
        perror("unlink");
        exit(1);
    }
}

static void recursive_rm(const char *path) {
    char buff[128] = "\0";

    if(chdir(path) == EOF) {
        perror("Change Directory");
        exit(1);
    }
    if(getcwd(buff, 127) == NULL) {
        perror("getcwd");
        exit(1);
    }

    if(strlen(buff) > 127) {
        perror("Overname");
        exit(1);
    }

    DIR *opd;
    struct dirent *rddir;
    struct stat st;

    if((opd = opendir(buff)) == NULL) {
        perror("opendir");
        exit(1);
    }

    while((rddir = readdir(opd)) != NULL) {
        if(stat(rddir->d_name, &st) == EOF) {
            perror("Stat");
            closedir(opd);
            exit(1);
        }
        if(S_ISDIR(st.st_mode)) {
            if(rmdir(rddir->d_name) == EOF) {
                if(strcmp(rddir->d_name, ".") && strcmp(rddir->d_name, "..")) {
                    recursive_rm(rddir->d_name);
                }
            }
        }
        if(unlink(rddir->d_name) == EOF);
    }
    if(rmdir(buff) == EOF) {
        perror("rmdir");
    }

    if(closedir(opd) == EOF) {
        perror("Closedir");
        exit(1);
    }
}

static void rm_err(const char *path) {
    printf("%s: ファイル名を指定してください.\n", path);
    printf("詳しくは`rm --help`を実行してください.\n");
    exit(1);
}
                        

実行例:

$ ./rm --help
Usage: rm [Option] [Filename]...
ファイルを削除します.(ディレクトリは無効)

-r, -R, --recursive: ディレクトリを含む全てを削除
-h, --help: ヘルプを表示
-v, --version: バージョンを表示

rmではディレクトリを削除できません. ディレクトリを含め全てを削除したい場合は`--recursive`,(もしくは, -r -R)をオプションで指定してください.


Comment

個々を分別して解説していきます.

                            #include<getopt.h>

static struct option long_options[] = {
    {"recursive", required_argument, 0, 'r'},
    {"help",      no_argument      , 0, 'h'},
    {"version",   no_argument      , 0, 'v'},
    {0, 0, 0, 0},
};
includeの下の構造体の部分は, ロングオプションを指定させ, どのオプションに対応するかを決めます.
                            int opt, option_index = 0;
    int f_ver, f_help, f_rec;
    f_rec  = 0;
    f_help = 0;
    f_ver  = 0;

    while((opt = getopt_long(argc, argv, "vhrRia:", long_options, &option_index)) != EOF) {
        switch(opt) {
            case 'r':
                f_rec = 1;
                break;
            case 'R':
                f_rec = 1;
                break;
            case 'h':
                f_help = 1;
                break;
            case 'v':
                f_ver = 1;
                break;
            case '?':
                f_help = 1;
                break;
            default:
                f_help = 1;
                break;
        }
    }
    
    
int型のフラグ('f_*'です)に最初に0を代入させ, while文でオプションを終わるまで走査させます.
指定されたオプションのフラグに1を代入させる.
                            if(f_help || f_ver) {
        if(f_help) help(argv[0]);
        if(f_ver) version();
        exit(0);
    }

    else if(f_rec) {
        if(argc == 2)
            rm_err(name);
        for(i = 2; i < argc; i++) {
            recursive_rm(argv[i]);
        }
    }

    if(f_rec == 0 && f_help == 0 && f_ver == 0)
        for(i = 1; i < argc; i++)
            do_remove(argv[i]);
            
if文に1と0を入れるとそれに応じてくれるため, フラグに1と0を代入させました.
                            static void recursive_rm(const char *path) {
    char buff[128] = "\0";

    if(chdir(path) == EOF) {
        perror("Change Directory");
        exit(1);
    }
    if(getcwd(buff, 127) == NULL) {
        perror("getcwd");
        exit(1);
    }

    if(strlen(buff) > 127) {
        perror("Overname");
        exit(1);
    }

    DIR *opd;
    struct dirent *rddir;
    struct stat st;

    if((opd = opendir(buff)) == NULL) {
        perror("opendir");
        exit(1);
    }

    while((rddir = readdir(opd)) != NULL) {
        if(stat(rddir->d_name, &st) == EOF) {
            perror("Stat");
            closedir(opd);
            exit(1);
        }
        if(S_ISDIR(st.st_mode)) {
            if(rmdir(rddir->d_name) == EOF) {
                if(strcmp(rddir->d_name, ".") && strcmp(rddir->d_name, "..")) {
                    recursive_rm(rddir->d_name);
                }
            }
        }
        if(unlink(rddir->d_name) == EOF);
    }
    if(rmdir(buff) == EOF) {
        perror("rmdir");
    }

    if(closedir(opd) == EOF) {
        perror("Closedir");
        exit(1);
    }
}
一番面倒なのがこの部分です.
opendir, readdir, chdirを省きます.
システムコールのstatでreaddirで読み込んだファイルの種類を判別させます.
stat.hでディレクトリかレギュラーファイルかを判別するマクロが定義されているのでそれを利用し, ディレクトリだけを判断させます.
もし, ディレクトリが削除出来なければ(rmdir), 削除する権限がないか, ディレクトリ内に他のファイルやディレクトリがあると判断できる.
そのため, rmdir == EOF(システムコールは大体エラーの場合は-1か負の数を返す.)でありかつ, "..", "."でない場合のみにもう一度この関数に再帰して同じ処理をさせる.