質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.32%

cp -r コマンドのC言語による実装

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 2,610

thoifon

score 10

前提・実現したいこと

ls -r コマンドのC言語のソースコードを参考にして
cp -r コマンドを実装しているのですが
どこを変えていいのかわかりません…

該当のソースコード

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

struct strbuf {
    char *ptr;
    size_t len;
};

void traverse(struct strbuf *buf);
void traverseSub(struct strbuf *buf, int first);
struct strbuf *strbuf_new(void);
void strbuf_realloc(struct strbuf *buf, size_t size);

void print_error(char *s){
    fprintf(stderr, "%s: %s\n", s, strerror(errno));
}

void die (const char *s){
    perror(s);
    exit(1);
}
int main(int argc, char *argv[]){
    struct strbuf *pathbuf;

    if(argc != 2){
        fprintf(stderr, "Usage: %s dir\n", argv[0]);
        exit(1);
    }
    pathbuf = strbuf_new();
    strbuf_realloc(pathbuf, strlen(argv[1]));
    strcpy(pathbuf->ptr, argv[1]);
    traverse(pathbuf);
    exit(0);
}

void traverse(struct strbuf *pathbuf){
    traverseSub(pathbuf, 1);
}

void traverseSub(struct strbuf *pathbuf, int first){
    DIR *d;
    struct dirent *ent;
    struct stat st;

    d = opendir(pathbuf->ptr);
    if (!d) {
        switch (errno){
            case ENOTDIR:
                return;
            case ENOENT:
                if(first){
                    die(pathbuf->ptr);
                } else {
                    return;
                }
            case EACCES:
                puts(pathbuf->ptr);
                print_error(pathbuf->ptr);
                return;
            default:
                perror(pathbuf->ptr);
                exit(1);
            }
        }
        puts(pathbuf->ptr);
        while(ent = readdir(d)){
            if(strcmp(ent->d_name, ".") == 0)
                continue;
            if(strcmp(ent->d_name, "..") == 0)
                continue;
            strbuf_realloc(pathbuf, pathbuf->len + 1 + strlen(ent->d_name) + 1);
            // special handling for the root directory 
            if(strcmp(pathbuf->ptr, "/") != 0){
                strcat(pathbuf->ptr, "/");
            }
            strcat(pathbuf->ptr, ent->d_name);
            if(lstat(pathbuf->ptr, &st) < 0){
                switch (errno){
                case ENOENT:
                    break;
                case EACCES:
                    print_error(pathbuf->ptr);
                    break;
                default:
                    die(pathbuf->ptr);
                }
            } else {
                if (S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode)) {
                    traverseSub(pathbuf, 0);
                } else {
                    puts(pathbuf->ptr);
                }
            }
            *strrchr(pathbuf->ptr, '/') = '\0';
        }
        closedir(d);
    }

#define INITLEN 1024

struct strbuf * strbuf_new(void){
    struct strbuf *buf;

    buf = (struct strbuf*)malloc(sizeof(struct strbuf));
    if (!buf) {
        die("malloc(3)");
    }
    buf->ptr = malloc(INITLEN);
    if(!buf->ptr){
        die("malloc(3)");
    }
    buf->len = INITLEN;
    return buf;
}

void strbuf_realloc(struct strbuf *buf, size_t len){
    char *tmp;

    if(buf->len > len)
        return;
    tmp = realloc(buf->ptr, len);
    if (!tmp) {
        die("realloc(3)");
    }
    buf->ptr = tmp;
    buf->len = len;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    2016/07/02 06:39

    こちらの質問が他のユーザから「やってほしいことだけを記載した丸投げの質問」という指摘を受けました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

  • 退会済みユーザー

    2016/07/19 16:23

    こちらの質問が他のユーザから「問題・課題が含まれていない質問」という指摘を受けました
    teratailでは、漠然とした興味から票を募るような質問や、意見の主張をすることを目的とした投稿は推奨していません。
    「編集」ボタンから編集を行い、質問の意図や解決したい課題を明確に記述していただくと回答が得られやすくなります。

  • 退会済みユーザー

    退会済みユーザー

    2016/08/15 00:17

    ソースコード中にコメントや、質問者ご自身の解釈を付記すれば、課題が浮かび上がると思います。

    キャンセル

回答 1

checkベストアンサー

0

既にお答えは必要ないかもしれませんが、と在る人のやり方です。

・ソースコードは ls -r を実行されると言う事で多分、ディレクトリ内をリカーシブルにリストするものと考えます。なのでディレクトリとファイル名が出力されます。
・目的のcp -rは、Linuxでの cp -R dir1 dir2と同様な動きをする事と考えます。(ディレクトリのコピー)
・だとすると、どこを変更するかと考えて、まずは全体の動きを見たいので、各サブルーチンにDEBUG文をprintfを最初と最後に入れます。
・実行結果からおおその動きが分かりますので、さらにファイルをcpする際に必要そうな値の場所にもprintf文を入れます。
・cpに必要な情報が揃ったところで、次にdirとfileかを判断してる箇所に、ディレクトリ作成、ファイルのコピーを行うサブルーチンを加えます。(mkdirやcpはソースを持って来てサブルーチン化します。)

プログラムを修正する場合、こんな感じでやっております。

サンプルですのでご参考としてください。

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

struct strbuf {
    char *ptr;
    size_t len;
};

void traverse(struct strbuf *buf);
void traverseSub(struct strbuf *buf, int first);
struct strbuf *strbuf_new(void);
void strbuf_realloc(struct strbuf *buf, size_t size);

void print_error(char *s){
    fprintf(stderr, "%s: %s\n", s, strerror(errno));
}

void die (const char *s){
    perror(s);
    exit(1);
}

char dir1[256];
char dir2[256];

int ccp(char *frfile,char *tofile)
{
        FILE* fpSrc;
        FILE* fpDest;
        char c;

        fpSrc = fopen( frfile, "rb" );
        if( fpSrc == NULL ){
                exit( EXIT_FAILURE );
        }

        fpDest = fopen( tofile, "wb" );
        if( fpDest == NULL ){
                exit( EXIT_FAILURE );
        }

        while( 1 ){
                /* バイナリデータとして 1Byteずつ読み込み、1Byteずつ書き込む */
                fread( &c, sizeof(c), 1, fpSrc );
                if( feof( fpSrc ) ){
                        break;
                }
                if( ferror( fpSrc ) ){
                        exit( EXIT_FAILURE );
                }
                fwrite( &c, sizeof(c), 1, fpDest );
        }

        if( fclose( fpDest ) == EOF ){
                fputs( "ファイルクローズに失敗しました。\n", stderr );
                exit( EXIT_FAILURE );
        }
        if( fclose( fpSrc ) == EOF ){
                fputs( "ファイルクローズに失敗しました。\n", stderr );
                exit( EXIT_FAILURE );
        }
        return 0;
}

int main(int argc, char *argv[]){
    struct strbuf *pathbuf;

    if(argc != 3){
        fprintf(stderr, "Usage:cpdir dir1 dir2\n");
        exit(1);
    }
    pathbuf = strbuf_new();
    strbuf_realloc(pathbuf, strlen(argv[1]));
    strcpy(pathbuf->ptr, argv[1]);
        strcpy(dir1, argv[1]);
        strcpy(dir2, argv[2]);
    traverse(pathbuf);
    exit(0);
}

void traverse(struct strbuf *pathbuf){
    traverseSub(pathbuf, 1);
}

void traverseSub(struct strbuf *pathbuf, int first){
    DIR *d;
    struct dirent *ent;
    struct stat st;
        char   to_name[2048];

        // printf("traverseSub S\n");
        if (first){
                strcpy(to_name,dir2);
        }else{
                strcpy(to_name,dir2);
                strcpy(to_name+strlen(dir2),pathbuf->ptr+strlen(dir1));
        }

    d = opendir(pathbuf->ptr);
    if (!d) {
        switch (errno){
            case ENOTDIR:
                return;
            case ENOENT:
                if(first){
                    die(pathbuf->ptr);
                } else {
                    return;
                }
            case EACCES:
                puts(pathbuf->ptr);
                print_error(pathbuf->ptr);
                return;
            default:
                perror(pathbuf->ptr);
                exit(1);
            }
        }
                //printf("Dir:%s \n", pathbuf->ptr);
                //printf("Dir2:%s \n", to_name);
        puts(pathbuf->ptr);
                if (mkdir(to_name,0755) == 0){
                        printf("%s mkdir\n", to_name);
                }
        while(ent = readdir(d)){
            if(strcmp(ent->d_name, ".") == 0)
                continue;
            if(strcmp(ent->d_name, "..") == 0)
                continue;
            strbuf_realloc(pathbuf, pathbuf->len + 1 + strlen(ent->d_name) + 1);
            // special handling for the root directory
            if(strcmp(pathbuf->ptr, "/") != 0){
                strcat(pathbuf->ptr, "/");
            }
            strcat(pathbuf->ptr, ent->d_name);
            if(lstat(pathbuf->ptr, &st) < 0){
                switch (errno){
                case ENOENT:
                    break;
                case EACCES:
                    print_error(pathbuf->ptr);
                    break;
                default:
                    die(pathbuf->ptr);
                }
            } else {
                if (S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode)) {
                    traverseSub(pathbuf, 0);
                } else {
                    puts(pathbuf->ptr);
                                        strcpy(to_name,dir2);
                                        strcpy(to_name+strlen(dir2),pathbuf->ptr+strlen(dir1));
                                        if (ccp(pathbuf->ptr, to_name)){
                                                printf("File:%s\n",to_name);
                                        }
                }
            }
            *strrchr(pathbuf->ptr, '/') = '\0';
        }
        closedir(d);
                //printf("traverseSub E\n");
}
#define INITLEN 1024

struct strbuf * strbuf_new(void){
    struct strbuf *buf;

    buf = (struct strbuf*)malloc(sizeof(struct strbuf));
    if (!buf) {
        die("malloc(3)");
    }
    buf->ptr = malloc(INITLEN);
    if(!buf->ptr){
        die("malloc(3)");
    }
    buf->len = INITLEN;
    return buf;
}

void strbuf_realloc(struct strbuf *buf, size_t len){
    char *tmp;

    if(buf->len > len)
        return;
    tmp = realloc(buf->ptr, len);
    if (!tmp) {
        die("realloc(3)");
    }
    buf->ptr = tmp;
    buf->len = len;
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る