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でループから抜ける。

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

記憶装置の原理

PCが記憶装置を制御している
– データ授受の為の情報通路: データ線、データバスと呼ばれる
– 読み出し・書込みの制御信号: R/Wを通告する
– 記憶装置のどの位置を選択するかのアドレス線

基本記憶素子をメモリセルという
主記憶装置の構造は
1. アドレス選択部
2. 制御部
3. 記憶部: 一つのアドレスに1バイト

RAM
L SRAM: 高速アクセス、フリップフロップ、バイポーラ型、MOS型のトランジスタが用いられている
L DRAM: コンデンサに電荷を蓄える
フラッシュメモリ
 大容量で書き換え可能回数の多い
キャッシュメモリ
 主記憶装置とCPUの間に小容量の高速メモリを入れる

プロセッサ基本機能

コンピュータの構成要素は、入力部、記憶部、演算部、出力部、制御部からなる。
一般的に演算部と制御部をまとめたものをプロセッサとして実現する
機械語がプロセッサを実行する

### 汎用プロセッサ
CPU
1チップで実現されたプロセッサをマイクロプロセッサという
例えば、通信制御プロセッサやデータベースプロセッサなど

### 専用コンピュータ
用途に合わせてプロセッサ自体も設計する

画像処理用プロセッサ…GPU
デジタル信号処理用プロセッサ…DSP
LSI設計用プロセッサ…CAD

命令実行部の構成回路

Program Counter
– 実行するプログラムの主記憶装置の先頭アドレス
– 命令はInstruction Registerで保持される
レジスタ群
– MARにデータアドレスが入れられ、それによって主記憶装置上のデータから読み出す
– データはレジスタに入れられる
Decoder
– 命令を解読する
– 選択された信号を制御部に渡す
アドレス計算回路
– 演算実行対象となるデータの読み出しのアドレスを計算して求める場合がある


演算実行部はデータに対する演算加工を実行する
クロック回路はクロック信号が発生するごとに動作を一つ一つ進めていく

-構成回路間の接続
各回路間はデータや制御信号の受け渡しのために接続される
直接接続と、バス線を設け、これに各回路を接続するバス回路方式がある

### アーキテクチャー
CPUアーキテクチャ
– 命令セット
命令語の構成、形式、種別、数の表現方法、オペランドの指定方法などを定める
– システム制御
割り込み方式、システムを管理するためのアクセス権利設定などを定める
– プロセッサ内部構造
回路構成および高速化のための制御方式の設計

メモリアーキテクチャ
主記憶がもつ物理的容量を超えた領域を含めたアドレスをどのように指定制御するか

入出力アーキテクチャ
入出力動作に関する命令又は司令をどのような方式で行うか

### 命令
命令の形式には命令部と一つ以上のオペランド部から構成される
– 命令部 + R1 + R2 : Add , 1, 2
– 命令部 + R + メモリアドレス : Add, 1, 2のメモリアドレス
– 命令部 + データ + メモリアドレス
– 命令部 + メモリアドレス1 + メモリアドレス2 : Add, 1のメモリアドレス, 2のメモリアドレス
– 命令部のみ
直接アドレス指定、間接アドレス指定(ポインタ)があある
命令、アドレスモード(AM)、オペランド1、オペランド2

### 動作の流れ
プロセッサの処理は予め用意されたプログラムによって処理される
プログラムカウンタ(PC)からプログラムを命令を読み出し、命令レジスタ(IR)に入れられる
命令デコーダ(DEC)で解読を行い、アドレス計算回路でレジスタもしくはデータに命令を出す
レジスタから演算回路を使用し、演算回路で演算を実行する
演算結果はレジスタもしくはメモリに返される

linuxの物理プロセッサーの表示
vagrant@ubuntu-bionic:~/dev$ grep physical.id /proc/cpuinfo | sort -u | wc -l
1

-命令の概要
データ転送、演算、ストリング操作、ビット操作、プログラム転送、プロセッサコントロール

なるほど、メモリ上のプログラムから、プロセッサを通じて演算処理が行われていることは理解しました。

C言語のファイル入出力

ファイル処理の手順は、ファールオープン->ファイルの読み書き->ファイルクローズとなる。

int main(void){

	FILE *fp;
	int ch;

	fp = fopen("myfile1.txt", "r");
	if (fp == NULL) return 1;

	ch = fgetc(fp);
	printf("ch=%c\n", ch);

	fclose(fp);

	return 0;
}

$ touch myfile1.txt
$ sudo vi myfile1.txt
$ ./dev
ch=a

ファイルが正常にオープンされると、ポインタを返す
FILE *fi, *foが良く使われる
入出力データの処理を効率的に行う為、ストリームという概念を使用する(stdin, stdout, stderr)
fp = fopen(“myfile1.txt”, “r”); と記述する

### fopen関数のオープンモード
r: read
w: write
a: apend
rb: binary fileのread
wb: binary fileのwrite
ab: binary fileのappend
※バイナリファイルとは自動変換を行わないこと

### ファイル入出力関数
fputs(), fgetc(), fputs(), fgets(), fprintf(), fscanf()

int main(void){

	FILE *fp;
	char ss[256];

	if((fp=fopen("myfile1.txt", "r")) == NULL){
		printf("file cannot open.\n");
		return 1;
	}

	while (fgets(ss, 256, fp) != NULL){
		printf("%s", ss);
	}

	fclose(fp);

	return 0;
}

### ファイル出力

int main(void){

	FILE *fp;
	int dt = 1234;

	if((fp=fopen("myfile1.txt", "w")) == NULL){
		printf("file cannot open.\n");
		return 1;
	}

	fputs("char output\n", fp);
	fputc('A', fp);
	fputc('\n', fp);
	fprintf(fp, "dt=%d\n", dt);

	fclose(fp);

	return 0;
}

ファイルコピー

int main(void){

	FILE *fin, *fout;
	char infile[80], outfile[80], ss[256];

	printf("inputfile="); gets(infile);
	printf("outputfile="); gets(outfile);

	if((fin=fopen(infile, "r")) == NULL){
		printf("cannot open input file.\n");
		return 1;
	}
	if((fin=fopen(outfile, "w")) == NULL){
		printf("cannot open output file.\n");
		return 1;
	}

	while(fgets(ss, 256, fin) != NULL){
		fputs(ss, fout);
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

### コマンドラインから引数を渡す

int main(int argc, char *argv[]){

	FILE *fin, *fout;
	char ss[256];

	if(argc != 3){
		printf("引数の数が異なります\n");
		return 1;
	}

	if((fin=fopen(argv[1], "r")) == NULL){
		printf("cannot open inputfile\n");
		return 1;
	}
	if((fin=fopen(argv[2], "w")) == NULL){
		printf("cannot open outputfile\n");
		return 1;
	}

	while (fgets(ss, 256, fin) != NULL){
		fputs(ss, fout);
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

argv[0]はコマンド名そのもの。
argcは全ての引数、argv[]は対応する引数

int main(int argc, char *argv[]){

	if(argc != 3) return 1;
	printf("argc =%d\n", argc);
	printf("argv[0] =%s\n", argv[0]);
	printf("argv[1] =%s\n", argv[1]);
	printf("argv[2] =%s\n", argv[2]);

	return 0;
}

C言語の標準ライブラリ関数

– 全て覚える必要はなく、使える関数だけ選択し、慣れるにしたがって利用関数を増やしていく
– ソースプログラム -> コンパイル -> オブジェクトモジュール + [標準ライブラリ] -> リンク -> 実行モジュール
– (1)ハードウェアにアクセスする関数、(2)数学処理、(3)一般ユーザでも記述可能な処理、(4)メモリ処理のような特殊動作関数 がある。(1)(2)(4)の独自開発は負担が大きい。

### 標準ライブラリ関数一覧
stdio.h: 入出力処理
stdlib.h: 一般ユーティリティ、数値変換や記憶割り当てなどサポート
string.h: 文字列操作
ctype.h: 文字操作
math.h: 数学処理
time.h: 時間処理

### 入出力関数 stdio.h
https://en.wikipedia.org/wiki/C_file_input/output

### 一般ユーティリティ関数 stdlib.h
abort, abs, atexit, atof, atoi, atol, bsearch, calloc, div, exit, free, getenv, labs, ldiv, malloc, qsort, rand, realloc, srand, strtod, strtol, strtoul, systemなど

### 文字列処理関数 string.h
https://en.wikipedia.org/wiki/C_string_handling

int main(void){

	char *p, s1[80], s2[80], s3[] = "abcdefg";
	int n;

	strcpy(s1, "ABC");
	strcpy(s2, "XYZ");
	printf("s1=%s s2=%s\n", s1, s2);

	n = strlen(s1);
	printf("s1 length=%d\n", n);

	if(strcmp(s1, s2) > 0)
		printf("left is large\n");
	if(strcmp(s1, s2) < 0)
		printf("right is large\n");
	if(strcmp(s1, s2) == 0)
		printf("equal\n");
	
	strcat(s1, "1234");
	strcat(s1, s2);
	strncat(s1, s3, 4);
	printf("文字連結=%s\n", s1);

	p = strchr(s3, 'c');
	puts(p);
	p = strstr(s3, "def");
	puts(p);

	return 0;
}
&#91;/code&#93;

### 文字処理関数 ctype.h
<a href="https://en.wikipedia.org/wiki/C_character_classification">https://en.wikipedia.org/wiki/C_character_classification</a>

int main(void){

	int c, c2;

	printf("------isalnum:\n");
	for (c=0; c<=127; c++)
		if(isalnum(c)) putchar(c);
	
	printf("------isalpha:\n");
	for (c=0; c<=127; c++)
		if(isalpha(c)) putchar(c);

	printf("------isdigit:\n");
	for (c=0; c<=127; c++)
		if(isdigit(c)) putchar(c);

	printf("------islower:\n");
	for (c=0; c<=127; c++)
		if(islower(c)) putchar(c);

	printf("------isupper:\n");
	for (c=0; c<=127; c++)
		if(isupper(c)) putchar(c);

	printf("------isxdigit:\n");
	for (c=0; c<=127; c++)
		if(isxdigit(c)) putchar(c);

	printf("------isprint:\n");
	for (c=0; c<=127; c++)
		if(isprint(c)) putchar(c);

	printf("------isgraph:\n");
	for (c=0; c<=127; c++)
		if(isgraph(c)) putchar(c);

	printf("------ispunct:\n");
	for (c=0; c<=127; c++)
		if(ispunct(c)) putchar(c);

	printf("------iscntrl:\n");
	for (c=0; c<=127; c++)
		if(iscntrl(c)) putchar(c);

	printf("------isspace:\n");
	for (c=0; c<=127; c++)
		if(isspace(c)) putchar(c);

	printf("------tolower:\n");
	c = 'A';
	c2 = tolower(c);
	printf("c=%c tolower=%c\n", c, c2);

	printf("------toupper:\n");
	c = 'b';
	c = toupper(c);
	printf("toupper=%c\n", c);

	return 0;
}
&#91;/code&#93;
$ ./dev
------isalnum:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz------isalpha:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz------isdigit:
0123456789------islower:
abcdefghijklmnopqrstuvwxyz------isupper:
ABCDEFGHIJKLMNOPQRSTUVWXYZ------isxdigit:
0123456789ABCDEFabcdef------isprint:
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~------isgraph:
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~------ispunct:
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~------iscntrl:
------isspace:
------tolower:
c=A tolower=a
------toupper:
toupper=B

### 数学処理関数 math.h
sin, cos, tan, log, log10, pow, sqrtなど
関数の戻り値はdouble, 引数はdouble, 角度はラジアン
<a href="https://en.wikipedia.org/wiki/C_mathematical_functions">https://en.wikipedia.org/wiki/C_mathematical_functions</a>

#include <stdio.h>
#include <math.h>

int main(void){

	double x, r;

	printf(" x sin(x) cos(x)\n");
	for (x=0.0; x<=90.0; x+=15.0){
		r = x * 3.14159 / 180.0;
		printf("%6.2f %8.5f %8.5f\n", x, sin(r), cos(r));
	}

	return 0;
}
&#91;/code&#93;

### 時間処理関数 math.h
時間処理を行う時は複数の関数を組み合わせて使用する
<a href="https://en.wikipedia.org/wiki/C_date_and_time_functions">https://en.wikipedia.org/wiki/C_date_and_time_functions</a>

#include <stdio.h>
#include <time.h>

int main(void){

	time_t mytime;
	struct tm *ltime;
	char *p;

	time(&mytime);
	ltime = localtime(&mytime);
	p = asctime(ltime);
	printf("japan time: %s", p);

	p = ctime(&mytime);
	printf("ctime:  %s", p);

	printf("year=%d\n", ltime->tm_year+1900);
	printf("month=%d\n", ltime->tm_mon+1);
	printf("day=%d\n", ltime->tm_mday);
	printf("hour=%d\n", ltime->tm_hour);
	printf("minute=%d\n", ltime->tm_min);
	printf("second=%d\n", ltime->tm_sec);

	return 0;
}

$ ./dev
japan time: Fri May 15 23:23:52 2020
ctime: Fri May 15 23:23:52 2020
year=2020
month=5
day=15
hour=23
minute=23
second=52

int main(void){

	clock_t start, end;
	int keika;
	char s[80];

	start = clock();
	printf("started. put [enter], then end.");
	gets(s);
	end = clock();
	keika = (end - start) / CLOCKS_PER_SEC;
	printf("end: %d second passed. \n", keika);

	return 0;
}

### その他関数
atoi(), atol(), atof(), rand(), system(), exit()

int main(void){

int i, n;

srand(555);
printf("0 ~ 32767:\n");
for(i=1; i<=10; i++) printf("%d ", rand()); printf("\n0.0 ~ 1:\n"); for(i=1; i<=10; i++) printf("%5.3f ", rand() / (RAND_MAX+1.0)); printf("\n1 ~ 6:\n"); for(i=1; i<=10; i++){ n = (int)(rand() / (RAND_MAX+1.0) * 6.0); ++n; printf("%d ", n); } printf("\n"); return 0; } [/code] $ ./dev 0 ~ 32767: 1897361756 511234109 2040558095 299669925 1854125204 1147783809 1808688013 1528174380 1122472434 999190120 0.0 ~ 1: 0.224 0.826 0.696 0.201 0.311 0.565 0.758 0.135 0.223 0.791 1 ~ 6: 1 3 4 3 3 3 3 1 2 4 入出力、数学、文字、時間に加えて、ハードウェア周りか。 ハードウェア周りもマスターしたいな。

C言語の前処理指令

前処理司令とは、コンパイルの前に、テキスト上の処理を行う
前処理司令を使うことで、別のファイルを読み込んだり、記号定数を用意したり、条件付きコンパイルなどを行うことができる
前処理の工程を行うのがプリプロセッサ(preprocessor)

### 前処理司令
– #inclue: 指定されたファイルを挿入
– #define: マクロ定義
– #undef: マクロ定義を取り消す
– #if #ifdef #ifndef: 条件付きコンパイル
– #elif #else #endif: 同上
– defined: #if, #elifの中で判定処理に用いる単項演算子
– #line: 行番号の付け替え
– #pragma: コンパイラへのオプション指示
– #error: 前処理時のエラー表示
– # : #defineの中で使用時:仮引数の文字列化、単独使用時: 空司令であり何も実行しない
– ## : 字句の連結

### #inclue
指定されたファイルをディスクから読み込み、その場所に挿入する

/* myhead.h */
int hdt = 1234;

void myfunc(void){
	printf("test program\n");
}
#include <stdio.h>
#include "myhead.h"

int main(void){

	myfunc();
	printf("hdt=%d\n", hdt);

	return 0;
}

includeで指定するファイルはヘッダファイルと呼ばれる
ヘッダファイルは.hが付いており、標準ディレクトリから対象ファイルを探す

### #define
– オブジェクト形式マクロ
defineは文字列の置き換えを行う。関数のような形式でマクロ置換をすると言う意味をもつ
大文字で書くのが慣例

#define MYSIZE 100
#define MYSTR “abc”
#define MYCH ‘A’
#define begin {
#define end }

stdio.hではEOFは(-1)と定義されている

– 関数形式マクロ
#define average(a,b) (a+b)/2
#define putd(dt) printf(“%d\n”,dt);
関数式マクロは慎重に記述する必要がある

#include <stdio.h>

#define MAXSIZE 1234
#define average(a,b) (a+b)/2
#define putd(dt) printf("%d\n", dt)

int main(void){

	int n1, n2;

	n1 = MAXSIZE;
	n2 = average(1111, 3333);
	printf("n1=%d\n", n1);
	printf("n2=%d\n", n2);
	putd(n2);

	return 0;
}

$ ./dev
n1=1234
n2=2222
2222

### #if
if, elif, else, endifで条件付きコンパイル
デバッグ用によく使用される

#define JAPAN_MODE 1

#if JAPAN_MODE == 1
	char msg[] = "こんにちは";
#else
	char msg[] = "Hello";
#endif

int main(void){

	printf("msg=%s\n", msg);

	return 0;
}

フレームワークだと、#defineなどの定義はまとめて別ファイルに書くことが多いように思いますが、includeの下に書くんですね。言語の歴史が見れて面白い。

C言語構造体

### 構造体とは
関連したいくつもの変数がある時、これをまとめて一つの名前だけで処理できるようにするもの

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

struct Person {
	char name[40];
	int height;
	int weight;
};

void dataout(struct Person ps){
	printf("name=%s height=%d weight=%d\n", ps.name, ps.height, ps.weight);
}

int main(void){
	struct Person dt, dt2;

	strcpy(dt.name, "tanaka kenichi");
	dt.height = 180;
	dt.weight = 70;

	dt2 = dt;
	dataout(dt2);

	return 0;
}

$ ./dev
name=tanaka kenichi height=180 weight=70

### 構造体の宣言
構造体はintやcharなどの基本データ型を好きなように組み合わせて新しいデータ型として扱うもの
struct 構造体名 {
必要な変数宣言の並び;
};
構造体は初期化することもできる
構造体の宣言はプログラムの先頭部分に記述する
構造体のメンバを指定する時は、2種類のメンバアクセス演算子を使う
-> 変数名で操作する時は”.”
-> ポインタで操作する時は”->”

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

struct Person {
	char name[40];
	int height;
	int weight;
};

int main(void){
	struct Person d1, d2;
	struct Person *p = &d2;

	strcpy(d1.name, "tanaka kenichi");
	d1.height = 180;
	d1.weight = 70;

	strcpy(p->name, "suzuki tomomi");
	p->height = 160;
	p->weight = 60;

	printf("%s %d %d\n", d1.name, d1.height, d1.weight);
	printf("%s %d %d\n", p->name, p->height, p->weight);

	return 0;
}

### 構造体の引数渡し

#include <stdio.h>

struct myst {
	int d1, d2;
};

struct myst add(struct myst x, struct myst y){
	struct myst wk;
	wk.d1 = x.d1 + y.d1;
	wk.d2 = x.d2 + y.d2;
	return wk;
}

int main(void){
	struct myst a, b = {10, 20}, c = {100, 200};
	
	a = add(b, c);
	printf("%d %d\n", a.d1, a.d2);

	return 0;
}

### 共用体
メモリ上の同じアドレスを複数の変数で共用すると言う機能
キーワードがstructではなくunionになる

union MyType {
	char ch;
	int n1;
	int n2;
};

int main(void){
	union MyType dt;

	dt.n1 = 0x41424344;
	printf("dt.ch=%X\n", dt.ch);
	printf("dt.n1=%X\n", dt.n1);
	printf("dt.n2=%X\n", dt.n2);

	return 0;
}

$ ./dev
dt.ch=44
dt.n1=41424344
dt.n2=41424344

### 列挙型
名前付き定数を使って、データの取り得る値を全て列挙したもの

enum RetType {YES, NO, CANCEL};

int main(void){
	enum RetType ret;

	ret = YES;
	if (ret == YES)
		printf("ret is yes\n");
	else if (ret == NO)
		printf("ret is no\n");
	else if (ret == CANCEL)
		printf("ret is cancel\n");
	printf("%d %d %d\n", YES, NO, CANCEL);
	
	return 0;
}