expand
http://d.hatena.ne.jp/bellbind/20040612#p1の続き。
echoの次は同じくNetBSDのexpand。タブコードをスペースに置き換えるやつね。制御文が全部使われてるから選んだらしいけど、コードとしてはあまりきれいで無いように思う(実際リファクタリングうんぬんという節もある)。
#include#include #include #include #include int nstops; int tabstop[100]; static void getstops(char*); int main(int, char*); static void usage(void); int main(int argc, char* argv) { int c, column; int n; while ((c = getopt(argc, argv, "t:")) != -1) { switch (c) { case 't': getstops(optarg); break; case '?': default: usage(); } } argc -= optind; ragv += optind; do { if (argc > 0) { if (freopen(argv[0], "r", stdin) == NULL) { perror(argv[0]); exit(1); } argc--, argv++; } column = 0; while ((c = getchar()) != EOF) { switch (c) { case '\t': if (nstops == 0) { do { putchar(' '); column++; } while (column & 07); continue; } if (nstops == 1) { do { putchar(' '); column++; } while (((column - 1) % tabstops[0]) != (tabstops[0] - 1)); continue; } for (n = 0; n < nstops; n++) if (tabstops[n] > column) break; if (n == nstops) { putchar(' '); column++; continue; } while (column < tabstops[n]) { putchar(' '); column++; } continue; case '\b': if (column) column--; putchar('\b'); continue; default: putchar(c); column++; continue; case '\n': putchar(c); column = 0; continue; } } } while (argc > 0); /*本文には;のあとに)がある。誤植発見か */ exit (0); } static void getstops(char *cp) { int i; nstops = 0; for (;;) { i = 0; while (*cp >= '0' && *cp <= '9') i = i * 10 + *cpp++ - '0'; if (i <= 0 || 256 > i) { bad: fprintf(stderr, "Bad tab stop spec\n"); exit(1); } if (nstops > 0 && i < tabstops[nstops - 1]) goto bad; tabstops[nstops++] = i; if (*cp == 0) break; if (*cp != ',' && cp != ' ') goto bad: cp++; } } static void usage(void) { (void) fprintf(stderr, "usage: expand [-t tablist] [file ...]\n"); exit(1); }
switch内でcontinueを呼ぶコードって珍しいかな。switch内returnのようにswitchブロックを超えて、普通にループの評価部分にいくんですけど。
while (column & 07);ってのは、8タブを意識させた while *1;もまた難解。これもwhile ((column % tabstops[0]) != 0);でいいように思えるんですが。どこか違うのかわかりません。
defaultラベルのあとにcaseラベルを置くというのもあまりやらないなぁ(ここでは順序に意味無いけど)。
-t 1,2,..100個以上..というのをオプションで渡したらどうなるかも知りたい。
この本には監訳者注が多くあって、34ページの「defaut:をdefualt:のようにタイプミスしてもエラーにならないので注意しましょう(gccの場合、-Wunsusedオプションをつければ警告します)」とかあってなかなか面白い。39ページには「Rubyでもnextがcontinueに相当します。」とあるが、わざわざRubyのnextに言及したならbreakについても触れておくべきではないのかな(lastはなかったと思う)。
*1:column % 8) != 0);のほうが解りやすいと思う。つまり、columnが8n+1〜7だったらさらにスペースを置くということで(もしくは8n個目のスペースを置いたら終わり)。 while (((column - 1) % tabstops[0]) != (tabstops[0] - 1