トップ «前の日記(2014-05-23) 最新 次の日記(2014-05-25)» 編集

ヨタの日々

2001|08|09|10|11|12|
2002|01|02|03|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|12|
2024|01|02|03|04|

2014-05-24 :-)

_ 午前

1030 起床 && 部屋掃除

_ 午後

1200 おひる。うどん

1430 外出

1500 生田緑地

_

1700 買い物

1900 アニメ消化

2230 飯

_ 生田緑地ばら苑

行ってきた

開園から 2 週間目だし、ブログを見たら「見頃を迎えています」とあったので( 小川宏氏のばら: 生田緑地ばら苑 公式ブログ ) 駐車場が超絶混雑してそうなのでリスクを回避するために自転車で行ってきた。片道 30 分くらい。

入場口についたら入場待ちの車が 20 台くらい待機していた。自転車で来たのは正解だった。(または電車でもどうぞ)

IMG_4929

IMG_4943

IMG_4968

芝生に座ってヨタヨタする。ばら苑の入り口付近の売店で買ったものを食べるなど。

IMG_4983

IMG_4984

IMG_5004

_ [NetBSD][/bin/date][コードリーディング]NetBSD /bin/date を読む

ソース src/bin/date/date.c

マニュアル date - NetBSD Manual Pages

日付を表示するときの流れ

main

日付を設定するときの流れ

main
setthetime
netsettime (?)
settimeofday

main から読んでいく。

お馴染みの処理。

setprogname(argv[0]);
(void)setlocale(LC_ALL, "");

ここでいろいろ分岐している。

  // -r されてなかったらまず time で tval を初期化。

  if (!rflag && time(&tval) == -1)
    err(EXIT_FAILURE, "time");


  // date "+%Y/%m/%d%n %H:%M:%S" などと使われた場合
  // format に argv ( +%Y/%m/%d%n %H:%M:%S ) が入る

  /* allow the operands in any order */
  if (*argv && **argv == '+') {
    format = *argv;
    ++argv;
  } else

    // デフォルトがこのフォーマット
    format = "+%a %b %e %H:%M:%S %Z %Y";

  // date 20140524 などと使われた場合は↑は else を通るので
  // ここで argv は 20140524 が入っている
  // それを使って setthetime を呼ぶ
  if (*argv) {
    setthetime(*argv);
    ++argv;
  }

  // これはなんだ....
  if (*argv && **argv == '+')
    format = *argv;

  // 時間の文字列を格納するための配列。とりあえず 1024 としておく。
  // あとで strftime したときのために malloc で作っておく。
  if ((buf = malloc(bufsiz = 1024)) == NULL)
    goto bad;

  // localtime してる
  if ((tm = localtime(&tval)) == NULL)
    err(EXIT_FAILURE, "localtime %lld failed", (long long)tval);

  // strftime する
  // 0 (たぶんエラー) が返ってきたらとりあえず配列サイズを 2 倍にして再確保してリトライ
  while (strftime(buf, bufsiz, format, tm) == 0)
    if ((buf = realloc(buf, bufsiz <<= 1)) == NULL)
      goto bad;

  // 最後に印字して終了
  (void)printf("%s\n", buf + 1);
  free(buf);
  return 0;

strftime は結局 非 0 なら正常終了、0 ならエラーと言ってるようだ。

strftime(3) - NetBSD Manual Pages

     No more than maxsize characters will be placed into the array.  If the
     total number of resulting characters, including the terminating null
     character, is not more than maxsize, strftime() returns the number of
     characters in the array, not counting the terminating null.  Otherwise,
     zero is returned and the contents of the array are undefined.

ところで

time(3) - NetBSD Manual Pages

     Upon successful completion, time() returns the value of time.  Otherwise
     a value of ((time_t) -1) is returned and the global variable errno is set
     to indicate the error.

おかしいときは -1 を返すよ。errno に値を設定するよ

ERRORS
     No errors are defined.

でもエラーが無い。

さて

日付を設定するときの処理 setthetime を読む。

  for (t = p, dot = NULL; *t; ++t) {
    if (isdigit((unsigned char)*t))
      continue;
    if (*t == '.' && dot == NULL) {
      dot = t;
      continue;
    }
    badformat();
  }
  if (dot != NULL) {      /* .ss */
    len = strlen(dot);
    if (len != 3)
      badformat();
    ++dot;
    lt->tm_sec = ATOI2(dot);
    if (lt->tm_sec > 61)
      badvalue("seconds");
  } else {
    len = 0;
    lt->tm_sec = 0;
  }

ここでは

[[[[[[CC]yy]mm]dd]HH]MM[.SS]]

というフォーマットの最後の .SS が含まれているかどうかをチェックしている。↑の dot は .SS の . を意味する。

ここに出てきた ATOI2 は setthetime のすぐ上で定義されている。

#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))

ようするに文字列ポインタを 2 つ進めて、2 つ通り過ぎた 2 文字を 10 進法数値として扱う。

たとえば 20140524 という文字列の場合を考えてみる。

最初に呼び出すとき

ATOI2("20140524")
       ~~

"20" が取り出されて 20(10進法) となる。

次に呼び出すとき

ATOI2("20140524")
         ~~

"14" が取り出されて 14(10進法) となる。

以下同様。

switch ではこの仕組を利用して渡されたフォーマットを解析している。

  yearset = 0;
  switch (strlen(p) - len) {
  case 12:        /* cc */
    lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE;
    if (lt->tm_year < 0)
      badtime();
    yearset = 1;
    /* FALLTHROUGH */
  case 10:        /* yy */
    if (yearset) {
      lt->tm_year += ATOI2(p);
    } else {
      yearset = ATOI2(p);
      if (yearset < 69)
        lt->tm_year = yearset + 2000 - TM_YEAR_BASE;
      else
        lt->tm_year = yearset + 1900 - TM_YEAR_BASE;
    }
    /* FALLTHROUGH */
  case 8:          /* mm */
    lt->tm_mon = ATOI2(p);
    if (lt->tm_mon > 12 || lt->tm_mon == 0)
      badvalue("month");
    --lt->tm_mon;      /* time struct is 0 - 11 */
    /* FALLTHROUGH */
  case 6:          /* dd */
    lt->tm_mday = ATOI2(p);
    switch (lt->tm_mon) {
    case 0:
    case 2:
    case 4:
    case 6:
    case 7:
    case 9:
    case 11:
      if (lt->tm_mday > 31 || lt->tm_mday == 0)
        badvalue("day of month");
      break;
    case 3:
    case 5:
    case 8:
    case 10:
      if (lt->tm_mday > 30 || lt->tm_mday == 0)
        badvalue("day of month");
      break;
    case 1:
      if (lt->tm_mday > 29 || lt->tm_mday == 0 ||
          (lt->tm_mday == 29 &&
           !isleap(lt->tm_year + TM_YEAR_BASE)))
        badvalue("day of month");
      break;
    default:
      badvalue("month");
      break;
    }
    /* FALLTHROUGH */
  case 4:          /* hh */
    lt->tm_hour = ATOI2(p);
    if (lt->tm_hour > 23)
      badvalue("hour");
    /* FALLTHROUGH */
  case 2:          /* mm */
    lt->tm_min = ATOI2(p);
    if (lt->tm_min > 59)
      badvalue("minute");
    break;
  case 0:          /* was just .sss */
    if (len != 0)
      break;
    /* FALLTHROUGH */
  default:
    badformat();
  }

ここに出てくる TM_YEAR_BASE は src/lib/libc/time/tzfile.h で定義されてるものかな。以下の値となっている。2000 年問題ェ...

#define TM_YEAR_BASE  1900

FALLTHROUGH とあるように渡されたフォーマットを五月雨で処理していく。プログラミング言語 C には伝統として「switch case で break し忘れる」というバグがよく話題になるので、コメントとして書いておかないと break を意図して省いているのかどうか分からないのである。

実際に日付を設定している処理がここ。だと思う。

  // or で繋がっているので nflag がどのような値であってもこの if は通る。
  // つまり必ず netsettime が呼ばれる。はず
  /* set the time */
  if (nflag || netsettime(new_time)) {
    logwtmp("|", "date", "");
    if (aflag) {
      tv.tv_sec = new_time - tval;
      tv.tv_usec = 0;
      if (adjtime(&tv, NULL))
        err(EXIT_FAILURE, "adjtime");
    } else {
      tval = new_time;
      tv.tv_sec = tval;
      tv.tv_usec = 0;

      // ここで日付を設定

      if (settimeofday(&tv, NULL))
        err(EXIT_FAILURE, "settimeofday");
    }
    logwtmp("{", "date", "");
  }

nflag がなんのためにあるのか分からんのだが、netsettime を覗いてみる。

ソースは src/bin/date/netdate.c にある。

冒頭のコメント

/*
 * Set the date in the machines controlled by timedaemons by communicating the
 * new date to the local timedaemon.  If the timedaemon is in the master state,
 * it performs the correction on all slaves.  If it is in the slave state, it
 * notifies the master that a correction is needed.
 * Returns 0 on success.  Returns > 0 on failure.
 */

結局 timed デーモンが起動していない場合はエラーになるけど、返り値は 0 超の値を返すらしい。

そして netsettime から返って ↑ の処理に戻る。ようだ。

netsettime の呼び出しが無駄なような...

_ [艦これ]艦これ

天津風を改造した。

4-4 出撃。撃破 3 回目。