/*
 *  crc.c -- CRC (Cyclic Redundancy Check) を計算する
 *
 *  revision history:
 *	0.1: May 24, 1996 by Dai ISHIJIMA
 *
 *  参考文献:
 *      W.H.Press, et al.: Numerical Recipes in C
 *	(Cambridge University Press, 1992) 2nd ed., pp.896-901
 */

#include <stdio.h>

#define YES 1
#define NO 0

/* 生成多項式          6 5432 1098 7654 3210 */
#ifdef CCITT
#define GX 0x11021L /* 1 0001 0000 0010 0001: G(x) = x^16 + x^12 + x^5 + 1 */
#else
#define GX 0x18005L /* 1 1000 0000 0000 0101: G(x) = x^16 + x^15 + x^2 + 1 */
#endif

#define G ((unsigned short)(((unsigned long)GX) & 0xffff))


/* 表引きのための表 */
#define TABSIZ 256
static unsigned short crctbl[TABSIZ];


/* CRCを *計算* する */
unsigned short rawcrc(unsigned short crc, unsigned char ch)
{
    int i;

    crc = crc ^ ch << 8;
    for (i = 0; i < 8; i++) {
	if (crc & 0x8000) {
	    crc = (crc <<= 1) ^ G;
	}
	else {
	    crc <<= 1;
	}
    }
    return(crc);
}


/* 表引きのための初期化 */
void tblini()
{
    int i;

    for (i = 0; i < TABSIZ; i++) {
	crctbl[i] = rawcrc(i << 8, 0);
    }
}


#define lo(x) ((unsigned char)((x) & 0xff))
#define hi(x) ((unsigned char)((x) >> 8))

static unsigned char revtab[16] = {
    0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
    0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf,
};

/* ビットのならびを反転させる。例: 11001010 -> 01010011 */
#define revord(x) ((revtab[(x) & 0x0f] << 4) | revtab[((x) & 0xf0) >> 4])
#define revord16(x) ((revord(lo(x)) << 8) | revord(hi(x)))


/* CRCを順次計算する */
unsigned short crc16c(unsigned short crc, unsigned char ch, int rev)
{
    if (rev) {
	crc = revord16(crc);
	crc = crctbl[revord(ch) ^ hi(crc)] ^ lo(crc) << 8;
	crc = revord16(crc);
    }
    else {
	crc = crctbl[ch ^ hi(crc)] ^ lo(crc) << 8;
    }
    return(crc);
}


#ifdef BLOCKCALC
/* 文字列のCRCを返す */
unsigned short crc16(unsigned short crc, int len, unsigned char *s, int rev)
{
    int i;

    if (rev) {
	crc = revord16(crc);
    }
    for (i = 0; i < len; i++) {
	if (rev) {
	    crc = crctbl[revord(s[i]) ^ hi(crc)] ^ lo(crc) << 8;
	}
	else {
	    crc = crctbl[s[i] ^ hi(crc)] ^ lo(crc) << 8;
	}
    }
    if (rev) {
	return(revord16(crc));
    }
    else {
	return(crc);
    }
}
#endif /* BLOCKCALC */


#ifdef MAIN
/*
 *  MAIN が定義されているときは、標準入力の CRCを計算するプログラムが
 *  作られる。(上の関数のチェック用のプログラム)
 *
 *  使用法: crc [-i 初期値] [-r]
 *	-i 初期値    CRCの初期値を設定する
 *	-r           ビットならびを反転して CRCを計算する
 */
#define shift --argc; ++argv

main(int argc, char *argv[])
{
    int ch;
    int rev;
    unsigned short crc;
    char *prog;

    prog = *argv;
    shift;
    crc = 0;
    rev = NO;
    while ((argc > 0) && (argv[0][0] == '-')) {
	if (argv[0][1] == 'i') {
	    shift;
	    crc = atoi(*argv);
	}
	else if (argv[0][1] == 'r') {
	    rev = !rev;
	}
	else {
	    fprintf(stderr, "Usage: %s [-i initial_value] [-r]\n", prog);
	    exit(1);
	}
	shift;
    }
    if (argc > 0) {
	fprintf(stderr, "Usage: %s [-i initial_value] [-r]\n", prog);
	exit(1);
    }
    tblini();
    while ((ch = getchar()) != EOF) {
	crc = crc16c(crc, ch, rev);
    }
    printf("%04x\n", crc);
    exit(0);
}
#endif

/* Local Variables: */
/* compile-command:"cc -DMAIN -o crc crc.c" */
/* End: */
