明解C言語中級編 6章の自由課題解いてみた 中編

課題6-5と6-6はさくっと終わったんだけど、次の更新もいつになるかも分からないので中編ってことで更新しようかなと。今回も1個のプログラムで回答してます。

問題
6-5 list6-17のカレンダープログラムは月を文字で指定したときに先頭3文字で判定している。3文字目以降も誤ってたら不一致となるようにしんさい。
6-6 list6-17のプログラムを改良してlist6-12のように、横に3か月並べて表示するようにしんさい。

そーす

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

int mday[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

int dayofweek(int year, int month, int day);
int is_leap(int year);
int monthdays(int year, int month);
void make_calendar(int y, int m, char s[7][22]);
void print(char sbuf[3][7][22], int n);
void put_calendar(int y1, int m1, int y2, int m2);
int get_month(char *s);
int strncmpx(const char *s1, const char *s2, size_t n);

int main(int argc, char *argv[])
{
   int y1, m1, y2, m2;
   int retry=0;

   time_t t = time(NULL);
   struct tm *local = localtime(&t);

   y1 = local->tm_year + 1900;
   m1 = local->tm_mon + 1;

   if(argc >= 2){
     m1 = get_month(argv[1]);
     if(m1 < 0 || m1 > 12){
       fprintf(stderr, "月の値が不正です。\n");
       return 1;
     }
   }

   if(argc >= 3){
     y1 = atoi(argv[2]);
     if(y1 < 0){
       fprintf(stderr, "年の値が不正です。\n");
       return 1;
     }
   }

   if(argc >= 2){
     m2 = get_month(argv[3]);
     if(m2 < 0 || m2 > 12){
       fprintf(stderr, "月の値が不正です。\n");
       return 1;
     }
   }

   if(argc >= 3){
     y2 = atoi(argv[4]);
     if(y2 < 0){
       fprintf(stderr, "年の値が不正です。\n");
       return 1;
     }
   }

   printf("%d年%d月から%d年%d月までのカレンダー\n\n", y1, m1, y2, m2);

   put_calendar(y1, m1, y2, m2);

   return 0;
}

int dayofweek(int year, int month, int day)
{
  if(month==1 || month==2){
   year--;
   month += 12;
  }
  return((year + year/4 - year/100 + year/400 + (13*month+8)/5 + day) % 7);
}

int is_leap(int year)
{
  return(year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
}

int monthdays(int year, int month)
{
  if(month-- != 2)
   return (mday[month]);
  return(mday[month] + is_leap(year));
}

void make_calendar(int y, int m, char s[7][22])
{
  int i, k;
  int wd = dayofweek(y, m, 1);
  int mdays = monthdays(y, m);
  char tmp[4];

  sprintf(s[0], "%10d / %02d      ", y, m);

  for(k=1; k<7; k++)
   s[k][0]='\0';

  k=1;
  sprintf(s[k], "%*s", 3 * wd, "");

  for(i = 1; i <= mdays; i++){
     sprintf(tmp, "%3d", i);
     strcat(s[k], tmp);
     if(++wd % 7 ==0)
       if(s[k])
       k++;
  }

//    if(wd % 7 == 0)
//        k--;
//    else{
//        for(wd %= 7; wd < 7; wd++)
//            strcat(s[k], "   ");
//   }
   while(++k < 7 )
     sprintf(s[k], "");
}

void print(char sbuf[3][7][22], int n)
{
  int i, j;

   if(n==3){
  for(i=0; i<2; i++)
   printf("%21s   ", sbuf[i][0]);
     printf("%21s", sbuf[2][0]);
  putchar('\n');

  for(i=0; i<2; i++)
   printf("  日 月 火 水 木 金 土  ");
     printf(" 日 月 火 水 木 金 土 ");
  putchar('\n');

  for(i=0; i<2; i++)
   printf("----------------------  ");
     printf("---------------------");
  putchar('\n');

   for(i=1; i<7; i++){
       for(j=0; j<2; j++)
     printf("%-22s  ", sbuf[j][i]);
         printf("%-22s", sbuf[2][i]);
     if(i==5)
       if(*sbuf[0][6]==*sbuf[1][6] && *sbuf[1][6]==*sbuf[2][6])
         i+=2;

     putchar('\n');
     }

   putchar('\n');

   }else{
  for(i=0; i<n; i++)
   printf("%22s   ", sbuf[i][0]);
  putchar('\n');

  for(i=0; i<n; i++)
   printf("  日 月 火 水 木 金 土  ");
  putchar('\n');

  for(i=0; i<n; i++)
   printf("----------------------  ");
  putchar('\n');

   for(i=1; i<7; i++){
     for(j=0; j<n; j++)
      printf("%-22s  ", sbuf[j][i]);
   putchar('\n');
   }

   putchar('\n');
   }
}

void put_calendar(int y1, int m1, int y2, int m2)
{
  int y = y1;
  int m = m1;
  int n = 0;
  char sbuf[3][7][22];

  while(y <= y2){
   if(y == y2 && m > m2) break;
   make_calendar(y, m, sbuf[n++]);
   if(n==3){
    print(sbuf, n);
    n = 0;
   }
   m++;
   if(m==13 && y < y2){
    y++;
    m = 1;
   }
  }
  if(n)
   print(sbuf, n);
}

int get_month(char *s)
{
  int i;
  int m;
  int len;
  char *month[] = {"", "January", "February", "March", "April",
      "May", "June", "July",  "August", "September",
      "October", "November", "December"};

  len = strlen(s);

  m = atoi(s);
  if(m >= 1 && m <= 12)
   return m;

  for(i = 1; i <= 12; i++)
   if(strncmpx(month[i], s, len) == 0)
    return i;

  return -1;
}

int strncmpx(const char *s1, const char *s2, size_t n)
{
  while(n && toupper(*s1) && toupper(*s2)){
   if(toupper(*s1) != toupper(*s2))
    return ((unsigned char)*s1 - (unsigned char)*s2);
   s1++;
   s2++;
   n--;
  }

  if(!n) return 0;
  if(*s1) return 1;
  return -1;
}

実行結果

$ ./a.out january 2014 may 2014
2014年1月から2014年5月までのカレンダー

      2014 / 01               2014 / 02               2014 / 03
  日 月 火 水 木 金 土      日 月 火 水 木 金 土    日 月 火 水 木 金 土
----------------------  ----------------------  ---------------------
           1  2  3  4                       1                       1
  5  6  7  8  9 10 11     2  3  4  5  6  7  8     2  3  4  5  6  7  8
 12 13 14 15 16 17 18     9 10 11 12 13 14 15     9 10 11 12 13 14 15
 19 20 21 22 23 24 25    16 17 18 19 20 21 22    16 17 18 19 20 21 22
 26 27 28 29 30 31       23 24 25 26 27 28       23 24 25 26 27 28 29
                                                 30 31

       2014 / 04                2014 / 05
  日 月 火 水 木 金 土      日 月 火 水 木 金 土
----------------------  ----------------------
        1  2  3  4  5                 1  2  3
  6  7  8  9 10 11 12     4  5  6  7  8  9 10
 13 14 15 16 17 18 19    11 12 13 14 15 16 17
 20 21 22 23 24 25 26    18 19 20 21 22 23 24
 27 28 29 30             25 26 27 28 29 30 31

スペル間違いした例

$ ./a.out janualy 2014 may 2014
月の値が不正です。

こんな感じでした。list16を改造したのかlist12を改造したのかよく分からんことになってますが、骨組みは同じなので同じかなぁ。

職場でちょこっとPHPを使ったんですが、違うというと違うんだけど、なんとなくで出来ちゃうのでCをやるとイイね!という感じです。
ただしClassを使ったやうなオブジェクト指向のやうなソースはまだ読めないので、その辺はC++をやるまではまぁいいかなってな感じでござんす。