トリッキーコードネット トップへ戻る   C/C++, Java, Perl, PHP, JavaScript, アルゴリズム, ショートコーディング, IOCCCコードの解説, 等々

サイト情報

トリッキーなコード

7行プログラミング

物凄いコード集

アルゴリズム

データ構造

C/C++な話題

コードサンプル

ツール/環境構築

開発ノウハウ 等

ネタ/ジョーク集

おススメ書籍/サイト

サイトTOP >> 物凄いコード集 >> バイナリエディタプログラミング (C言語)

バイナリエディタ (C言語 - IOCCC)

1986年(第三回)IOCCCグランプリ作品中に、なんとバイナリエディタ表示コードが投稿されていました!

以下にご紹介します~^^;)
#include <stdio.h>
#define O1O printf
#define OlO putchar
#define O10 exit
#define Ol0 strlen
#define QLQ fopen
#define OlQ fgetc
#define O1Q abs
#define QO0 for
typedef char lOL;

lOL*QI[] = {"Use:\012\011dump file\012","Unable to open file '\x25s'\012",
 "\012","   ",""};

main(I,Il)
lOL*Il[];
{   FILE *L;
    unsigned lO;
    int Q,OL[' '^'0'],llO = EOF,

    O=1,l=0,lll=O+O+O+l,OQ=056;
    lOL*llL="%2x ";
    (I != 1<<1&&(O1O(QI[0]),O10(1011-1010))),
    ((L = QLQ(Il[O],"r"))==0&&(O1O(QI[O],Il[O]),O10(O)));
    lO = I-(O<<l<<O);
    while (L-l,1)
    {   QO0(Q = 0L;((Q &~(0x10-O))== l);
            OL[Q++] = OlQ(L));
        if (OL[0]==llO) break;
        O1O("\0454x: ",lO);
        if (I == (1<<1))
        {   QO0(Q=Ol0(QI[O<<O<<1]);Q<Ol0(QI[0]);
            Q++)O1O((OL[Q]!=llO)?llL:QI[lll],OL[Q]);/*"
            O10(QI[1O])*/
            O1O(QI[lll]);{}
        }
        QO0 (Q=0L;Q<1<<1<<1<<1<<1;Q+=Q<0100)
        {   (OL[Q]!=llO)? /* 0010 10lOQ 000LQL */
            ((D(OL[Q])==0&&(*(OL+O1Q(Q-l))=OQ)),
            OlO(OL[Q])):
            OlO(1<<(1<<1<<1)<<1);
        }
        O1O(QI[01^10^9]);
        lO+=Q+0+l;}
    }
    D(l) { return l>=' '&&l<='\~';
}
使い方は、パラメータにファイルパスを渡すと、バイナリコードとアスキーコードを表示してくれます。 バイナリエディタ (C言語 - IOCCC) それでは早速解説を始めます^^;) まずは、いつもの如くプリプロセッサの実行結果を表示させ、ついでに typedef されている 「IOL」を「char」に戻します。
#include <stdio.h>

char*QI[] = {"Use:\012\011dump file\012","Unable to open file '\x25s'\012",
 "\012","   ",""};

main(I,Il)
char*Il[];
{   FILE *L;
    unsigned lO;
    int Q,OL[' '^'0'],llO = (-1),

    O=1,l=0,lll=O+O+O+l,OQ=056;
    char*llL="%2x ";
    (I != 1<<1&&(printf(QI[0]),exit(1011-1010))),
    ((L = fopen(Il[O],"r"))==0&&(printf(QI[O],Il[O]),exit(O)));
    lO = I-(O<<l<<O);
    while (L-l,1)
    {   for(Q = 0L;((Q &~(0x10-O))== l);
            OL[Q++] = fgetc(L));
        if (OL[0]==llO) break;
        printf("\0454x: ",lO);
        if (I == (1<<1))
        {   for(Q=strlen(QI[O<<O<<1]);Q<strlen(QI[0]);
            Q++)printf((OL[Q]!=llO)?llL:QI[lll],OL[Q]);

            printf(QI[lll]);{}
        }
        for (Q=0L;Q<1<<1<<1<<1<<1;Q+=Q<0100)
        {   (OL[Q]!=llO)?
            ((D(OL[Q])==0&&(*(OL+abs(Q-l))=OQ)),
            putchar(OL[Q])):
            putchar(1<<(1<<1<<1)<<1);
        }
        printf(QI[01^10^9]);
        lO+=Q+0+l;}
    }
    D(l) { return l>=' '&&l<='\~';
}
変数名がトリッキー過ぎる為、以下の様な 多少分かりやすい(?)変数名に置換します。
元変数名新変数名
QIstr
Ilargv
Iargc
llOk
llls
OLj
OQt
llLpsz
lOio
Qi
Oq
lr
Lfp
ついでに、インデントが意図的にずれている為、適当に修正します。
 1 : #include <stdio.h>
 2 :
 3 : char *str[] = { "Use:\012\011dump file\012", "Unable to open file '\x25s'\012", "\012", "   ", "" };
 4 :
 5 : main(argc,argv)
 6 : char *argv[];
 7 : {
 8 :     FILE     *fp;
 9 :     unsigned  io;
10 :     int       i, j[' ' ^ '0'], k = (-1), q = 1, r = 0, s = q+q+q+r, t = 056;
11 :     char     *psz = "%2x ";
12 :
13 :     (argc != 1<<1 && (printf(str[0]), exit(1011-1010))),
14 :     ( (fp = fopen(argv[q], "r")) == 0 && (printf(str[q], argv[q]), exit(q)) );
15 :
16 :     io = argc-(q<<r<<q);
17 :
18 :     while (fp-r,1)
19 :     {
20 :         for(i = 0L; ((i &~(0x10-q))== r); j[i++] = fgetc(fp));
21 :
22 :         if (j[0]==k)
23 :             break;
24 :
25 :         printf("\0454x: ",io);
26 :
27 :         if (argc == (1<<1))
28 :         {
29 :             for (i = strlen(str[q<<q<<1]); i<strlen(str[0]); i++)
30 :                 printf( (j[i]!=k) ? psz : str[s], j[i]);
31 :
32 :             printf(str[s]);
33 :         }
34 :
35 :         for (i=0L; i<1<<1<<1<<1<<1; i+=i<0100)
36 :         {
37 :             (j[i]!=k) ? ((D(j[i])==0 && (*(j+abs(i-r))=t)), putchar(j[i]))
38 :                       : putchar(1<<(1<<1<<1)<<1);
39 :         }
40 :
41 :         printf(str[01^10^9]);
42 :         io += i+0+r;
43 :     }
44 : }
45 :
46 : D(r)
47 : {
48 :     return r >= ' ' && r <= '\~';
49 : }
アスキーコード表と見比べるに、3行目の str[0]は "Use:\n\tdump file\n" と、 str[1]は "Unable to open file '%s'\n" と、 str[2]は "\n" と、 それぞれ同義です。 また、10行目の j[' ' ^ '0'] は j[16] と (∵ 32 ^ 48 = 16)、 s = q+q+q+r は s = 3 と、 t = 056 は t = 46 と (∵ 056は八進数)、 それぞれ同義です。 20行目の (i &~(0x10-q)) は (i &~ 15) と同義です。(∵ 0x10 = 16, q = 0) そして、iが0~15の場合、(i &~ 15) は ずっと0です。 しかし、iが16になった場合、初めて(i &~ 15) は 0以外となります。 また、r = 0の為、 20行目は 「ファイルポインタfpから16文字を読み込み、配列jに格納する」という意味になります。 25行目の printf関数の第一パラメータは、"%4x: " と同義です。 37行目の 46は、アスキーコード表と照らし合わせて、.(ドット)だという事が分かります。 41行目の 01^10^9 は 2と同義です。 上記の解説を反映させ、 かつ、随所にあるシフト演算、足し算・引き算、値が固定である 変数k, q, r, s, t を、分かりやすく整数で置換します。 すると、以下の様なコードになります。
 1 : #include <stdio.h>
 2 :
 3 : char *str[] = { "Use:\n\tdump file\n", "Unable to open file '%s'\n", "\n", "   ", "" };
 4 :
 5 : main(argc,argv)
 6 : char *argv[];
 7 : {
 8 :     FILE     *fp;
 9 :     unsigned  io;
10 :     int       i, j[16];
11 :     char     *psz = "%2x ";
12 :
13 :     (argc != 2 && (printf(str[0]), exit(1))),
14 :     ( (fp = fopen(argv[1], "r")) == 0 && (printf(str[1], argv[1]), exit(1)) );
15 :
16 :     io = argc - 2;
17 :
18 :     while (fp,1)
19 :     {
20 :         for(i = 0; i != 16; j[i++] = fgetc(fp));
21 :
22 :         if (j[0] == -1)
23 :             break;
24 :
25 :         printf("%4x: ",io);
26 :
27 :         if (argc == 2)
28 :         {
29 :             for (i = strlen(str[4]); i<strlen(str[0]); i++)
30 :                 printf( (j[i] != -1) ? psz : str[3], j[i]);
31 :
32 :             printf(str[3]);
33 :         }
34 :
35 :         for (i = 0; i < 16; i += i<64)
36 :         {
37 :             (j[i] != -1) ? ((D(j[i]) == 0 && (*(j + abs(i)) = '.')), putchar(j[i]))
38 :                          : putchar(32);
39 :         }
40 :
41 :         printf(str[2]);
42 :         io += i;
43 :     }
44 : }
45 :
46 : D(r)
47 : {
48 :     return r >= ' ' && r <= '\~';
49 : }
3行目の str[4]は、使わなくなったので削除します。 str[3]は、配列から削除し、プログラム中の使用箇所は 「" "」 で置換します。 str[2]は、配列から削除し、プログラム中の使用箇所は 「"\n"」 で置換します。 16行目の argc - 2 は、argcが2以外の場合は13行目でプログラムが終了してしまう為、0です。 29行目の strlen(str[4])は、strlen("")なので0、 strlen(str[0])は、16です。(← 偶然ではなく、ワザと余計なスペースやタブを入れて、str[0]の文字列長が16になるようにしてあります) 35行目の i += i<64 は、i++と同義です。(∵ i<64 は trueと評価され、演算中では1として扱われます) 38行目のputchar(32)は、(アスキーコード表と照らし合わせて) putchar(' ')と同義です。 これらを反映させたコードは、下記の通りです。
 1 : #include <stdio.h>
 2 :
 3 : char *str[] = { "Use:\n\tdump file\n", "Unable to open file '%s'\n" };
 4 :
 5 : main(argc,argv)
 6 : char *argv[];
 7 : {
 8 :     FILE     *fp;
 9 :     unsigned  io;
10 :     int       i, j[16];
11 :     char     *psz = "%2x ";
12 :
13 :     (argc != 2 && (printf(str[0]), exit(1))),
14 :     ( (fp = fopen(argv[1], "r")) == 0 && (printf(str[1], argv[1]), exit(1)) );
15 :
16 :     io = 0;
17 :
18 :     while (fp,1)
19 :     {
20 :         for(i = 0; i != 16; j[i++] = fgetc(fp));
21 :
22 :         if (j[0] == -1)
23 :             break;
24 :
25 :         printf("%4x: ",io);
26 :
27 :         if (argc == 2)
28 :         {
29 :             for (i = 0; i < 16; i++)
30 :                 printf( (j[i] != -1) ? psz : "   ", j[i]);
31 :
32 :             printf("   ");
33 :         }
34 :
35 :         for (i = 0; i < 16; i++)
36 :         {
37 :             (j[i] != -1) ? ((D(j[i]) == 0 && (*(j + i) = '.')), putchar(j[i]))
38 :                          : putchar(' ');
39 :         }
40 :
41 :         printf("\n");
42 :         io += i;
43 :     }
44 : }
45 :
46 : D(r)
47 : {
48 :     return r >= ' ' && r <= '\~';
49 : }
これで、どこにでもある普通のC言語ソースコードになりましたYO~~^^;) 一応プログラムの流れを説明します。 パラメータが指定されていないと、 13行目で「ダンプするファイルが必要だよ」という旨のメッセージを表示して、プログラム終了。 その後、ダンプするファイルの読み込みに失敗した場合、 「ファイルを開くのに失敗しました」という旨のメッセージを表示して、プログラム終了。 (↑この際、ココで紹介した技を使っています) 20行目でファイルの内容を1byteずつ読み込みます。 fgetc関数を使用している為、ファイルの読み込みに失敗した場合は -1が返されます。 (↑これ、このソースコードを読む際に、非常に重要になってきます) 30行目では、 ファイルの読み込みに成功している場合は、"%2x"でバイナリの値をダンプ、 ファイルの読み込みに失敗している場合は、スペースを表示します。 37,38行目では、 ファイルの読み込みに成功している場合、 該当1byteを関数Dに渡し、戻りが0の場合はドットを表示します。戻りが1の場合は、アスキー文字の範囲の為そのまま表示します。 ファイルの読み込みが失敗している場合、スペースを表示します。 46~48行目の関数Dは、入力文字が スペース ~ ~(チルダ)の範囲にあるかを判定して結果を返します。 これにより、入力文字がアスキー文字か否かを判定します。 ・・・といったわけで、ファイルを読み込み、バイナリコードとアスキー文字を表示するプログラムの流れが分かりました♪ いやぁ~~、IOCCCのコードリーディングって面白いですね~~^^
         このエントリーをはてなブックマークに追加   


作業効率化・ライフハックのオススメ記事




コンピュータ・テクノロジーのオススメ記事





恋愛・人間関係のオススメ記事




※ 当サイトは、トップページからリンクで辿る事の出来るページに限り、リンクフリーです。
※ 当サイトの閲覧/利用によって生じた如何なる損害も、当サイト管理人は責任を負いません。
※ 当サイトの内容を転載される場合は、当サイトへのリンクをお願い致します。