diary.c

私の最初の Linux C プログラムは、diary.c です。これは、年と月を入力してやれば、その月の日付と曜日を計算して、日記形式の HTML を自動的に作り出すものです。

<p><b>1月1日(月)</b></p>
<p><b>1月2日(火)</b></p>
<p><b>1月3日(水)</b></p>

というのが基本のかたちです。

日付と曜日の計算は、以前使ったことのある次の関数を利用しました。

find_first_last(int year, int month)
{
static int month_day[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int i, days;

if((year%4)==0 && (year%100)!=0 || (year%400)==0) /* leap year or not? */
    month_day[2]=29;
days=year+(year-1)/4-(year-1)/100+(year-1)/400;   /* before the year */
for(i=0;i<month;i++)                              /* before the month */
    days+=month_day[i];
first_day=days%7;  /* sun=0 mon=1 tue=2 wed=3 thu=4 fri=5 sat=6 */
last_date=month_day[month];           /* the last day of the month */
}

ここでは、最初に、各月の日数をあらかじめ month_day にいれておき、入力した年がうるう年かどうかの判定をし、うるう年なら2月の日数を訂正するようにしてあります。次に、入力した年と月から、その月の1日が何曜日にあたるか、その月が何日まであるかを計算しています。

それで、main() は次のようになります。

while(day < last_date){
    n=(first_day + day)%7;
    fprintf(fp,"<p><b>%d月%d日(%s)</b></p>\n\n", month, day+1, yobi[n]);
    day++;
    }

yobi は、日本語で、「日」から「土」を次のように割り当てました。

strcpy(yobi[0],"日");
strcpy(yobi[1],"月");
strcpy(yobi[2],"火");
strcpy(yobi[3],"水");
strcpy(yobi[4],"木");
strcpy(yobi[5],"金");
strcpy(yobi[6],"土");

年と月の入力は、コマンドラインからの入力と、コンソールからの入力の両方にし、コマンドラインに年月の入力がなければ、コンソールでプロンプトを出すようにしました。setup() がそれです。コマンドラインからの年月の入力は年の下二桁と月の二桁の組合せにしました。0102 という形式です。このプログラムでは 20xx年だけに対応しており 199x年の入力は無視しています。

if(argc>1)
    input=atoi(argv[1]);
else
    input=setup();

year=input/100;
month=input%100;

if(month==0 || month>12){
    printf("\nIncorrect Input\n");
    exit(0);
    }

find_first_last(2000+year, month);

さて、コンソールからの入力ですが、これには <curses.h> が必要です。initscr() でコンソールをイニシャライズし、endwin() で閉じます。その間に mvprintw(row, col, string) コマンドを使って、指定した位置に指定した文字列を描いています。カーソールを動かした後は refresh() で画面を更新しています。

int setup()
{
int year, month, ch;

initscr();
mvprintw(5, 5, "Making <diary.html>...");
refresh();
mvprintw(7, 5, "Year: **");
mvprintw(7, 20, "Month: **");
refresh();
strinput(7, 5, "Year: ", 2);
year=atoi(gbuf);
strinput(7, 20, "Month: ", 2);
month=atoi(gbuf);
mvprintw(10,5, "[BS]=Try again");
mvprintw(9, 5, "Year=%d, Month=%d. Ok? ", 2000+year, month);
refresh();
ch=getch();
endwin();
if(ch=='\x1b'){
    printf("\nTerminated by user.\n");
    exit(0);
    }
if(ch=='\b')
    setup();
return year*100+month;
}

上記のファンクションの中の strinput(row, col, string, length) は getch() を使って次のように作りました。

strinput(int row, int col, char *message, int ln)
{
int  c,i=0;

mvprintw(row, col, message);
col=col+strlen(message);
move(row, col);
refresh();

while(i<ln){
    c=getch();
    if(c=='\n' || c=='\t')
        break;
    if(c=='\x1b'){
        endwin();
        printf("\nTerminated by user\n");
        exit(0);
        }

    else if(c=='\b'){
        if(i==0){
            move(row, col);
            i=0;
            refresh();
            }
        else{
            delch();
            i--;
            }
        }
    else{
        if(i==ln){
            move(row, col+ln);
            delch();
            refresh();
            }
        else{
            gbuf[i]=c;
            i++;
            }
        }
    }
gbuf[i]='\0';
}

ここで、文字列の長さをチェックさせたのは、年数二桁を記入したら自動的に月を記入できるようにするためです。入力した文字列はグローバル変数の gbuf に格納し、他のルーチンに渡しています。

最後に工夫したのは、表題に全角数字を入れることです。このため、半角数字を全角数字に変えるファンクションを作りました。ダイアリーページは S-JIS コードで作っていますので、これを次のように変換しています。変換した文字列はグローバル変数 znum に格納していています。

z_number(int number)
{
int i, j;
char buf[7];

sprintf(buf, "%d", number);
for(i=0, j=0; buf[i]!='\0'; i++, j+=2){
    znum[j]='\x82';
    znum[j+1]=buf[i]-'0'+'\x4f';
    }
znum[j]='\0';
}

次は diary.c の全体です。これをコンパイルするには ncurses パッケージが必要です。次のコマンドでコンパイルしてください。

gcc -I/usr/include/ncurses diary.c -o diary -lncurses

次のシェルスクリプトを作っておけば、次からは、C ファイル名だけで、ncurses の必要なソースコードをコンパイルできます。

#!/bin/bash
gcc -I/usr/include/ncurses $1.c -o $1 -lncurses


/*

diary.c

make diaryXXXX.html

*/

#include <stdio.h>
#include <curses.h>

#define SIZE    250

/* global var */

char gbuf[5], znum[9];
int  first_day, last_date;

main(int argc, char **argv)
{
FILE *fp;
int  input, year, month, day=0, n;
char yobi[7][3], outname[15];

strcpy(yobi[0],"日");
strcpy(yobi[1],"月");
strcpy(yobi[2],"火");
strcpy(yobi[3],"水");
strcpy(yobi[4],"木");
strcpy(yobi[5],"金");
strcpy(yobi[6],"土");

if(argc>1)
    input=atoi(argv[1]);
else
    input=setup();

year=input/100;
month=input%100;

if(month==0 || month>12){
    printf("\nIncorrect Input\n");
    exit(0);
    }

find_first_last(2000+year, month);

sprintf(outname, "diary%02d%02d.html", year, month);
if((fp=fopen(outname,"w"))==NULL){
    printf("\nCan't create <%s>", outname);
    exit(0);
    }

fprintf(fp, "<html>\n<head>\n<meta http-equiv=\"Content-Type\" ");
fprintf(fp, "content=\"text/html; charset=EUC-JP\">\n");
fprintf(fp, "<title>USA Diary</title>\n</head>\n<body>\n");
fprintf(fp, "<center><table cellpadding=\"20\"><tr><td>");
fprintf(fp, "<img src=\"../images/%02d%02d.gif\"></td>\n", year, month);
z_number(2000+year);
fprintf(fp, "<td><h2>USA Diary %s年", znum);
z_number(month);
fprintf(fp, "%s月</td></tr>\n", znum);
fprintf(fp, "</table></center>\n");
fprintf(fp, "<p align=\"center\">");
fprintf(fp, "<a href=\"diary%02d%02d.html\">",
    year-(month==1), (month-1)*(month>1)+12*(month==1));
fprintf(fp, "先月分を見る</a>");
fprintf(fp, "<!--     <a href=\"diary.html\">
    <!-- <a href=\"diary%02d%02d.html\">次月分を見る</a --></p>\n\n",
    year+(month==12), (month+1)*(month<12)+(month==12));
fprintf(fp, "<!--\n");

while(day < last_date){
    n=(first_day + day)%7;
    fprintf(fp,"<p><b>%d月%d日(%s)</b></p>\n\n", month, day+1, yobi[n]);
    day++;
    }

fprintf(fp, "-->\n<br>\n<div align=\"center\">\n<form>");
fprintf(fp, "<input type=\"button\" ");
fprintf(fp, "value=\"ホームページにもどる\" ");
fprintf(fp, "onclick=\"location='../index.html'\"></form>\n");
fprintf(fp, "</div>\n</body>\n</html>\n");

printf("\nCompleted on <%s>\n\n", outname);
}

find_first_last(int year, int month)
{
static int month_day[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int i, days;

if((year%4)==0 && (year%100)!=0 || (year%400)==0) /* leap year or not? */
    month_day[2]=29;
days=year+(year-1)/4-(year-1)/100+(year-1)/400;   /* before the year */
for(i=0;i<month;i++)                              /* before the month */
    days+=month_day[i];
first_day=days%7;  /* sun=0 mon=1 tue=2 wed=3 thu=4 fri=5 sat=6 */
last_date=month_day[month];           /* the last day of the month */
}

int setup()
{
int year, month, ch;

initscr();
mvprintw(5, 5, "Making <diary.html>...");
refresh();
mvprintw(7, 5, "Year: **");
mvprintw(7, 20, "Month: **");
refresh();
strinput(7, 5, "Year: ", 2);
year=atoi(gbuf);
strinput(7, 20, "Month: ", 2);
month=atoi(gbuf);
mvprintw(10,5, "[BS]=Try again");
mvprintw(9, 5, "Year=%d, Month=%d. Ok? ", 2000+year, month);
refresh();
ch=getch();
endwin();
if(ch=='\x1b'){
    printf("\nTerminated by user.\n");
    exit(0);
    }
if(ch=='\b')
    setup();
return year*100+month;
}

strinput(int row, int col, char *message, int ln)
{
int  c,i=0;

mvprintw(row, col, message);
col=col+strlen(message);
move(row, col);
refresh();

while(i<ln){
    c=getch();
    if(c=='\n' || c=='\t')
        break;
    if(c=='\x1b'){
        endwin();
        printf("\nTerminated by user\n");
        exit(0);
        }

    else if(c=='\b'){
        if(i==0){
            move(row, col);
            i=0;
            refresh();
            }
        else{
            delch();
            i--;
            }
        }
    else{
        if(i==ln){
            move(row, col+ln);
            delch();
            refresh();
            }
        else{
            gbuf[i]=c;
            i++;
            }
        }
    }
gbuf[i]='\0';
}

z_number(int number)
{
int i, j;
char buf[7];

sprintf(buf, "%d", number);
for(i=0, j=0; buf[i]!='\0'; i++, j+=2){
    znum[j]='\x82';
    znum[j+1]=buf[i]-'0'+'\x4f';
    }
znum[j]='\0';
}

[目次にもどる]