//------------------------------------------------------------------------------
// 標準入力の各行に指定書式の連番を付けて標準出力する
//------------------------------------------------------------------------------
//
// コマンド仕様:
// SeqNo "<書式>" <初期値> <増分>
//
// <書式>:
// 通常文字部: そのまま出力する
// 変換規定部: % で始まる以下の構成部を変換して出力する
// ・フラグ(省略可)
// - 左詰
// + 正の10進数値の場合、+ 符号を付ける
// 空白 正の10進数値の場合、+ 符号の代わりに空白を付ける
// 0 数値の場合、フィールド幅の左余白を 0 で埋める
// ・フィールド幅(省略可)
// 最少限の出力桁数
// ・ピリオッド(省略可)
// フィールド幅と精度の区切り
// ・精度(省略可)
// 数値の場合、最少限の桁数
// 文字列の場合、最大字数(半角単位)
// ・変換規定文字(変換規定部の最後の文字、省略不可)
// 連番に対して
// d 10進数字列(半角)
// N 10進数字列(全角)
// x 16進数字列( 10以上の桁は a〜f )(半角)
// X 16進数字列( 10以上の桁は A〜Z )(半角)
// a 英小文字1字(半角) a,b,c,...,z
// A 英大文字1字(半角) A,B,C,...,Z
// z 英小文字1字(全角) a,b,c,...z
// Z 英大文字1字(全角) A,B,C,...Z
// M 丸付数字1字(全角) @,A,...,S
// I イロハの1字(全角) イ,ロ,ハ,...,ス
// i イ ロ ハ の1字(半角) イ,ロ,ハ,...,ス
// H ひらかな1字(全角) あ,い,う,...,ん
// K カタカナ1字(全角) ア,イ,ウ,...,ン
// k カ タ カ ナ 1字(半角) ア,イ,ウ,...,ン
// 標準入力に対して
// p 標準入力から次に読み込んだ1行(改行コードを除く)
// %p では、フラグ、フィールド幅、精度の指定は無効
// エスケープ文字: \ と次の1字を以下の制御コードに変換して出力する
// \t TAB
// \r CR
// \n LF
// \f FF
// \v VT
// \e ESC
// これ以外の場合、\ の次の1字をそのまま出力
// 《注意》
// <書式> 内に、%p がない場合、最後にあると解釈される
// 改行コードは、"<書式>" の最後に出力される
// <書式> 省略時は、"%d" と解釈される
// <初期値>:
// 変換 省略/不正 有効指定
// ------- ----------- -----------------------------------
// %d 1 10進数字列(半角)
// %N 1 10進数字列(半角)
// %x %X 0 16進数字列(半角)
// その他 最初の文字 その文字範囲内の1字、または、順番
// <増分>:
// 正または負の10進数字列(半角)
// 省略時は、1 になる
//
// 例:
// FSeqNo "(%d) %p" ・・・ 各行の先頭に (1) (2) (3) ... を付加
// FSeqNo "%N. %p" ・・・ 各行の先頭に 1. 2. 3. ... を付加
// FSeqNo "%p%04X" ・・・ 各行の末尾に、4桁16進数の連番を付加
//------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef enum { FALSE = 0, TRUE = 1 } BOOL;
typedef enum { EOL_NONE, EOL_CRLF, EOL_LF, EOL_CR } EOLTYP;
UCHAR* pArgFormat = (UCHAR*)"%d"; // コマンド引数の <書式>
UCHAR* pArgSeqNo = NULL; // コマンド引数の <初期値>
int SeqInc = 1; // コマンド引数の <増分>
int SeqNo; // 現在の連番値
BOOL IsSeqInit = FALSE; // 連番は初期設定済みか?
BOOL IsStdinEOF = FALSE; // 標準入力は終了したか?
EOLTYP EolType = EOL_CRLF; // 標準入力の改行コード種別
#define MAX_TXSPEC 100 // 書式内の変換規定部( %... )の最大長
/// 関数先行宣言 ///
void PrintText();
void SetInitSeqNo( int c );
void PrintSeqNo( UCHAR* top_tp, UCHAR* end_tp );
void PrintLine();
void PrintEOL();
int Cut( int min, int v, int max );
int Chop( int min, int v, int max );
int Cycle( int min, int v, int max );
//-----------------------------------------------------------------------------
int main( int argc, char *argv[] )
{
switch( argc )
{
default:
case 4: SeqInc = atoi( argv[3] );
case 3: pArgSeqNo = (UCHAR*) argv[2];
case 2: pArgFormat = (UCHAR*) argv[1];
case 1: break;
}
PrintText();
return 1;
}
//-----------------------------------------------------------------------------
// 指定の書式で連番を付けた各行を標準出力にプリントする
void PrintText()
{
int c;
UCHAR* cp;
BOOL IsLineOut;
UCHAR TxSpec[ MAX_TXSPEC + 4 ]; // 変換規定部( %... )の文字列バッファ
UCHAR* tp;
UCHAR* lim_tp;
TxSpec[0] = '%';
cp = pArgFormat;
IsLineOut = FALSE;
for( ;; )
{
LOOP:
switch( c = *cp++ )
{
case '%': break;
case 0: // <書式> の終端に至った時
TXSPEC_END:
if( ! IsLineOut ) // <書式> 内の %p の指定がなかった?
PrintLine();
PrintEOL();
if( IsStdinEOF )
return;
else if(( c = getchar() ) == EOF )
return;
else
ungetc( c, stdin );
cp = pArgFormat;
IsLineOut = FALSE;
continue;
case '\\': // エスケープ文字
switch( *cp++ )
{
case 't': c = '\t'; break; // TAB
case 'r': c = '\r'; break; // CR
case 'n': c = '\n'; break; // LF
case 'f': c = 0x0C; break; // FF
case 'v': c = 0x0B; break; // VT
case 'e': c = 0x1B; break; // ESC
default: c = cp[-1]; break; // 未定義
}
// 制御は下に行く
default: putchar( c );
continue;
}
// % 以降の変換規定部を走査
tp = &TxSpec[1];
lim_tp = &TxSpec[ MAX_TXSPEC ];
while( tp < lim_tp )
{
switch( *tp++ = *cp++ )
{
case 'd': // (半角)10進数字列
case 'x': // (半角)16進数字列( 10以上の桁は a〜f )
case 'X': // (半角)16進数字列( 10以上の桁は A〜Z )
case 'a': // (半角)英小文字1字
case 'A': // (半角)英大文字1字
case 'z': // (全角)英小文字1字
case 'Z': // (全角)英大文字1字
case 'N': // (全角)10進数字列
case 'M': // (全角)丸付数字(1字)
case 'I': // (全角)イロハ (1字)
case 'i': // (半角)イ ロ ハ (1字)
case 'H': // (全角)ひらかな(1字)
case 'K': // (全角)カタカナ(1字)
case 'k': // (半角)カ タ カ ナ (1字)
*tp = 0;
if( ! IsSeqInit )
SetInitSeqNo( tp[-1] );
PrintSeqNo( TxSpec, tp - 1 );
goto LOOP;
break;
case 'p': // 標準入力の1行を標準出力
PrintLine();
IsLineOut = TRUE;
goto LOOP;
case '-': case '+': case ' ': case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
continue;
case 0: // 変換規定文字がない場合
fputs( (char*) TxSpec, stdout ); // そのまま標準出力
goto TXSPEC_END;
default: // 変換規定文字が所定外の場合
*--tp = 0;
fputs( (char*) TxSpec, stdout ); // そのまま標準出力
--cp;
goto LOOP;
}
//(注)ここに来ることはない
}
// 変換規定文字列部が長すぎる場合
c = cp[-1];
if(( 0x81 <= c && c <= 0x9F ) || ( 0xE0 <= c && c <= 0xFC ))
--cp, --tp; // 全角第1バイトの場合
*tp = 0;
fputs( (char*) TxSpec, stdout ); // そのまま標準出力
//goto LOOP;
}
}
//-----------------------------------------------------------------------------
/// 各種の単字連番表 ///
const USHORT HankLower[] =
{
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
};
const USHORT HankUpper[] =
{
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
};
const USHORT ZenkLower[] =
{
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
};
const USHORT ZenkUpper[] =
{
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
};
#define N_ALPHAS 26
const USHORT MaruNum[] =
{
'@','A','B','C','D','E','F','G','H','I',
'J','K','L','M','N','O','P','Q','R','S',
};
#define N_MARUS ( sizeof( MaruNum ) / sizeof( USHORT ))
const USHORT ZenkIroha[] =
{
'イ','ロ','ハ','ニ','ホ','ヘ','ト','チ','リ','ヌ','ル','ヲ',
'ワ','カ','ヨ','タ','レ','ソ','ツ','ネ','ナ','ラ','ム',
'ウ','ヰ','ノ','オ','ク','ヤ','マ','ケ','フ','コ','エ','テ',
'ア','サ','キ','ユ','メ','ミ','シ','ヱ','ヒ','モ','セ','ス',
};
const USHORT HankIroha[] =
{
'イ','ロ','ハ','ニ','ホ','ヘ','ト','チ','リ','ヌ','ル','ヲ',
'ワ','カ','ヨ','タ','レ','ソ','ツ','ネ','ナ','ラ','ム',
'ウ','ィ','ノ','オ','ク','ヤ','マ','ケ','フ','コ','エ','テ',
'ア','サ','キ','ユ','メ','ミ','シ','ェ','ヒ','モ','セ','ス',
};
// 《注意》「ヰ」,「ヱ」 に相当する文字がないため 「ィ」,「ェ」 で代用
#define N_IROHAS 47
const USHORT ZenkHira[] =
{
'あ', 'い', 'う', 'え', 'お',
'か', 'き', 'く', 'け', 'こ',
'さ', 'し', 'す', 'せ', 'そ',
'た', 'ち', 'つ', 'て', 'と',
'な', 'に', 'ぬ', 'ね', 'の',
'は', 'ひ', 'ふ', 'へ', 'ほ',
'ま', 'み', 'む', 'め', 'も',
'や', 'ゆ', 'よ',
'ら', 'り', 'る', 'れ', 'ろ',
'わ', 'を', 'ん',
};
const USHORT ZenkKata[] =
{
'ア', 'イ', 'ウ', 'エ', 'オ',
'カ', 'キ', 'ク', 'ケ', 'コ',
'サ', 'シ', 'ス', 'セ', 'ソ',
'タ', 'チ', 'ツ', 'テ', 'ト',
'ナ', 'ニ', 'ヌ', 'ネ', 'ノ',
'ハ', 'ヒ', 'フ', 'ヘ', 'ホ',
'マ', 'ミ', 'ム', 'メ', 'モ',
'ヤ', 'ユ', 'ヨ',
'ラ', 'リ', 'ル', 'レ', 'ロ',
'ワ', 'ヲ', 'ン',
};
const USHORT HankKata[] =
{
'ア', 'イ', 'ウ', 'エ', 'オ',
'カ', 'キ', 'ク', 'ケ', 'コ',
'サ', 'シ', 'ス', 'セ', 'ソ',
'タ', 'チ', 'ツ', 'テ', 'ト',
'ナ', 'ニ', 'ヌ', 'ネ', 'ノ',
'ハ', 'ヒ', 'フ', 'ヘ', 'ホ',
'マ', 'ミ', 'ム', 'メ', 'モ',
'ヤ', 'ユ', 'ヨ',
'ラ', 'リ', 'ル', 'レ', 'ロ',
'ワ', 'ヲ', 'ン',
};
#define N_KANAS 46
//-----------------------------------------------------------------------------
// 指定の変換規定文字に基づいて、連番のタイプと初期値を決定する
void SetInitSeqNo( int c )
{
UCHAR* cp;
const USHORT* kp;
int i;
int N;
int k;
IsSeqInit = TRUE;
switch( c )
{
default:
case 'd': // 10進数字列(半角)
case 'N': // 10進数字列(全角)
if( pArgSeqNo != NULL )
SeqNo = atoi( (char*) pArgSeqNo );
else
SeqNo = 1;
return;
case 'x': // 16進数字列( 10以上の桁は a〜f )(半角)
case 'X': // 16進数字列( 10以上の桁は A〜Z )(半角)
SeqNo = 0;
if( pArgSeqNo != NULL )
{
for( cp = pArgSeqNo ;; cp++ )
{
switch( *cp )
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
SeqNo = ( SeqNo << 4 ) + ( *cp - '0' );
continue;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
SeqNo = ( SeqNo << 4 ) + ( *cp - 'A' + 10 );
continue;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
SeqNo = ( SeqNo << 4 ) + ( *cp - 'a' + 10 );
continue;
default: break;
}
break;
}
}
return;
case 'a': // 英小文字半角1字
kp = HankLower;
N = N_ALPHAS;
break;
case 'A': // 英大文字半角1字
kp = HankUpper;
N = N_ALPHAS;
break;
case 'z': // 英小文字全角1字
kp = ZenkLower;
N = N_ALPHAS;
break;
case 'Z': // 英大文字全角1字
kp = ZenkUpper;
N = N_ALPHAS;
break;
case 'I': // イロハ(全角1字)
kp = ZenkIroha;
N = N_IROHAS;
break;
case 'i': // イ ロ ハ (半角1字)
kp = HankIroha;
N = N_IROHAS;
break;
case 'H': // (全角)ひらかな(1字)
kp = ZenkHira;
N = N_KANAS;
break;
case 'K': // (全角)カタカナ(1字)
kp = ZenkKata;
N = N_KANAS;
break;
case 'k': // (半角)カ タ カ ナ (1字)
kp = HankKata;
N = N_KANAS;
break;
case 'M': // 丸付数字(全角1字)
kp = MaruNum;
N = N_MARUS;
break;
}
SeqNo = 0;
if( pArgSeqNo != NULL ) // 連番の <初期値> の指定あり?
{
if( *pArgSeqNo >= 0x80 ) // <初期値> は、実文字で指定?
{
if( *kp < 0x100 ) // 対象は半角?
k = *pArgSeqNo;
else // 対象は全角
k = ( pArgSeqNo[0] << 8 ) + pArgSeqNo[1];
for( i = 0 ; i < N ; i++ )
{
if( kp[i] == k )
{
SeqNo = i;
break;
}
}
}
else // <初期値> が番号(数字)で指定されている場合
SeqNo = Chop( 1, atoi( (char*) pArgSeqNo ), N ) - 1;
}
return;
}
//-----------------------------------------------------------------------------
// 指定の変換規定に従って、現連番を標準出力にプリントし、連番を次値に更新
// top_tp = 書式変換規定部の最初の文字( % )のアドレス
// end_tp = 書式変換規定部の最後の文字( 変換規定文字 )のアドレス
void PrintSeqNo( UCHAR* top_tp, UCHAR* end_tp )
{
int v;
UCHAR s[4];
v = SeqNo;
SeqNo += SeqInc;
switch( *end_tp )
{
case 'd': // 10進数字列(半角)
case 'x': // 16進数字列( 10以上の桁は小文字 )
case 'X': // 16進数字列( 10以上の桁は大文字 )
printf( (char*) top_tp, v );
break;
case 'a': // 英小文字半角1字
SeqNo = Cycle( 0, SeqNo, N_ALPHAS - 1 );
v = HankLower[ v ];
PRINT_SEQ_HANK:
*end_tp = 'c';
printf( (char*) top_tp, v );
break;
case 'A': // 英大文字半角1字
SeqNo = Cycle( 0, SeqNo, N_ALPHAS - 1 );
v = HankUpper[ v ];
goto PRINT_SEQ_HANK;
case 'z': // 英小文字(全角1字)
SeqNo = Cycle( 0, SeqNo, N_ALPHAS - 1 );
v = ZenkLower[ v ];
PRINT_SEQ_ZENK:
s[0] = v >> 8;
s[1] = v & 0xFF;
s[2] = 0;
*end_tp = 's';
printf( (char*) top_tp, s );
break;
case 'Z': // 英大文字(全角1字)
SeqNo = Cycle( 0, SeqNo, N_ALPHAS - 1 );
v = ZenkUpper[ v ];
goto PRINT_SEQ_ZENK;
case 'M': // 丸付数字(全角1字)
SeqNo = Cycle( 0, SeqNo, N_MARUS - 1 );
v = MaruNum[ v ];
goto PRINT_SEQ_ZENK;
case 'I': // イロハ(全角1字)
SeqNo = Cycle( 0, SeqNo, N_IROHAS - 1 );
v = ZenkIroha[ v ];
goto PRINT_SEQ_ZENK;
case 'i': // イ ロ ハ (半角1字)
SeqNo = Cycle( 0, SeqNo, N_IROHAS - 1 );
v = HankIroha[ v ];
goto PRINT_SEQ_HANK;
case 'H': // (全角)ひらかな(1字)
SeqNo = Cycle( 0, SeqNo, N_KANAS - 1 );
v = ZenkHira[ v ];
goto PRINT_SEQ_ZENK;
case 'K': // (全角)カタカナ(1字)
SeqNo = Cycle( 0, SeqNo, N_KANAS - 1 );
v = ZenkKata[ v ];
goto PRINT_SEQ_ZENK;
case 'k': // (半角)カ タ カ ナ (1字)
SeqNo = Cycle( 0, SeqNo, N_KANAS - 1 );
v = HankKata[ v ];
goto PRINT_SEQ_HANK;
case 'N': // 10進数字列(全角)
{
#define MAX_SEQNO_LEN 100
UCHAR SeqNoText[ MAX_SEQNO_LEN + 4 ];
UCHAR* cp;
int c;
// 現連番値を10進数字列(半角)に変換
*end_tp = 'd';
if( _snprintf( (char*) SeqNoText, MAX_SEQNO_LEN,
(char*) top_tp, v ) < 0 )
SeqNoText[ MAX_SEQNO_LEN ] = 0; // 最大サイズを超えた場合
// 半角→全角に変換して出力
for( cp = SeqNoText ; ( c = *cp++ ) != 0 ; )
{
if( '0' <= c && c <= '9' )
c = '0' + ( c - '0' );
else if( c == '-' )
c = '−';
else if( c == ' ' )
c = ' ';
//(注)それ以外の文字は、そのまま
if( c >= 0x100 )
putchar( c >> 8 );
putchar( c & 0xFF );
}
}
break;
}
}
//-----------------------------------------------------------------------------
// 標準入力から読み出した1行分を標準出力する(ここでは改行コードは出力しない)
void PrintLine()
{
int c;
for( ;; )
{
switch( c = getchar() )
{
case '\r': switch( c = getchar() )
{
default: ungetc( c, stdin );
EolType = EOL_CR;
break;
case EOF: IsStdinEOF = TRUE;
EolType = EOL_CR;
break;
case '\n': EolType = EOL_CRLF;
break;
}
break;
case '\n': EolType = EOL_LF;
break;
case EOF: IsStdinEOF = TRUE;
EolType = EOL_NONE;
break;
default: putchar( c );
continue;
}
break;
}
}
//-----------------------------------------------------------------------------
// 標準入力から読み出したのと同じ改行コードを標準出力する
void PrintEOL()
{
switch( EolType )
{
case EOL_NONE: break;
case EOL_CRLF: putchar( '\r' );
case EOL_LF: putchar( '\n' ); break;
case EOL_CR: putchar( '\r' ); break;
}
}
//-----------------------------------------------------------------------------
int Cut( int min, int v, int max )
{
return ( v <= min ) ? min : (( max <= v ) ? max : v ) ;
}
int Cycle( int min, int v, int max )
{
int d = max - min + 1;
while( v < min )
v += d;
return ( v - min ) % d + min;
}
int Chop( int min, int v, int max )
{
return ( v < min || max < v ) ? min : v ;
}
//-----------------------------------------------------------------------------