はじめての C

C programming note*1
今度は、1行に 入る 文字数を、プログラムの中で 定義するかわりに、

コマンドラインの パラメータとして

$./ce3r -w30

のように 指定して 実行するだけで、コンパイルして プログラムを つくり直すことなく、既定値の 80 文字の 代わりに 30 文字の 幅で、中央揃えするというように 実行時に 値を 指定できるよう (p140)

改良していきます。 このとき、

-w と その後の 数字 (この例では 30) の間には 空白を 空けずに 続けて タイプ

することと します ← コードが 複雑に ならないので。
まず はじめは、オプション -w に 続く文字が すべて 数字 から なっているか どうか、チェックする 関数、

#include <ctype.h>

#define YES 1
#define NO 0

int alldigit(char *s)
{
if (*s == '\0')
return NO;
for ( ; *s != '\0'; s++) {
if (!isdigit(*s))
return NO;
}
return YES;
}

まず、if文で 文字列の 先頭が '\0' かどうかを チェックしています。 この関数は int型の 戻り値を もつので、'\0' であれば NO つまり 0 を 返します。
for文では、文字列を 順に 見ていって、標準ライブラリ関数 isdigit() を使い、それが 数字か 否かを チェックしています。
ここでは、否定演算子 '!' が 使われていて、1つでも 数字以外の 文字が 見つかれば 0 を返し、そうでなければ YES つまり 1 を 返します。
オプション処理は main() 関数の はじめで 実行されます。
その前に、もう1つ、まちがって 入力したとき、その関数の 簡単な 使い方を 標準エラー出力で 表示し、プログラムを 終了させる 関数を つくっておきます。
void usage(void)
{
fprintf(stderr, "Usage: cc3r [-wNN] [file ... ]\n");
exit(1);
}
次が、オプションの処理、
#include <string.h>

int width = WIDTH;

while (--argc > 0 && **++argv == '-') {
for (s = *argv + 1; *s != '\0'; s++) {
switch(*s) {
case 'w':
if (alldigit(s + 1) == NO)
usage(); /* no return */
width = atoi(s + 1);
while (*(++s + 1))
;
break;
default:
usage();
}
}
}
if (width <= 0 || WIDTH < width)
usage();

最後の if文ですが、これは 入力した オプションの数値が 0 以下、または 既定値よりも 大きいと、usage() を 呼び出して プログラムを 終了させるための 処置です。
以前 つくった オプション解析コードとは、switch文の 内容が 少しだけ 違います、
switch(*s) {
case 'w':
if (alldigit(s + 1) == NO) {
usage();
width = atoi(s + 1);
while (*(++s + 1))
;
break;
default:
usage();
}
まず、alldigit() で s + 1 つまり -w オプションに 続く文字が 数字か どうか チェックし、数字以外なら usage() を 呼び出し、関数を 終了させます。
そして、文字が すべて 数字であれば、その文字を 標準ライブラリ関数 atoi() で int型へと 変換し、変数 width に 代入します。
もし、オプション -w の後ろの コマンドライン上に 文字列が あれば、while ループの null statement を使って スキップさせておき、最後に break で この switch文を 抜けます。
while(*(++s + 1))
;
この switch文では、例外処理に default ラベルを 使い、-w 以外の オプションでは、usage() を 呼び出して 関数を 終了させるように しています。
ここまでの コードを 組み立てていけば、このプログラムは 一応 完成しますが、もう少しだけ、より 一般的な プログラムに 直していきます。

*1:「作ってわかる Cプログラミング」