C言語でrmコマンドを書きたい

■要求定義
– ファイルやフォルダを削除する
– リクルーシブの削除も対応したい

■考えた事
– remove()と_unlink()があるらしい
– remove関数は、厳密には「その名前でのファイルへのアクセスを、再びファイルを生成しない限り、不可能にする」という処理を行なっているらしい

int main(){

	// char *dir = "./newfolder";
	char *dir = "./hoge.txt";
	if(remove(dir) == 0){
		printf("%sの削除が完了しました。\n", dir);
	} else {
		printf("%sの削除に失敗しました。\n", dir);
	}
	return 0;
}

$ ./dev
./hoge.txtの削除が完了しました。

*dirをファイルがあるフォルダに変更するとremoveでは削除できない
$ ./dev
./newfolderの削除に失敗しました。

->ディレクトリ内のファイルを削除してからディレクトリを削除するというステップが必要

■フォルダの中のファイルリスト一覧表示

#include <stdio.h>
#include <dirent.h>

int main(){

	DIR *dir;
	struct dirent *dp;
	char dirpath[] = "./newfolder";

	dir = opendir(dirpath);
	if(dir == NULL) { return 1; }

	dp = readdir(dir);
	while(dp != NULL){
		printf("%s\n", dp->d_name);
		dp = readdir(dir);
	}

	if(dir != NULL) {
		closedir(dir);
	}
	return 0;
}

$ ./dev
.
foo.c
..
hoge.c

これをremove()と組み合わせれば再帰的に削除できるか。

#include <stdio.h>
#include <string.h>
#include <dirent.h>

int main(){

	DIR *dir;
	char newdir[255] = "./newfolder/";

	char *target = "./newfolder";
	if(remove(target) == 0){
		printf("%sの削除が完了しました。\n", target);
	} else {

		struct dirent *dp;
		dir = opendir(target);
		if(dir == NULL) { return 1; }

		dp = readdir(dir);
		while(dp != NULL){
			// printf("%s\n", dp->d_name);
			strcat(newdir,dp->d_name);
			printf("%s\n", newdir);
			remove(newdir);
			dp = readdir(dir);
		}
		if(dir != NULL) {
			closedir(dir);
		}
		remove(target);
		printf("%sの削除が完了!\n", target);
	}

	
	return 0;
}

$ ./dev
./newfolder/.
./newfolder/.foo.c
./newfolder/.foo.c..
./newfolder/.foo.c..hoge.c
./newfolderの削除が完了!

strcatで文字列連結するのがうまくいってない。何故だろう。

C言語でrmdirコマンドを書きたい

■要求定義
– 空のフォルダを削除する

#include <stdio.h>
#include <sys/stat.h>

int main(){

	char *dir = "./newfolder";
	rmdir(dir);
		
	return 0;
}

$ ./dev

フォルダの中にファイルが入っているときは、rmdirでは削除されない。

linuxコマンドとcの標準ライブラリ関数がネーミングが同じ場合は面白みがないな。

C言語でtouchコマンドを書きたい

■要求定義
– ファイルを作成する

■検討
– touchはファイルの中身は書かないから、fopen, fcloseだけで良いか?

#include <stdio.h>

int main(){

	FILE *fp;
	fp = fopen("test.txt", "w");
	fclose(fp);
		
	return 0;
}

$ ./dev

■追加
– コマンドラインで入力した値のファイルを作成する

#include <stdio.h>

int main(){

	char ss[256];
    gets(ss);

	FILE *fp;
	fp = fopen(ss, "w");
	fclose(fp);
		
	return 0;
}

$ ./dev
hoge.txt

できました。

fopenの拡張子はテキストファイル(.dat, .txt, .c, .h)、バイナリファイル(.exe, .jpg, .gif, .mpeg)どちらも可

なるほど、C言語はバイナリの概念がかなり出てくるな。。

C言語でclearコマンドを書きたい

■要求定義
– 消さずに画面を綺麗にする

■検討
– windowsでsystem(“cls”); で消せると書いてある。嫌な予感がするが、bionicでも動く?

#include 
#include 

int main(){

	for(int idx=0; idx<10; idx++){
    	printf("%d\n", idx);
  	}

	system("clear");
		
	return 0;
}

0
1
2
3
4
5
6
7
8
9
sh: 1: cls: not found

やっぱり。DOS/Windowsコマンドらしく、linuxには無いとのこと。

int main(){

	for(int idx=0; idx<10; idx++){
    	printf("%d\n", idx);
  	}

	system("clear");
		
	return 0;
}

これなら動くけど意味ないね。

エスケープシーケンシーとはターミナルを制御する制御文字
printf("\033[2J") //画面クリア
などがある。

system("\033[2J");


う〜ん、clearコマンドとはちょっと違うな。

C言語でcpコマンドを書きたい

■要求定義
– ファイルやフォルダをコピーする
– cp (移動元ファイル名/フォルダ名) (移動先ファイル名/フォルダ名) 

#include <stdio.h>
#include <stdlib.h> //汎用ライブラリ
#include <string.h>

int main(){

	FILE *fpSrc, *fpDes, *fp;
	char ss[256], ch[50];

	fpSrc = fopen("test.txt", "r");
	fpDes = fopen("copy.txt", "w");

	while(fgets(&ss, 256, fpSrc) != NULL){
		printf("%s", ss);
		fwrite(ss, 1, 8, fpDes);
	}

	fclose(fpSrc);
	fclose(fpDes);

	printf("コピーしました。\n");
		
	return 0;
}

$ ./dev
asdf
1234コピーしました。

■考察
あれ、コピー先のデータはバイナリデータで入ってるな。
調べてみると、fwriteはバイナリデータの書き込みとのこと。
fwriteではなく、fputs()に変更します。

int main(){

	FILE *fpSrc, *fpDes;
	char ss[256];

	fpSrc = fopen("test.txt", "r");
	fpDes = fopen("copy.txt", "w");

	while(fgets(&ss, 256, fpSrc) != NULL){
		printf("%s", ss);
		fputs(ss, fpDes);
	}

	fclose(fpSrc);
	fclose(fpDes);

	printf("コピーしました。\n");
		
	return 0;
}

コピーできました。
バイナリとは2進数データのこと。

C言語でmvコマンドを書きたい

■要求定義
– ファイルを移動させる

■ステップ1
– 標準ライブラリ関数であるrename関数を使う

#include <stdio.h>

int main(){

	rename("./test.c", "./hoge/test.c");
	printf("移動しました。\n");
		
	return 0;
}

$ ./dev
移動しました。

移動できた事を確認。

■ステップ2
– ユーザ入力で$ mv ${FileName} ${MoveDir}となるので、コマンドラインからの入力を元に移動させたい
– 文字列の分割は”strtok”を使う。ポインタでファイル名s1, 異動先s2を指定する。

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

int main(){

	char ss[256] = "./test.c ./hoge/test.c";
	char *s1, *s2;
	s1 = strtok(ss, " ");
	s2 = strtok(NULL, " ");
	rename(s1, s2);	
	printf("移動しました。\n");
		
	return 0;
}

■ステップ3
– gets()でコマンドラインから入力した値を取得して移動させたい

int main(){

	// char ss[256] = "./test.c ./hoge/test.c";
	char ss[256];
	char *s1, *s2;
	
	gets(ss);
	s1 = strtok(ss, " ");
	s2 = strtok(NULL, " ");
	rename(s1, s2);	
	printf("移動しました。\n");
		
	return 0;
}

$ ./dev
./test.c ./hoge/test.c
移動しました。

ほう、面白い。コマンドラインからの入力は、strtokで分割して処理すれば良いですな。

C言語でcdコマンドを書きたい

■要求定義
– ユーザ名の入力に合わせてカレントディレクトリを移動する

■検討事項
ディレクトリ一覧を取得し、ユーザ入力と一致したら移動か
+ chdirでディレクトリを変更

#include <stdio.h>
#include <string.h> // 文字列操作
#include <unistd.h> // standard symbolic constants and types

int main(){

	char pathname[512];
	memset(pathname, '\0', 512); // memsetはバイトメモリブロックのセット

	char path[64] = "./hoge";
	chdir(path);
	getcwd(pathname, 512); // getcwdはカレントディレクトリ
	printf("%s\n",pathname);
		
	return 0;
}

$ pwd
/home/vagrant/dev
$ ./dev
/home/vagrant/dev/hoge

cだと、chdir(path)でディレクトリを変更できる

C言語でlsコマンドを書きたい

■要求定義
– まずはファイル名一覧表示
– オプションは後から考える

■検討した事
標準関数でファイル名一覧の取得関数はないらしい

#include <stdio.h>
#include <dirent.h> // format of directory entries

int main(){
	DIR *dir;
	struct dirent *dp; // 構造体
	char path[64] = "./";

	dir=opendir(path); // opendirはディレクトリオープン、 dirent.hが必要

	for(dp=readdir(dir);dp!=NULL;dp=readdir(dir)){ // readdirはディレクトリ読み込み
		printf("%s\n",dp->d_name);
	}
		
	return 0;
}

$ ./dev
.
dev
hello.c
..

forループでdp=readdir(dir);dp!=NULL;dp=readdir(dir)の書き方がよくわからんな。

C言語でログイン処理を書きたい

さて、C言語の基礎を写経したので、ここから少しずつC言語を使って緩くOSを学んでいきたい
まずmac, windowsにもあるログイン処理から

■要求定義
– ユーザ名に一致したパスワードを入力し、一致していたらログイン
– 不一致の場合は、パスワードを再入力してくださいと表示

#include <stdio.h>
#define User "hpscript"
#define Password "asdf"

int main(){
	char s[5];
	
	while(1){
		printf("%sさん、パスワードを入力してください。\n", User);
			gets(s);
		if(strcmp(s, Password)==0){
			printf("ログイン成功しました。\n");
			break;
		} else {
			printf("%sさん、パスワードを入力してください。\n", s);
		}
	}
	
	return 0;
}

$ ./dev
hpscriptさん、パスワードを入力してください。
aaaa
aaaaさん、パスワードを入力してください。
hpscriptさん、パスワードを入力してください。
asdf
ログイン成功しました。

文字列の比較は、strcmpを使用する
if(s == Password){} としても、trueにならない。
while(1)で無限ループにして、一致した場合はbreakでループから抜ける。

なるほど、型の宣言するところとか、色々頭使うなー