ページ 1 / 1
メニューを常に表示させたい。
Posted: 2011年6月15日(水) 10:51
by cale
前回副管理人さんにいろいろと手直ししてもらい、
操作性はご愛嬌ですが、なんとか見れるとこまできました。
が、ここでまた詰まりまして。
最初に表示される「MENU」、操作を選ぶときに「m」と入力すると
表示されるようにしたいのですが。
どうすればいいでしょうか?
操作の方に「m」は作ってあるんですが、どうしようかと・・・
コード:
int main(void)
{
int state = INPUT_YEAR;
char n = 0;
FILE *fp;
if( (fp = fopen("Calendar.txt", "a") ) == NULL) /* 起動時にメモ帳を新規作成する */
{
printf("Calendar.txtが開けません\n");
getchar();
exit(EXIT_FAILURE);
}
fclose(fp);
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの表示には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
while( 1 ) /* M E N Uのループ */
{
switch( state ) /* switchで状態別の処理 */
{
/*==============================================================*/
/* 西暦の入力 */
/*==============================================================*/
case INPUT_YEAR:
while(1)
{
printf("西暦を入力してください(1970年から2038年まで有効)>>");
scanf("%d",&year);
fflush(stdin);
if((year > 1969) && (year < 2039))
{
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR); /*状態によりMENU操作(前)へ*/
break;
}
else
{
error(year);
continue;
}
}
break;
/*==============================================================*/
/* 月の入力 */
/*==============================================================*/
case INPUT_MONTH:
while(1)
{
printf("月を入力してください(1月から12月まで有効)>>");
scanf("%d", &month);
fflush(stdin);
if((month > 0) && (month < 13))
{
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,CALENDAR,ERROR,END,ERROR); /*MENU操作カレンダー(前)へ*/
break;
}
else
{
error(month);
continue;
}
}
break;
/*==============================================================*/
/* カレンダーの表示 */
/*==============================================================*/
case CALENDAR:
while(1)
{
int i, e, w;
w = week_of_day(year, month, 1); /* 1日の曜日を求める */
e = month_last_day(year, month); /* 月の最終日を求める */
printf(" ******%d年%d月*****\n", year, month);
printf(" 日 月 火 水 木 金 土\n");
printf(" --------------------\n");
for (i = 1; i <= w; i++) /* 1日まで空白で埋める */
{
printf(" ");
}
for (i = 1; i <= e; i++) /* 最終日まで表示 */
{
printf(" %2d", i);
if ( (i + w) % 7 == 0) /* 土曜日で改行 */
{
putchar('\n');
}
}
printf("\n--------------------\n");
if(state == 3)
{
state = select_2(MENU,INPUT_YEAR,INPUT_MONTH,READ_OR_WRITE,CALENDAR,END,ERROR); /*MENU操作カレンダー(後)へ*/
break;
}
else
{
error(calendar);
continue;
}
}
break;
/*==============================================================*/
/* Read/Writeの選択(未) */
/*==============================================================*/
case READ_OR_WRITE:
break;
}
}
return 0;
}
日数や曜日の計算部分のプログラムは省略してあります。
メニュー操作はこちらです。
コード:
/*==============================================================*/
/* 「M E N U」の操作、カレンダー(前) */
/*==============================================================*/
int select_1(int selm,int sels,int selb,int selg,int selc,int selx,int sele)
{
while(1)
{
char n=0,str[256];
printf("操作を「M E N U」から選んでください>>");
scanf("%255[^\n]%*[^\n]",str);
getchar();
if(strlen(str) == 1)
{
n = str[0];
}
if(n =='m')
{
return selm;
}
else if(n == 's')
{
return sels;
}
else if(n == 'b')
{
return selb;
}
else if(n == 'g')
{
return selg;
}
else if(n == 'c')
{
printf("**************ERROR*************\n");
printf("西暦と月の入力が完了していません\n");
continue;
}
else if(n == 'x')
{
return selx;
}
else
{
printf("**********ERROR***********\n");
printf("不適切な値が入力されました\n");
continue;
}
break;
}
return 0;
}
select2は「n == C」の部分が通常処理なだけで、他に違いはありません。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 11:49
by softya(ソフト屋)
こちらの続きですね? 同じ名前を継続するようにお願いします。
「カレンダー(ファイルオープンによるメモ機能付き) • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=8544
で質問ですが
case MENU:
を追加すれば良いのではないでしょうか?
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 11:53
by cale
すいません、名前は今度からcaleで統一します。
つまり、メニューの下にもう一つメニューを作ればいいんでしょうか?
コード:
//メニュー略
case MENU:
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの表示には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
のような感じで。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 11:55
by softya(ソフト屋)
最初からcaseでそこが選ばれるようにすれば2重にする必要はないと思います。
つまり、state=MENUを初期値にすれば良いのでは?
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 12:02
by cale
すいません、2重にならないようにするにはどうしたら・・・
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 12:07
by softya(ソフト屋)
まず、
として、最初の方にあるprintfはとっぱらって、
コード:
//メニュー略
case MENU:
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの表示には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR); /*状態によりMENU操作(前)へ*/
break;
で解決しませんか?
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 12:18
by cale
西暦の入力が飛ばされました><
やはりMENUを常に表示となると、最初からいきなり操作の選択を出さないといけないんですかね。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 12:24
by softya(ソフト屋)
そう言えば、西暦の入力が優先でしたね。
だとすると
int state = INPUT_YEAR;
に戻して
コード:
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの表示には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
を関数にするのが無駄がなくて良いかも知れません。
コード:
int main(void)
{
int state = INPUT_YEAR;
char n = 0;
FILE *fp;
if( (fp = fopen("Calendar.txt", "a") ) == NULL) /* 起動時にメモ帳を新規作成する */
{
printf("Calendar.txtが開けません\n");
getchar();
exit(EXIT_FAILURE);
}
fclose(fp);
menuprint();
while( 1 ) /* M E N Uのループ */
{
switch( state ) /* switchで状態別の処理 */
{
case MENU:
menuprint();
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR); /*状態によりMENU操作(前)へ*/
break;
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 13:10
by cale
やはり関数ですかね、2個作らないとやっぱり最初の表示+後から呼び出しはできないですね。
あともう一ついいですか?
2011、6と入力してカレンダーを表示させます。
「月を間違えた!」てことで、
を前に戻る。
ここで数字以外(例:a)を打ち込んだら、「6」が残っているらしく
そのまま通ってしまうんですが。
どうすればいいでしょう?
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 14:08
by softya(ソフト屋)
cale さんが書きました:やはり関数ですかね、2個作らないとやっぱり最初の表示+後から呼び出しはできないですね。
あともう一ついいですか?
2011、6と入力してカレンダーを表示させます。
「月を間違えた!」てことで、
を前に戻る。
ここで数字以外(例:a)を打ち込んだら、「6」が残っているらしく
そのまま通ってしまうんですが。
どうすればいいでしょう?
scanfに失敗すると戻り値に0が帰るので、それでチェックできます。
コード:
//成功するまでループ
while( 0==scanf("%d", &month) ) { //数値以外だと0で戻ってくるのでwhileループすることに成る。
scanf("%*s");//入力バッファをクリアする。必須。
}
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 14:24
by cale
月を入力でなぜかエラーに飛びますね。
う~ん、入れた場所を間違えた・・・
なんてことはない。
scanfとfflushのとこに入れたんですが、ちゃんと動作しないですね。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 14:38
by softya(ソフト屋)
cale さんが書きました:月を入力でなぜかエラーに飛びますね。
う~ん、入れた場所を間違えた・・・
なんてことはない。
scanfとfflushのとこに入れたんですが、ちゃんと動作しないですね。
どう言うコードなのか見ないとなんとも言えないですね。
一番確実なのは、文字列としてscanfして数値文字チェックと数値変換(atoi関数)を自前でやることですけどね。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 14:46
by cale
コード:
case INPUT_MONTH:
while(1)
{
printf("月を入力してください(1月から12月まで有効)>>");
//ここに先ほどのを入れる?
if((month > 0) && (month < 13))
{
コード:
//成功するまでループ
while( 0==scanf("%d", &month) ) { //数値以外だと0で戻ってくるのでwhileループすることに成る。
scanf("%*s");//入力バッファをクリアする。必須。
}
この文を最初のscanf~fflushの代わりに使ったら。
処理はしたんですが一度エラー処理に行き「不適切~」の文を出し、また通常処理に戻ってきました。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 14:48
by softya(ソフト屋)
fflush()は消さないでくださいね。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 15:03
by cale
コード:
case INPUT_MONTH:
while(1)
{
printf("月を入力してください(1月から12月まで有効)>>");
while( 0 == scanf("%d", &month)){
scanf("%*s");
fflush(stdin);
}
やはり一度エラーに行きますね。
ステップオーバーで一行一行見たんですが。
月の判定でmonthに「6」を持ったまま。
stateの選択操作に行ってしまうのでエラー判定を持ってきてしまってるみたいです。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 15:31
by softya(ソフト屋)
cale さんが書きました:コード:
case INPUT_MONTH:
while(1)
{
printf("月を入力してください(1月から12月まで有効)>>");
while( 0 == scanf("%d", &month)){
scanf("%*s");
fflush(stdin);
}
やはり一度エラーに行きますね。
ステップオーバーで一行一行見たんですが。
月の判定でmonthに「6」を持ったまま。
stateの選択操作に行ってしまうのでエラー判定を持ってきてしまってるみたいです。
fflush(stdin);
をループの中に入れたら意味がなくなりますよ。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 15:36
by cale
おぉ、ちゃんと動いた。
でもまだ、「b」など予期せぬ値が来た時の入力が弾いてくれないですが。
これはscanfの仕様ですかね。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 16:14
by softya(ソフト屋)
cale さんが書きました:おぉ、ちゃんと動いた。
でもまだ、「b」など予期せぬ値が来た時の入力が弾いてくれないですが。
これはscanfの仕様ですかね。
入力されたものを確実にエラーチェックするには、上にも書いたとおり
一番確実なのは、文字列としてscanfして数値文字チェックと数値変換(atoi関数)を自前でやることですけどね。
と言う事になります。scanfは万能ではありませんし、仕事ではまず使いません。
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 16:51
by cale
うぅ、自前かぁ。
atoiはまだ勉強してないんでなんとも・・・
(注)文を書いてなんとかしようと思います。
書いてるうちにどんどん聞きたいことが出てきます。
read/writeからの前回言った内容ですが、
ここから先がまったくどうすればいいか思いつかず、困ってます。
カレンダーの表示までなんとかきましたが、これから先がなんとも><
プログラム途中終了の「x」ですが、今のretunr 0の位置だと何も起きないです。
どこに置けばよいでしょう?
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 17:12
by softya(ソフト屋)
cale さんが書きました:書いてるうちにどんどん聞きたいことが出てきます。
read/writeからの前回言った内容ですが、
ここから先がまったくどうすればいいか思いつかず、困ってます。
カレンダーの表示までなんとかきましたが、これから先がなんとも><
実装のアイデアなら出しますが、どんな機能を入れるかは自分で考えたほうが良いですよ。
これも訓練ですから。
cale さんが書きました:プログラム途中終了の「x」ですが、今のretunr 0の位置だと何も起きないです。
どこに置けばよいでしょう?
ソースコード全体が分からないのでなんとも言えないです。
case END:
で終了させれば良いのでは?
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 17:43
by cale
case ENDの前に他のcaseを入れ忘れてました。
お手数おかけしました。
デバック中に気づいたのですが。
コード:
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの確認には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
while( 1 ) /* M E N Uのループ */
{
switch( state ) /* switchで状態別の処理 */
{
/*==============================================================*/
/* MENU画面の呼び出し */
/*==============================================================*/
case MENU:
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの確認には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR); /*状態によりMENU操作へ*/
break;
case MENUを作りましてどこからでもMENUを呼び出せるようにはしました。
しかしこれだとカレンダーを表示した後などに「m」でMENUを呼び出し、
「c」でカレンダー確認をしようとすると最初に戻ってしまうみたいで。
今まで入力したのが初期化されてしまいます。
select_1だと「西暦と月の入力が完了していません」と出てしまう。
かといってselect_2では何も表示されない。
MENUもカレンダー前と後と作るべきですかね?
Re: メニューを常に表示させたい。
Posted: 2011年6月15日(水) 18:04
by softya(ソフト屋)
意図するメニューの流れを整理して書きだしてもらいたいのとソースコード全体を提示してもらえますか?
どうなっているのか、断片的で私にはよく分かりません。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 10:45
by cale
遅れて申し訳ありません。
コード:
int main(void)
{
int state = INPUT_YEAR;
char n = 0;
FILE *fp;
if( (fp = fopen("Calendar.txt", "a") ) == NULL) /* 起動時にメモ帳を新規作成する */
{
printf("Calendar.txtが開けません\n");
getchar();
exit(EXIT_FAILURE);
}
fclose(fp);
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの確認には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
while( 1 ) /* M E N Uのループ */
{
switch( state ) /* switchで状態別の処理 */
{
/*==============================================================*/
/* MENU画面の呼び出し */
/*==============================================================*/
case MENU:
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの確認には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR); /*状態によりMENU操作へ*/
break;
/*==============================================================*/
/* 西暦の入力 */
/*==============================================================*/
case INPUT_YEAR:
while(1)
{
printf("西暦を入力してください(1970年から2038年まで有効)>>");
scanf("%d",&year); /*未*/
fflush(stdin);
if((year > 1969) && (year < 2039))
{
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR); /*状態によりMENU操作(前)へ*/
break;
}
else
{
error(year);
continue;
}
}
break;
/*==============================================================*/
/* 月の入力 */
/*==============================================================*/
case INPUT_MONTH:
while(1)
{
printf("月を入力してください(1月から12月まで有効)>>");
scanf("%d",&year); /*未*/
fflush(stdin);
if((month > 0) && (month < 13))
{
state = select_1(MENU,INPUT_YEAR,INPUT_YEAR,CALENDAR,ERROR,END,ERROR); /*MENU操作カレンダー(前)へ*/
break;
}
else
{
error(month);
continue;
}
}
break;
/*==============================================================*/
/* カレンダーの表示 */
/*==============================================================*/
case CALENDAR:
while(1)
{
int i, e, w;
w = week_of_day(year, month, 1); /* 1日の曜日を求める */
e = month_last_day(year, month); /* 月の最終日を求める */
printf(" ******%d年%d月*****\n", year, month);
printf(" 日 月 火 水 木 金 土\n");
printf(" --------------------\n");
for (i = 1; i <= w; i++) /* 1日まで空白で埋める */
{
printf(" ");
}
for (i = 1; i <= e; i++) /* 最終日まで表示 */
{
printf(" %2d", i);
if ( (i + w) % 7 == 0) /* 土曜日で改行 */
{
putchar('\n');
}
}
printf("\n--------------------\n");
if(state == 3)
{
state = select_2(MENU,INPUT_YEAR,INPUT_MONTH,READ_OR_WRITE,CALENDAR,END,ERROR); /*MENU操作カレンダー(後)へ*/
break;
}
else
{
error(calendar);
continue;
}
}
break;
/*==============================================================*/
/* Read/Writeの選択 */
/*==============================================================*/
case READ_OR_WRITE:
break;
case INPUT_DAY:
break;
case INPUT_MEMO:
break;
case END:
return 0;
}
}
return 0;
}
/*==============================================================*/
/* 曜日を求める */
/*==============================================================*/
int week_of_day(int year, int month, int day)
{
if(month == 1)
{
year = year - 1;
month = 13;
}
else if(month == 2)
{
year = year - 1;
month = 14;
}
return (5 * year / 4 - year / 100 + year / 400 + (26 * month + 16) / 10 + day ) % 7;
}
/*==============================================================*/
/* 月の最終日 */
/*==============================================================*/
int month_last_day(int year, int month)
{
int d = 0, last[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month == 2) {
d = last[month - 1] + leap_year(year); /* 閏年なら+1 */
}
else if (month >= 1 && month <= 12) {
d = last[month - 1];
}
return d;
}
/*==============================================================*/
/* 閏年の判定 */
/*==============================================================*/
int leap_year(int year)
{
{
int rc = 0;
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
{
rc = 1;
}
return rc;
}
}
/*==============================================================*/
/* 「M E N U」の操作、カレンダー(前) */
/*==============================================================*/
int select_1(int selm,int sels,int selb,int selg,int selc,int selx,int sele)
{
while(1)
{
printf("操作を「M E N U」から選んでください>>");
scanf("%255[^\n]%*[^\n]",str);
getchar();
if(strlen(str) == 1)
{
n = str[0];
}
if(n =='m')
{
return selm;
}
else if(n == 's')
{
return sels;
}
else if(n == 'b')
{
return selb;
}
else if(n == 'g')
{
return selg;
}
else if(n == 'c')
{
printf("**************ERROR*************\n");
printf("西暦と月の入力が完了していません\n");
continue;
}
else if(n == 'x')
{
return selx;
}
else
{
printf("**********ERROR***********\n");
printf("不適切な値が入力されました\n");
continue;
}
break;
}
return 0;
}
/*==============================================================*/
/* 「M E N U」の操作、カレンダー(後) */
/*==============================================================*/
int select_2(int selm,int sels,int selb,int selg,int selc,int selx,int sele)
{
while(1)
{
printf("操作を「M E N U」から選んでください>>");
scanf("%255[^\n]%*[^\n]",str);
getchar();
if(strlen(str) == 1)
{
n = str[0];
}
if(n =='m')
{
return selm;
}
else if(n == 's')
{
return sels;
}
else if(n == 'b')
{
return selb;
}
else if(n == 'g')
{
return selg;
}
else if(n == 'c')
{
return selc;
}
else if(n == 'x')
{
return selx;
}
else
{
printf("**********ERROR***********\n");
printf("不適切な値が入力されました\n");
continue;
}
break;
}
return 0;
}
/*==============================================================*/
/* エラー処理 */
/*==============================================================*/
void error(int)
{
printf("**********ERROR***********\n");
printf("不適切な値が入力されました\n");
}
現在ここまでできています。
例:メニューの流れ。
西暦→月→カレンダー→メニュー表示→「c」確認→「西暦と月の入力~」となります。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 10:58
by softya(ソフト屋)
最終的な完成形においての処理の流れをメニューの分岐を含めて書きだしてみてください。
それがcaleさんが作りたいカレンダー処理の仕様になります。
それと現在のプログラムを比べて問題点を明確にしていきましょう。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 11:14
by cale
西暦→月→カレンダー→RorW?→日付入力→メモ(メモ日に*と表示)→最初に戻る。
これが一連の流れです。
メモの部分は前回のトピにも書いてある通りで50文字までとか10件までしか書きこめないとかの制約です。
一回の入力ごとにMENUからのキーボード操作をします。
どこからでも確認ができたり、終了することもできる。
仕様はこんなもので・・・
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 11:38
by softya(ソフト屋)
コンパイルが通らないのでヘッダのインクルード部分からmainまでの部分ももらえますか?
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 11:44
by cale
こちらはヘッダです。
コード:
#pragma warning(disable:4996) //エラー警告抑制
/* 各プロトタイプ宣言 */
/*西暦と月の入力前MENU操作*/
int select_1(int selm,int sels,int selb,int selg,int selc,int selx,int sele);
/*西暦と月の入力後MENU操作*/
int select_2(int selm,int sels,int selb,int selg,int selc,int selx,int sele);
void error(int n);
char n=0,str[256];
int year,month,day,calendar,notes;
int month_last_day(int year, int month);
int week_of_day(int year, int month, int day);
int leap_year(int year);
/* 状態管理の定数 */
enum {
MENU, /* メニューの表示(前) */
INPUT_YEAR, /* 西暦を入力 */
INPUT_MONTH, /* 月を入力 */
CALENDAR, /* カレンダー表示 */
READ_OR_WRITE, /* Read/Write選択 */
INPUT_DAY, /* 日付を入力 */
INPUT_MEMO, /* メモを入力 */
END, /* 終了 */
ERROR, /* ERROR処理 */
};
こちらはmainより上です。
コード:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "calendar.h"
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 12:12
by softya(ソフト屋)
しかしこれだとカレンダーを表示した後などに「m」でMENUを呼び出し、
「c」でカレンダー確認をしようとすると最初に戻ってしまうみたいで。
今まで入力したのが初期化されてしまいます。
select_1だと「西暦と月の入力が完了していません」と出てしまう。
かといってselect_2では何も表示されない。
「西暦と月の入力が完了していません」エラーは、ちゃんと変数をチェックして下さい。
year,monthの初期値を0とかにしておけば未入力か判定できるはずです。
select_1で選択だけを行うべきでエラーチェックしてはいけません。select_2も不要になるはずです。
year,monthの値でエラーチェックできます。
西暦→月→カレンダー→RorW?→日付入力→メモ(メモ日に*と表示)→最初に戻る。
これが一連の流れです。
メモの部分は前回のトピにも書いてある通りで50文字までとか10件までしか書きこめないとかの制約です。
一回の入力ごとにMENUからのキーボード操作をします。
どこからでも確認ができたり、終了することもできる。
仕様はこんなもので・・・
もっと詳しく書いてください。そうしないと問題点や抜けは見えてきません。
例えばこうです。
(1) 西暦の入力。エラーなら(1)に戻る。
(2) メニュー処理 s:(1)へ b:(1)へ g:(3)へ c:エラーチェックしエラーなら(2)へ OKなら(n)へ x:終了へ
:
:
(n)カレンダー処理
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 13:31
by cale
(1) 西暦の入力。エラーなら(1)に戻る。
MENU操作 s:(1)へ b:(1)へ g:(2)へ c:エラーなら(1)へ OKなら(c)へ m:(m)へ x:終了へ
(2) 月の入力。エラーなら(2)に戻る。
MENU操作 s:(1)へ b:(1)へ g:(3)へ c:エラーなら(2)へ OKなら(c)へ m:(m)へ x:終了へ
(3) カレンダー処理。エラーなら(3)に戻る。
MENU操作 s:(1)へ b:(2)へ g:(4)へ c:エラーなら(2)へ OKなら(c)へ m:(m)へ x:終了へ
(4)RorW?。エラーなら(4)に戻る。
MENU操作 s:(1)へ b:(3)へ g:(5)へ c:エラーなら(2)へ OKなら(c)へ m:(m)へ x:終了へ
(5)日付。エラーなら(5)に戻る。
MENU操作 s:(1)へ b:(4)へ g:(6)へ c:エラーなら(2)へ OKなら(c)へ m:(m)へ x:終了へ
(6)メモ。エラーなら(6)に戻る。
MENU操作 s:(1)へ b:(5)へ g:(7)へ c:エラーなら(2)へ OKなら(c)へ m:(m)へ x:終了へ
(7)最終の確認。エラーなら(7)に戻る。
MENU操作 s:(1)へ b:(6)へ g:(8)へ c:エラーなら(2)へ OKなら(c)へ m:(m)へ x:終了へ
(8)END
(m)
メニューの確認
(c)
カレンダーチェック
もっとわけるのであれば。
カレンダー処理後、RorWの時の「s:」は西暦まで戻らずRorWに戻るようにしたいです。
現状のまま作ると、「s:」で西暦ですが、3と4で切り替えて。
3以降の「s:」は違うメニューにしたいですが、
こんなことにしたらもっと長くなりそうなのでやめときます。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 14:40
by softya(ソフト屋)
この流れだと
OKなら(c)へ m:(m)へ
の場合はのその後の操作が確定できません。
(c)へは(3)へではダメでしょうか?
(m)へは、メニュー項目の表示で再び同じメニュー処理では無いですか?
まとめるとこうなります。
(1) 西暦の入力。エラーなら(1)に戻る。
(1.5)MENU操作 s:(1)へ b:(1)へ g:(2)へ c:エラーなら(1)へ OKなら(3)へ m:メニューの一覧表示して(1.5)へ x:終了へ
以下同様。
違いますか?
カレンダー処理後、RorWの時の「s:」は西暦まで戻らずRorWに戻るようにしたいです。
現状のまま作ると、「s:」で西暦ですが、3と4で切り替えて。
3以降の「s:」は違うメニューにしたいですが、
select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR);
で次のメニューを決めているので出来るとは思いますが、それで操作に問題がないか一度仕様を書いてみてください。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 15:26
by cale
softya(ソフト屋) さんが書きました:
(1) 西暦の入力。エラーなら(1)に戻る。
(1.5)MENU操作 s:(1)へ b:(1)へ g:(2)へ c:エラーなら(1)へ OKなら(3)へ m:メニューの一覧表示して(1.5)へ x:終了へ
以下同様。quote]
見直したらそうでした。
すいません。
select_1(MENU,INPUT_YEAR,INPUT_YEAR,INPUT_MONTH,ERROR,END,ERROR);
で次のメニューを決めているので出来るとは思いますが、それで操作に問題がないか一度仕様を書いてみてください。
ここはまだ、RorW以降ができてないのでまだ、これだ!っていう仕様を決めかねてます。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 15:38
by softya(ソフト屋)
cale さんが書きました:ここはまだ、RorW以降ができてないのでまだ、これだ!っていう仕様を決めかねてます。
仕様というのは作る前に決めるものですよ。
とりあえず問題があるかも知れないが、とりあえず「これなら行けそうだ」と言うレベルで決めてしまわないと作る予定・スケジュールが立ちません。
それが設計であり、仕様の策定です。
RorW以降の掘り下げが足らないのでは無いのですか?
コンソールで行う操作を仮想的に書いてみてください。
見直したらそうでした。
すいません。
現状のプログラムはそうなっていませんので、何処が悪いか何処を直すべきか見直してみてください。
私は仕様を整理してみるとstate MENUは無い方が良かったと言えると思います。
mを押したらメニュー項目一覧を表示するだけで良いので、select内で完結できたのでは?
※ メニュー項目一覧の関数化が放置されますが、関数化をお願いします。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 16:23
by cale
関数化忘れてました一番下にこれ追加です。
コード:
/*==============================================================*/
/* MENU表示 */
/*==============================================================*/
int menuprint(int)
{
printf("\n***********メモ機能付きカレンダー************\n\n");
printf("* * * * * * * * * M E N U * * * * * * * * * *\n");
printf("* (注)スペース等は入力しないでください *\n");
printf("* *\n");
printf("* 最初から入力の場合は 「s」を入力 *\n");
printf("* 前に戻りたい場合は 「b」を入力 *\n");
printf("* 次の処理に行くには 「g」を入力 *\n");
printf("* メニューの確認には 「m」を入力 *\n");
printf("* カレンダーの確認は 「c」を入力 *\n");
printf("* プログラムの終了は 「x」を入力 *\n");
printf("* * * * * * * * * * * * * * * * * * * * * * *\n\n");
return 0;
}
MENUは関数でなんとかできました。
case MENUのstateの値を、if~でstate(1,2,3)の切替を行えばなんとか先ほどの3と4で分けれそうです。
コンソールだとこのような感じで表示させたいです。
******2011年6月*****
日 月 火 水 木 金 土
--------------------
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
---------------------
と表示されたあとですが。
RorW?>>W
メニュー処理(カレンダー表示後)
何日に書きこむ?>>16
メニュー処理(カレンダー表示後)
メモ(50文字まで)>>abc
******2011年6月*****
日 月 火 水 木 金 土
--------------------
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 *16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
--------------------
メニュー処理
西暦からか、RorWからか、終了か。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 16:41
by softya(ソフト屋)
case MENUのstateの値を、if~でstate(1,2,3)の切替を行えばなんとか先ほどの3と4で分けれそうです。
それよりは、select()関数内でmが押されたらmenuprint()して再び
printf("操作を「M E N U」から選んでください>>");
にもどる方が良いと思うのですが。
あと、後半の流れですが
RorW?>>W
Rの時はどうなるのでしょうか?
そして、
******2011年6月*****
日 月 火 水 木 金 土
--------------------
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 *16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
--------------------
の*マークの位置は既にメモが記録されていた場合は、最初のカレンダー表示の時点でマークは表示されているんですよね?
何時ファイルをチェックするのでしょうか?
幾つも日付にマークされる場合も当然有りますよね?
※ 今気づいたが、W時に既にその日付にメモが書いてあったらどうしましょう?
それと
メニュー処理(カレンダー表示後)
メニュー処理
西暦からか、RorWからか、終了か。
これもちゃんと書いてみてください。
曖昧だとイメージもはっきりしないのでプログラム化がやはり出来ません。
どうです?仮想的にでも書いてみると色々見えてきませんか?
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 17:25
by cale
RorW>>
R処理→(1)日付選択、*の有無でエラー(1)か(1.5)へ
(1.5)メニュー処理(カレンダー表示後)
m:RorW後のMENU, s:RorW, b:RorW, g:次へ, c:check, x:終了
(2)読み込み、エラー(1)か(2.5)へ
(2.5)メニュー処理(カレンダー表示後)
m:RorW後のMENU, s:RorW, b:RorW, g:次へ, c:check, x:終了
(3)メモ閲覧後、RorWへ戻る。
W処理→(1)日付選択、*の有無でエラー(1)か(1.5)へ
(1.5)メニュー処理(カレンダー表示後)
m:RorW後のMENU, s:RorW, b:RorW, g:次へ, c:check, x:終了
(2)書き込み(50文字,書き込み日に*)、エラー(1)か(2.5)へ
(2.5)メニュー処理(カレンダー表示後),
m:RorW後のMENU, s:RorW, b:RorW, g:次へ, c:check, x:終了
(3)書き込み後MENU処理(3.5)へ
(3.5)MENU処理最終
m:MENU処理最終の表示、S:西暦から入力、b:RorWから入力、c:check、x:終了。
ふぅ、詳しく書いてみましたがどうでしょう?
MENUはやはり、あと二つ別に作ったほうがよろしいですかね。
カレンダー表示後のRorWに戻れるメニューと、
最後の、西暦からか、RorWか、カレンダーのチェックか終了。
Re: メニューを常に表示させたい。
Posted: 2011年6月16日(木) 18:16
by softya(ソフト屋)
気になるとことろ羅列
・c:check, ってなんでしょうか?
・(3)メモ閲覧後、RorWへ戻る。メモ閲覧後って具体的に何をするのでしょうか?
・*の有無でエラー(1)か(1.5)へ。ってことは一度書いたら修正できないのでしょうか?
・(2)書き込み(50文字,書き込み日に*)、エラー(1)か(2.5)へ。ここでカレンダーは表示されるのでしょうか?
それとmenuprint()やselect()の仕組みが少々変わる程度なら引数でmenuprint(引数)やselect(引数)の動作を変えてはどうでしょうか?
で大変だとは思いますが、全体の仕様を書きなおしてみてください。これが出来れば、後はプログラム化するだけです。
仕様ですが、今回はここから始めましょう。これで全部書いてみてください。
(1)MENU操作の表示(最初)
(2) 西暦の入力。エラーなら(2)に戻る。
(2.5)MENU操作(最初) s:(2)へ b:(2)へ g:(3)へ c:エラーなら(2)へ OKなら(4)へ m:メニューの一覧表示して(2.5)へ x:終了へ
Re: メニューを常に表示させたい。
Posted: 2011年6月26日(日) 00:24
by かずま
cal さんが書きました:
MENUはやはり、あと二つ別に作ったほうがよろしいですかね。
カレンダー表示後のRorWに戻れるメニューと、
最後の、西暦からか、RorWか、カレンダーのチェックか終了。
そんなにメニューがたくさんあったら、使いにくくないですか?
また、メニューにある、『次の処理に行くには 「g」を入力』の
次の処理が何を意味するのか分かりにくいと思います。
カレンダーを表示するために、年と月の入力が必要ですが、初期値として
現在の年と月を使い、あとはコマンドで変更するだけでよいのでは?
試しに作ってみました。
1901~2099 を入力すると、年を変更し、カレンダーを表示します。
m1 ~m12 で、月を変更し、カレンダーを表示します。
m だけだと、カレンダーの再表示です。
1~31 を入力すると、その日のメモを表示します。
メモはなくても日付を表示しますから、続けて w で書き込みができます。
d で削除もできます。
w1 ~ w31 で、日付指定で書き込みできます。
d1 ~ d31 で、日付指定で削除できます。
コード:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct Message {
int date;
char *msg;
struct Message *prev, *next;
} Message;
typedef struct {
int year, month, day;
Message root, *message[32];
} Data;
int day_of_week(int y, int m, int d)
{
y -= m < 3;
return (y + y/4 - y/100 + y/400 + ".bed=pen+mad."[m] + d) % 7;
}
int is_leap(int y) { return y % 4 == 0 && (y % 100 || y % 400 == 0); }
int last_day(int y, int m)
{
return (m == 2) ? is_leap(y) + 28 : (0x15aa>>m & 1) + 30;
}
void print_calendar(Data *dp)
{
int w = day_of_week(dp->year, dp->month, 1);
int z = last_day(dp->year, dp->month), d;
printf("%13d年 %d月\n" " 日 月 火 水 木 金 土\n" "%*s",
dp->year, dp->month, w * 4, "");
for (d = 1; d <= z; d++)
printf("%*c%d%c", (d<10)+1, dp->message[d]?'*':' ', d, ++w%7?' ':'\n');
printf(w % 7 ? "\n\n" : "\n");
}
void print_help(void)
{
puts(" q : 終了 (quit)\n"
" h : ヘルプ (help)\n"
" nnnn : 年の変更 (1901~2099)\n"
" m [nn] : 月(month)の変更とカレンダー表示\n"
" nn : その日のメモ表示 (1~31)\n"
" w [nn] : メモの書き込み (write)\n"
" d [nn] : その日のメモの削除 (delete)\n");
}
void print_message(Data *dp)
{
printf("%d/%d/%d: %s", dp->year, dp->month, dp->day,
dp->message[dp->day] ? dp->message[dp->day]->msg : "\n");
}
int confirm(const char *message)
{
char buf[1024];
printf("%s (y/n): ", message);
return fgets(buf, sizeof(buf), stdin) && buf[0] == 'y';
}
void load_message(Data *dp)
{
char buf[1024];
FILE *fp = fopen("Calendar.txt", "r");
if (!fp) return;
dp->root.prev = dp->root.next = &dp->root;
while (fgets(buf, sizeof(buf), fp)) {
int y, m, d, n;
if (sscanf(buf, "%d/%d/%d %n", &y, &m, &d, &n) == 3) {
Message *mp = malloc(sizeof(Message));
mp->date = y << 9 | m << 5 | d;
mp->msg = strdup(buf + n);
mp->prev = dp->root.prev;
mp->next = &dp->root;
dp->root.prev = dp->root.prev->next = mp;
}
}
fclose(fp);
}
void save_message(Data *dp)
{
FILE *fp = fopen("Calendar.txt", "w");
if (fp) {
Message *mp = dp->root.next;
while (mp != &dp->root) {
Message *np = mp->next;
fprintf(fp, "%d/%02d/%02d %s", mp->date >> 9,
mp->date >> 5 & 0x0f, mp->date & 0x1f, mp->msg);
free(mp->msg);
free(mp);
mp = np;
}
}
}
void set_message(Data *dp)
{
int date1 = dp->year << 9 | dp->month << 5 | 1;
int date31 = dp->year << 9 | dp->month << 5 | 31;
Message *mp = dp->root.next;
int d;
for (d = 0; d < 32; d++) dp->message[d] = 0;
for (; mp != &dp->root && mp->date <= date31; mp = mp->next)
if (mp->date >= date1) dp->message[mp->date & 0x1f] = mp;
}
void delete_message(Data *dp)
{
Message *mp = dp->message[dp->day];
if (mp) {
mp->prev->next = mp->next;
mp->next->prev = mp->prev;
free(mp->msg);
free(mp);
dp->message[dp->day] = 0;
}
}
void write_message(Data *dp, const char *buf)
{
Message *mp = dp->message[dp->day];
if (mp) {
free(mp->msg);
mp->msg = strdup(buf);
} else {
int date = dp->year << 9 | dp->month << 5 | dp->day;
Message *np = dp->root.next;
for ( ; np != &dp->root && np->date < date; np = np->next) ;
mp = malloc(sizeof(Message));
mp->date = date;
mp->msg = strdup(buf);
mp->prev = np->prev;
mp->next = np;
np->prev = np->prev->next = mp;
dp->message[dp->day] = mp;
}
}
int main(void)
{
Data data;
char buf[1024];
time_t t = time(0);
struct tm *tp = localtime(&t);
data.year = tp->tm_year + 1900;
data.month = tp->tm_mon + 1;
data.day = tp->tm_mday;
load_message(&data);
set_message(&data);
print_help();
print_calendar(&data);
print_message(&data);
while (printf("> "),fgets(buf, sizeof(buf), stdin)) {
int n = atoi(buf);
if (n > 1900 && n < 2100 && n != data.year) // change year
data.year = n, print_calendar(&data);
else if (n >= 1 && n <= 31) // print message of the day
data.day = n, print_message(&data);
else if (buf[0] == 'm') { // change month and print calendar
n = atoi(buf + 1);
if (n >= 1 && n <= 12 && n != data.month)
data.month = n, set_message(&data);
print_calendar(&data);
} else if (buf[0] == 'w') { // write
n = atoi(buf + 1);
if (n >= 1 && n <= last_day(data.year, data.month)) data.day = n;
printf("%d/%d/%d: ", data.year, data.month, data.day);
if (fgets(buf, sizeof(buf), stdin) && buf[0] != '\n') {
if (!data.message[data.day] || confirm("上書きしますか?"))
write_message(&data, buf), print_calendar(&data);
}
} else if (buf[0] == 'd') { // delete
n = atoi(buf + 1);
if (n >= 1 && n <= last_day(data.year, data.month)) data.day = n;
if (data.message[data.day] && confirm("削除しますか?"))
delete_message(&data), print_calendar(&data);
} else if (buf[0] == 'q') { // quit
if (confirm("終了しますか?")) break;
} else if (buf[0] == 'h') print_help();
}
save_message(&data);
return 0;
}
Re: メニューを常に表示させたい。
Posted: 2011年6月27日(月) 21:15
by かずま
かずま さんが書きました:
試しに作ってみました。
即席で作って、すぐに投稿すると、やはり多くのバグが入ってしまうようです。
Calendar.txt がないと起動しないとか、6/31 を指定できてしまうとか、save_message() の中で fclose していないとか。
malloc や strdup の結果のチェックはわざとしていません。
他にも変な記述がたくさんあります。
元の質問者が課題としてを与えられていた問題だとしても、そのまま解答として提出できないようにと考えています。
あくまでも、参考程度に利用してください。
いろいろアドバイスすることが教育的でよいのかもしれませんが、ある程度動くプログラムを一所懸命読んでもらって、わからないところを自分で調べてみることも十分勉強になることだと思います。
コード:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct Message {
int date;
char *msg;
struct Message *prev, *next;
} Message;
typedef struct {
int year, month, day;
Message root, *message[32];
} Data;
void print_help(void)
{
puts(" q : 終了 (quit)\n"
" h : ヘルプ (help)\n"
" 1901~2099 : 年の指定\n"
" m [1~12] : 月の指定とカレンダーの表示 (month)\n"
" 1~31 : 日の指定とメモの表示\n"
" w [1~31] : 日の指定とメモの書き込み (write)\n"
" d [1~31] : 日の指定とメモの削除 (delete)\n");
}
int day_of_week(int y, int m, int d)
{
y -= m < 3;
return (y + y/4 - y/100 + y/400 + ".bed=pen+mad."[m] + d) % 7;
}
#define IS_LEAP(y) ((y) % 4 == 0 && ((y) % 100 || (y) % 400 == 0))
int last_day(int y, int m)
{
return (m == 2) ? IS_LEAP(y) + 28 : (0x15aa>>m & 1) + 30;
}
void print_calendar(Data *dp)
{
int w = day_of_week(dp->year, dp->month, 1);
int z = last_day(dp->year, dp->month), d;
printf("%13d年 %d月\n" " 日 月 火 水 木 金 土\n" "%*s",
dp->year, dp->month, w * 4, "");
for (d = 1; d <= z; d++)
printf("%*c%d%c", (d<10)+1, dp->message[d]?'*':' ', d, ++w%7?' ':'\n');
puts(w % 7 ? "\n" : "");
}
void print_message(Data *dp)
{
printf("%d/%d/%d: %s", dp->year, dp->month, dp->day,
dp->message[dp->day] ? dp->message[dp->day]->msg : "\n");
}
int confirm(const char *message)
{
char buf[1024];
printf("%s (y/n): ", message);
return fgets(buf, sizeof(buf), stdin) && buf[0] == 'y';
}
Message *insert(Message *pos, int date, const char *msg)
{
Message *mp = malloc(sizeof(Message));
mp->date = date;
mp->msg = strdup(msg);
mp->prev = pos->prev;
mp->next = pos;
return pos->prev = pos->prev->next = mp;
}
void load_message(Data *dp)
{
char buf[1024];
int y, m, d, n;
FILE *fp = fopen("Calendar.txt", "r");
dp->root.prev = dp->root.next = &dp->root;
if (!fp) return;
while (fgets(buf, sizeof(buf), fp))
if (sscanf(buf, "%d/%d/%d %n", &y, &m, &d, &n) == 3)
insert(&dp->root, y << 9 | m << 5 | d, buf + n);
fclose(fp);
}
void save_message(Data *dp)
{
FILE *fp = fopen("Calendar.txt", "w");
if (fp) {
Message *mp = dp->root.next;
while (mp != &dp->root) {
Message *np = mp->next;
fprintf(fp, "%d/%02d/%02d %s", mp->date >> 9,
mp->date >> 5 & 0x0f, mp->date & 0x1f, mp->msg);
free(mp->msg);
free(mp);
mp = np;
}
fclose(fp);
}
}
void set_message(Data *dp)
{
int date1 = dp->year << 9 | dp->month << 5 | 1;
int date31 = dp->year << 9 | dp->month << 5 | 31;
Message *mp = dp->root.next;
int d;
for (d = 0; d < 32; d++) dp->message[d] = 0;
for (; mp != &dp->root && mp->date <= date31; mp = mp->next)
if (mp->date >= date1) dp->message[mp->date & 0x1f] = mp;
}
void delete_message(Data *dp)
{
Message *mp = dp->message[dp->day];
if (mp) {
mp->prev->next = mp->next;
mp->next->prev = mp->prev;
free(mp->msg);
free(mp);
dp->message[dp->day] = 0;
print_calendar(dp);
}
}
void write_message(Data *dp, const char *buf)
{
Message *mp = dp->message[dp->day];
if (mp) {
free(mp->msg);
mp->msg = strdup(buf);
} else {
int date = dp->year << 9 | dp->month << 5 | dp->day;
for (mp = dp->root.next; mp != &dp->root && mp->date < date; mp = mp->next) ;
dp->message[dp->day] = insert(mp, date, buf);
}
print_calendar(dp);
}
int main(void)
{
Data data;
char buf[1024];
time_t t = time(0);
struct tm *tp = localtime(&t);
data.year = tp->tm_year + 1900;
data.month = tp->tm_mon + 1;
data.day = tp->tm_mday;
load_message(&data);
set_message(&data);
print_help();
print_calendar(&data);
print_message(&data);
while (printf("> "), fgets(buf, sizeof(buf), stdin)) {
int n = atoi(buf);
if (n > 1900 && n < 2100 && n != data.year) // change year
data.year = n, print_calendar(&data);
else if (n >= 1 && n <= last_day(data.year, data.month)) // the day
data.day = n, print_message(&data);
else if (buf[0] == 'm') { // change month and print calendar
n = atoi(buf + 1);
if (n >= 1 && n <= 12 && n != data.month)
data.month = n, set_message(&data);
print_calendar(&data);
} else if (buf[0] == 'w') { // write
n = atoi(buf + 1);
if (n >= 1 && n <= last_day(data.year, data.month)) data.day = n;
printf("%d/%d/%d: ", data.year, data.month, data.day);
if (fgets(buf, sizeof(buf), stdin) && buf[0] != '\n')
if (!data.message[data.day] || confirm("上書きしますか?"))
write_message(&data, buf);
} else if (buf[0] == 'd') { // delete
n = atoi(buf + 1);
if (n >= 1 && n <= last_day(data.year, data.month)) data.day = n;
if (data.message[data.day] && confirm("削除しますか?"))
delete_message(&data);
} else if (buf[0] == 'q') { // quit
if (confirm("終了しますか?")) break;
} else if (buf[0] == 'h') print_help();
}
save_message(&data);
return 0;
}