/*
 *	comptest - 簡単な圧縮プログラム
 *
 *	zlibライブラリを使った純粋な圧縮解凍ツールです。
 *	gzipやpkzip形式のような独自ヘッダを付加しません。
 *
 * * Fri Jun 14 12:30:00 JST 2002 Naoyuki Sawa
 * - 出典はこちら：
 *	http://www.matsusaka-u.ac.jp/~okumura/compression/zlib.html
 *   著者の奥村晴彦様に感謝いたします。
 * - 文字コードをEUC(LF)→SJIS(CR+LF)に変更し、
 *   このコメントを追加したこと以外は、
 *   オリジナルソースから一切変更していません。(追記：変更しました。以下参照）
 * * Fri Jun 14 12:30:00 JST 2002 Naoyuki Sawa
 * - fopen()がテキストモードだったので、バイナリモードに変更しました。
 *   オリジナルはUNIX用なので、テキスト/バイナリ関係ありませんが、
 *   Windowsでは明示的にバイナリモードでfopen()しておかないと、
 *   データが途中で切れて、壊れたファイルが生成されたりします。
 * * Sat Jun 15 08:37:00 JST 2002 Naoyuki Sawa
 * - zlibダイナミックライブラリを使うと、バージョン違いで初期化に失敗し、
 *   アプリケーションエラーで終了してしまうことがあるようなので、
 *   zlibスタティックライブラリを直接リンクするようにしました。
 *   20KB程度大きくなりました。DLL版:45KB→LIB版:75KB
 * - 現在配布されているzlibは1.1.4で、僕も1.1.4を使って開発しています。
 *   しかし、P/ECE開発環境に含まれているzlibは1.1.3です。
 *   1.1.4を使ってコンパイルしたアプリケーションを、1.1.3 DLLで実行すると、
 *   前述の通り、初期化に失敗するようです。
 */

/* zlib を使う例 */
/* コンパイル: gcc -Wall comptest.c -o comptest -lz */
/* -lz は libz.a をリンクする（たぶん /usr/local/lib にある）*/

#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>               /* /usr/local/include/zlib.h */

#define INBUFSIZ   1024         /* 入力バッファサイズ（任意） */
#define OUTBUFSIZ  1024         /* 出力バッファサイズ（任意） */

z_stream z;                     /* ライブラリとやりとりするための構造体 */

char inbuf[INBUFSIZ];           /* 入力バッファ */
char outbuf[OUTBUFSIZ];         /* 出力バッファ */
FILE *fin, *fout;               /* 入力・出力ファイル */

void do_compress(void)          /* 圧縮 */
{
    int count, flush, status;

    /* すべてのメモリ管理をライブラリに任せる */
    z.zalloc = Z_NULL;
    z.zfree = Z_NULL;
    z.opaque = Z_NULL;

    /* 初期化 */
    /* 第2引数は圧縮の度合。0〜9 の範囲の整数で，0 は無圧縮 */
    /* Z_DEFAULT_COMPRESSION (= 6) が標準 */
    if (deflateInit(&z, Z_DEFAULT_COMPRESSION) != Z_OK) {
        fprintf(stderr, "deflateInit: %s\n", (z.msg) ? z.msg : "???");
        exit(1);
    }

    z.avail_in = 0;             /* 入力バッファ中のデータのバイト数 */
    z.next_out = outbuf;        /* 出力ポインタ */
    z.avail_out = OUTBUFSIZ;    /* 出力バッファのサイズ */

    /* 通常は deflate() の第2引数は Z_NO_FLUSH にして呼び出す */
    flush = Z_NO_FLUSH;

    while (1) {
        if (z.avail_in == 0) {  /* 入力が尽きれば */
            z.next_in = inbuf;  /* 入力ポインタを入力バッファの先頭に */
            z.avail_in = fread(inbuf, 1, INBUFSIZ, fin); /* データを読み込む */

            /* 入力が最後になったら deflate() の第2引数は Z_FINISH にする */
            if (z.avail_in < INBUFSIZ) flush = Z_FINISH;
        }
        status = deflate(&z, flush); /* 圧縮する */
        if (status == Z_STREAM_END) break; /* 完了 */
        if (status != Z_OK) {   /* エラー */
            fprintf(stderr, "deflate: %s\n", (z.msg) ? z.msg : "???");
            exit(1);
        }
        if (z.avail_out == 0) { /* 出力バッファが尽きれば */
            /* まとめて書き出す */
            if (fwrite(outbuf, 1, OUTBUFSIZ, fout) != OUTBUFSIZ) {
                fprintf(stderr, "Write error\n");
                exit(1);
            }
            z.next_out = outbuf; /* 出力バッファ残量を元に戻す */
            z.avail_out = OUTBUFSIZ; /* 出力ポインタを元に戻す */
        }
    }

    /* 残りを吐き出す */
    if ((count = OUTBUFSIZ - z.avail_out) != 0) {
        if (fwrite(outbuf, 1, count, fout) != count) {
            fprintf(stderr, "Write error\n");
            exit(1);
        }
    }

    /* 後始末 */
    if (deflateEnd(&z) != Z_OK) {
        fprintf(stderr, "deflateEnd: %s\n", (z.msg) ? z.msg : "???");
        exit(1);
    }
}

void do_decompress(void)        /* 展開（復元） */
{
    int count, status;

    /* すべてのメモリ管理をライブラリに任せる */
    z.zalloc = Z_NULL;
    z.zfree = Z_NULL;
    z.opaque = Z_NULL;

    /* 初期化 */
    z.next_in = Z_NULL;
    z.avail_in = 0;
    if (inflateInit(&z) != Z_OK) {
        fprintf(stderr, "inflateInit: %s\n", (z.msg) ? z.msg : "???");
        exit(1);
    }

    z.next_out = outbuf;        /* 出力ポインタ */
    z.avail_out = OUTBUFSIZ;    /* 出力バッファ残量 */
    status = Z_OK;

    while (status != Z_STREAM_END) {
        if (z.avail_in == 0) {  /* 入力残量がゼロになれば */
            z.next_in = inbuf;  /* 入力ポインタを元に戻す */
            z.avail_in = fread(inbuf, 1, INBUFSIZ, fin); /* データを読む */
        }
        status = inflate(&z, Z_NO_FLUSH); /* 展開 */
        if (status == Z_STREAM_END) break; /* 完了 */
        if (status != Z_OK) {   /* エラー */
            fprintf(stderr, "inflate: %s\n", (z.msg) ? z.msg : "???");
            exit(1);
        }
        if (z.avail_out == 0) { /* 出力バッファが尽きれば */
            /* まとめて書き出す */
            if (fwrite(outbuf, 1, OUTBUFSIZ, fout) != OUTBUFSIZ) {
                fprintf(stderr, "Write error\n");
                exit(1);
            }
            z.next_out = outbuf; /* 出力ポインタを元に戻す */
            z.avail_out = OUTBUFSIZ; /* 出力バッファ残量を元に戻す */
        }
    }

    /* 残りを吐き出す */
    if ((count = OUTBUFSIZ - z.avail_out) != 0) {
        if (fwrite(outbuf, 1, count, fout) != count) {
            fprintf(stderr, "Write error\n");
            exit(1);
        }
    }

    /* 後始末 */
    if (inflateEnd(&z) != Z_OK) {
        fprintf(stderr, "inflateEnd: %s\n", (z.msg) ? z.msg : "???");
        exit(1);
    }
}

int main(int argc, char *argv[])
{
    int c;

    if (argc != 4) {
        fprintf(stderr, "Usage: comptest flag infile outfile\n");
        fprintf(stderr, "  flag: c=compress d=decompress\n");
        exit(0);
    }
    if (argv[1][0] == 'c') {
        c = 1;
    } else if (argv[1][0] == 'd') {
        c = 0;
    } else {
        fprintf(stderr, "Unknown flag: %s\n", argv[1]);
        exit(1);
    }
    /*if ((fin = fopen(argv[2], "r")) == NULL) {  {{N.SAWA:削除}}*/
    if ((fin = fopen(argv[2], "rb")) == NULL) { /*{{N.SAWA:追加}}*/
        fprintf(stderr, "Can't open %s\n", argv[2]);
        exit(1);
    }
    /*if ((fout = fopen(argv[3], "w")) == NULL) {   {{N.SAWA:削除}}*/
    if ((fout = fopen(argv[3], "wb")) == NULL) { /* {{N.SAWA:追加}}*/
        fprintf(stderr, "Can't open %s\n", argv[3]);
        exit(1);
    }
    if (c) do_compress(); else do_decompress();
    fclose(fin);
    fclose(fout);
    return 0;
}
