/*
 *  extract-ng.c  -  extrae archivos (codigos fuente y demas de los articulos)
 *
 *  Programa original:  phrack staff (r)
 *  Modificacion CRC32: cafo
 *  Modificacion MD5:   cafo
 *  Modificacion SHA1:  kenshin
 *  Otras mejoras:      kenshin
 *  
 *  Licencia: >=GPL2
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>

//#include <assert.h
#include <errno.h>

#define BEGIN_TAG   "<++> "
#define END_TAG     "<-->"
#define BT_SIZE     strlen(BEGIN_TAG)
#define ET_SIZE     strlen(END_TAG)

#define NONE "\033[00m"
#define GREY "\033[1;30m"
#define GREEN "\033[1;32m"
#define BLUE "\033[1;34m"
#define WHITE "\033[1;36m"
#define RED "\033[1;31m"

#define OK ""BLUE"["GREEN" ok "BLUE"]"NONE""
#define KO ""BLUE"["RED" !! "BLUE"]"NONE""

struct f_name
{
    unsigned char name[256];
    struct f_name *next;
};

#ifdef __alpha
typedef unsigned int uint32;
#else
typedef unsigned long uint32;
#endif

struct MD5Context
{
	uint32 buf[4];
	uint32 bits[2];
	unsigned char in[64];
};

#undef BIG_ENDIAN_HOST
typedef unsigned int u32;

#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )

typedef struct {
	u32  h0,h1,h2,h3,h4;
	u32  nblocks;
	unsigned char buf[64];
	int  count;
} SHA1_CONTEXT;


void sha1_init( SHA1_CONTEXT *hd )
{
	hd->h0 = 0x67452301;
	hd->h1 = 0xefcdab89;
	hd->h2 = 0x98badcfe;
	hd->h3 = 0x10325476;
	hd->h4 = 0xc3d2e1f0;
	hd->nblocks = 0;
	hd->count = 0;
}

static void
transform( SHA1_CONTEXT *hd, unsigned char *data )
{
	u32 a,b,c,d,e,tm;
	u32 x[16];

	a = hd->h0;
	b = hd->h1;
	c = hd->h2;
	d = hd->h3;
	e = hd->h4;

#ifdef BIG_ENDIAN_HOST
	memcpy( x, data, 64 );
#else
	int i;
	unsigned char *p2;

	for (i = 0, p2 = (unsigned char*)x; i < 16; i++, p2 += 4 ) {
		p2[3] = *data++;
		p2[2] = *data++;
		p2[1] = *data++;
		p2[0] = *data++;
	}
#endif

#define K1 0x5A827999L
#define K2 0x6ED9EBA1L
#define K3 0x8F1BBCDCL
#define K4 0xCA62C1D6L
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) (x ^ y ^ z)
#define F3(x, y, z) ((x & y) | (z & (x | y)))
#define F4(x, y, z) (x ^ y ^ z)

#define M(i) (tm = x[i&0x0f] ^ x[(i-14)&0x0f] ^ x[(i-8)&0x0f] \
		^ x[(i-3)&0x0f], (x[i&0x0f] = rol(tm,1)) )

#define R(a, b, c, d, e, f, k, m)				\
	do { 							\
		e += rol(a, 5) + f(b, c, d) + k + m;		\
		b = rol(b, 30);					\
	} while(0)
	
	R(a, b, c, d, e, F1, K1, x[ 0]);    R(e, a, b, c, d, F1, K1, x[ 1]);
	R(d, e, a, b, c, F1, K1, x[ 2]);    R(c, d, e, a, b, F1, K1, x[ 3]);
	R(b, c, d, e, a, F1, K1, x[ 4]);    R(a, b, c, d, e, F1, K1, x[ 5]);
	R(e, a, b, c, d, F1, K1, x[ 6]);    R(d, e, a, b, c, F1, K1, x[ 7]);
	R(c, d, e, a, b, F1, K1, x[ 8]);    R(b, c, d, e, a, F1, K1, x[ 9]);
	R(a, b, c, d, e, F1, K1, x[10]);    R(e, a, b, c, d, F1, K1, x[11]);
	R(d, e, a, b, c, F1, K1, x[12]);    R(c, d, e, a, b, F1, K1, x[13]);
	R(b, c, d, e, a, F1, K1, x[14]);    R(a, b, c, d, e, F1, K1, x[15]);
	R(e, a, b, c, d, F1, K1, M(16));    R(d, e, a, b, c, F1, K1, M(17));
	R(c, d, e, a, b, F1, K1, M(18));    R(b, c, d, e, a, F1, K1, M(19));
	
	R(a, b, c, d, e, F2, K2, M(20));    R(e, a, b, c, d, F2, K2, M(21));
	R(d, e, a, b, c, F2, K2, M(22));    R(c, d, e, a, b, F2, K2, M(23));
	R(b, c, d, e, a, F2, K2, M(24));    R(a, b, c, d, e, F2, K2, M(25));
	R(e, a, b, c, d, F2, K2, M(26));    R(d, e, a, b, c, F2, K2, M(27));
	R(c, d, e, a, b, F2, K2, M(28));    R(b, c, d, e, a, F2, K2, M(29));
	R(a, b, c, d, e, F2, K2, M(30));    R(e, a, b, c, d, F2, K2, M(31));
	R(d, e, a, b, c, F2, K2, M(32));    R(c, d, e, a, b, F2, K2, M(33));
	R(b, c, d, e, a, F2, K2, M(34));    R(a, b, c, d, e, F2, K2, M(35));
	R(e, a, b, c, d, F2, K2, M(36));    R(d, e, a, b, c, F2, K2, M(37));
	R(c, d, e, a, b, F2, K2, M(38));    R(b, c, d, e, a, F2, K2, M(39));
	
	R(a, b, c, d, e, F3, K3, M(40));    R(e, a, b, c, d, F3, K3, M(41));
	R(d, e, a, b, c, F3, K3, M(42));    R(c, d, e, a, b, F3, K3, M(43));
	R(b, c, d, e, a, F3, K3, M(44));    R(a, b, c, d, e, F3, K3, M(45));
	R(e, a, b, c, d, F3, K3, M(46));    R(d, e, a, b, c, F3, K3, M(47));
	R(c, d, e, a, b, F3, K3, M(48));    R(b, c, d, e, a, F3, K3, M(49));
	R(a, b, c, d, e, F3, K3, M(50));    R(e, a, b, c, d, F3, K3, M(51));
	R(d, e, a, b, c, F3, K3, M(52));    R(c, d, e, a, b, F3, K3, M(53));
	R(b, c, d, e, a, F3, K3, M(54));    R(a, b, c, d, e, F3, K3, M(55));
	R(e, a, b, c, d, F3, K3, M(56));    R(d, e, a, b, c, F3, K3, M(57));
	R(c, d, e, a, b, F3, K3, M(58));    R(b, c, d, e, a, F3, K3, M(59));
	
	R(a, b, c, d, e, F4, K4, M(60));    R(e, a, b, c, d, F4, K4, M(61));
	R(d, e, a, b, c, F4, K4, M(62));    R(c, d, e, a, b, F4, K4, M(63));
	R(b, c, d, e, a, F4, K4, M(64));    R(a, b, c, d, e, F4, K4, M(65));
	R(e, a, b, c, d, F4, K4, M(66));    R(d, e, a, b, c, F4, K4, M(67));
	R(c, d, e, a, b, F4, K4, M(68));    R(b, c, d, e, a, F4, K4, M(69));
	R(a, b, c, d, e, F4, K4, M(70));    R(e, a, b, c, d, F4, K4, M(71));
	R(d, e, a, b, c, F4, K4, M(72));    R(c, d, e, a, b, F4, K4, M(73));
	R(b, c, d, e, a, F4, K4, M(74));    R(a, b, c, d, e, F4, K4, M(75));
	R(e, a, b, c, d, F4, K4, M(76));    R(d, e, a, b, c, F4, K4, M(77));
	R(c, d, e, a, b, F4, K4, M(78));    R(b, c, d, e, a, F4, K4, M(79));

	/* Update chaining vars */
	hd->h0 += a;
	hd->h1 += b;
	hd->h2 += c;
	hd->h3 += d;
	hd->h4 += e;

#undef F1
#undef F2
#undef F3
#undef F4
}

static void
sha1_write( SHA1_CONTEXT *hd, unsigned char *inbuf, size_t inlen)
{
	if (hd->count == 64) {
		transform( hd, hd->buf );
		hd->count = 0;
		hd->nblocks++;
	}
	if (!inbuf)
		return;
	if (hd->count) {
		for (; inlen && hd->count < 64; inlen--)
			hd->buf[hd->count++] = *inbuf++;
		sha1_write (hd, NULL, 0);
		if (!inlen)
			return;
	}

	while (inlen >= 64) {
		transform(hd, inbuf);
		hd->count = 0;
		hd->nblocks++;
		inlen -= 64;
		inbuf += 64;
	}
	for (; inlen && hd->count < 64; inlen--)
		hd->buf[hd->count++] = *inbuf++;
}

/* The routine final terminates the computation and
 * returns the digest.
 * The handle is prepared for a new cycle, but adding bytes to the
 * handle will the destroy the returned buffer.
 * Returns: 20 bytes representing the digest.
 */

static void
sha1_final(SHA1_CONTEXT *hd)
{
	u32 t, msb, lsb;
	unsigned char *p;

	sha1_write(hd, NULL, 0);
	t = hd->nblocks;
	/* multiply by 64 to make a byte count */
	lsb = t << 6;
	msb = t >> 26;
	/* add the count */
	t = lsb;
	if( (lsb += hd->count) < t )
		msb++;
	/* multiply by 8 to make a bit count */
	t = lsb;
	lsb <<= 3;
	msb <<= 3;
	msb |= t >> 29;

	if(hd->count < 56) { /* enough room */
		hd->buf[hd->count++] = 0x80;
		while(hd->count < 56)
			hd->buf[hd->count++] = 0;
	}
	else { /* need one extra block */
		hd->buf[hd->count++] = 0x80;
		while(hd->count < 64)
			hd->buf[hd->count++] = 0;
		sha1_write(hd, NULL, 0);
		memset(hd->buf, 0, 56 );
	}
	
	hd->buf[56] = msb >> 24;
	hd->buf[57] = msb >> 16;
	hd->buf[58] = msb >>  8;
	hd->buf[59] = msb;
	hd->buf[60] = lsb >> 24;
	hd->buf[61] = lsb >> 16;
	hd->buf[62] = lsb >>  8;
	hd->buf[63] = lsb;
	transform( hd, hd->buf );

	p = hd->buf;
#ifdef BIG_ENDIAN_HOST
#define X(a) do { *(u32*)p = hd->h##a ; p += 4; } while(0)
#else /* little endian */
#define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16;	 \
		      *p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0)
#endif
	X(0);
	X(1);
	X(2);
	X(3);
	X(4);
#undef X
}

void sha1file(FILE *fp, unsigned char *uno)
{
	//assert (sizeof (u32) == 4);
	fseek(fp,0,SEEK_SET);

	unsigned char buffer[4096];
	size_t n;
	
	SHA1_CONTEXT ctx;
	sha1_init(&ctx);
	
	while (n = fread(buffer, 1, sizeof buffer, fp))
		sha1_write(&ctx, buffer, n);
	
	sha1_final(&ctx);

	for (n = 0; n < 20; n++)
		sprintf(uno+n*2, "%02x", ctx.buf[n]);
}

typedef struct MD5Context MD5_CTX;

#ifndef HIGHFIRST
#define byteReverse(buf, len)
#else
void byteReverse(unsigned char *buf, unsigned longs);

#ifndef ASM_MD5
void byteReverse(unsigned char *buf, unsigned longs)
{
	uint32 t;
	do {
		t = (uint32)((unsigned) buf[3] << 8 | buf[2]) << 16 |
				((unsigned) buf[1] << 8 | buf[0]);
		*(uint32 *)buf = t;
		buf += 4;
	} while (--longs);
}
#endif
#endif

#ifndef ASM_MD5

#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))

#define MD5STEP(f, w, x, y, z, data, s) (w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x)

void MD5Transform(uint32 buffer[4], uint32 const in[16])
{
	register uint32 a, b, c, d;

	a = buffer[0];
	b = buffer[1];
	c = buffer[2];
	d = buffer[3];

	MD5STEP(F1, a, b, c, d, in[0]  + 0xd76aa478, 7);
	MD5STEP(F1, d, a, b, c, in[1]  + 0xe8c7b756, 12);
	MD5STEP(F1, c, d, a, b, in[2]  + 0x242070db, 17);
	MD5STEP(F1, b, c, d, a, in[3]  + 0xc1bdceee, 22);
	MD5STEP(F1, a, b, c, d, in[4]  + 0xf57c0faf, 7);
	MD5STEP(F1, d, a, b, c, in[5]  + 0x4787c62a, 12);
	MD5STEP(F1, c, d, a, b, in[6]  + 0xa8304613, 17);
	MD5STEP(F1, b, c, d, a, in[7]  + 0xfd469501, 22);
	MD5STEP(F1, a, b, c, d, in[8]  + 0x698098d8, 7);
	MD5STEP(F1, d, a, b, c, in[9]  + 0x8b44f7af, 12);
	MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
	MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
	MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
	MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
	MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
	MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);

	MD5STEP(F2, a, b, c, d, in[1]  + 0xf61e2562, 5);
	MD5STEP(F2, d, a, b, c, in[6]  + 0xc040b340, 9);
	MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
	MD5STEP(F2, b, c, d, a, in[0]  + 0xe9b6c7aa, 20);
	MD5STEP(F2, a, b, c, d, in[5]  + 0xd62f105d, 5);
	MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
	MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
	MD5STEP(F2, b, c, d, a, in[4]  + 0xe7d3fbc8, 20);
	MD5STEP(F2, a, b, c, d, in[9]  + 0x21e1cde6, 5);
	MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
	MD5STEP(F2, c, d, a, b, in[3]  + 0xf4d50d87, 14);
	MD5STEP(F2, b, c, d, a, in[8]  + 0x455a14ed, 20);
	MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
	MD5STEP(F2, d, a, b, c, in[2]  + 0xfcefa3f8, 9);
	MD5STEP(F2, c, d, a, b, in[7]  + 0x676f02d9, 14);
	MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);

	MD5STEP(F3, a, b, c, d, in[5]  + 0xfffa3942, 4);
	MD5STEP(F3, d, a, b, c, in[8]  + 0x8771f681, 11);
	MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
	MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
	MD5STEP(F3, a, b, c, d, in[1]  + 0xa4beea44, 4);
	MD5STEP(F3, d, a, b, c, in[4]  + 0x4bdecfa9, 11);
	MD5STEP(F3, c, d, a, b, in[7]  + 0xf6bb4b60, 16);
	MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
	MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
	MD5STEP(F3, d, a, b, c, in[0]  + 0xeaa127fa, 11);
	MD5STEP(F3, c, d, a, b, in[3]  + 0xd4ef3085, 16);
	MD5STEP(F3, b, c, d, a, in[6]  + 0x04881d05, 23);
	MD5STEP(F3, a, b, c, d, in[9]  + 0xd9d4d039, 4);
	MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
	MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
	MD5STEP(F3, b, c, d, a, in[2]  + 0xc4ac5665, 23);

	MD5STEP(F4, a, b, c, d, in[0]  + 0xf4292244, 6);
	MD5STEP(F4, d, a, b, c, in[7]  + 0x432aff97, 10);
	MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
	MD5STEP(F4, b, c, d, a, in[5]  + 0xfc93a039, 21);
	MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
	MD5STEP(F4, d, a, b, c, in[3]  + 0x8f0ccc92, 10);
	MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
	MD5STEP(F4, b, c, d, a, in[1]  + 0x85845dd1, 21);
	MD5STEP(F4, a, b, c, d, in[8]  + 0x6fa87e4f, 6);
	MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
	MD5STEP(F4, c, d, a, b, in[6]  + 0xa3014314, 15);
	MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
	MD5STEP(F4, a, b, c, d, in[4]  + 0xf7537e82, 6);
	MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
	MD5STEP(F4, c, d, a, b, in[2]  + 0x2ad7d2bb, 15);
	MD5STEP(F4, b, c, d, a, in[9]  + 0xeb86d391, 21);

	buffer[0] += a;
	buffer[1] += b;
	buffer[2] += c;
	buffer[3] += d;
}
#endif

void MD5Init(struct MD5Context *ctx)
{
	ctx->buf[0] = 0x67452301;
	ctx->buf[1] = 0xefcdab89;
	ctx->buf[2] = 0x98badcfe;
	ctx->buf[3] = 0x10325476;

	ctx->bits[0] = 0;
	ctx->bits[1] = 0;
}

void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
{
	uint32 t;

	t = ctx->bits[0];
	
	if ((ctx->bits[0] = t+((uint32)len << 3)) < t)
		ctx->bits[1]++;
	
	ctx->bits[1] += len >> 29;
	t = (t >> 3) & 0x3f;

	if (t) {
		unsigned char *p = (unsigned char *) ctx->in + t;
		t = 64 - t;
		
		if (len < t) {
			memcpy(p, buf, len);
			return;
		}
		
		memcpy(p, buf, t);
		byteReverse(ctx->in, 16);
		MD5Transform(ctx->buf, (uint32 *) ctx->in);
		buf += t;
		len -= t;
	}

	while (len >= 64) {
		memcpy(ctx->in, buf, 64);
		byteReverse(ctx->in, 16);
		MD5Transform(ctx->buf, (uint32 *)ctx->in);
		buf += 64;
		len -= 64;
	}

	memcpy(ctx->in, buf, len);
}

void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
{
	unsigned count;
	unsigned char *p;

	count = (ctx->bits[0] >> 3) & 0x3F;
	p = ctx->in + count;
	*p++ = 0x80;
	count = 64 - 1 - count;

	if (count < 8) {
		memset(p, 0, count);
		byteReverse(ctx->in, 16);
		MD5Transform(ctx->buf, (uint32 *) ctx->in);
		memset(ctx->in, 0, 56);
	}
	else
		memset(p, 0, count - 8);
	
	byteReverse(ctx->in, 14);
	((uint32 *) ctx->in)[14] = ctx->bits[0];
	((uint32 *) ctx->in)[15] = ctx->bits[1];
	MD5Transform(ctx->buf, (uint32 *) ctx->in);
	byteReverse((unsigned char *) ctx->buf, 4);
	memcpy(digest, ctx->buf, 16);
	memset(ctx, 0, sizeof(ctx));
}

int hex_digit(int c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
	
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	
	return -1;
}

int mdfile(FILE *archivo, unsigned char *digest)
{
	unsigned char buffer[1024];
	MD5_CTX ctx;
	int n;

	fseek(archivo,0,SEEK_SET);
	MD5Init(&ctx);
	
	while ((n = fread(buffer, 1, sizeof(buffer), archivo)) > 0)
		MD5Update(&ctx, buffer, n);
	
	MD5Final(digest, &ctx);
}

unsigned long crcTable[256];

void crcgen()
{
	unsigned long crc, poly;
	int i, j;
	
	poly = 0xEDB88320L;
	
	for (i = 0; i < 256; i++) {
		crc = i;
		
		for (j = 8; j > 0; j--) {
			if (crc&1)
				crc = (crc >> 1) ^ poly;
			else
				crc >>= 1;
		}
		crcTable[i] = crc;
	}
}

unsigned long check_crc( FILE *fp )
{
	int c;
	register unsigned long crc = 0xFFFFFFFF;
	
	while((c = getc(fp)) != EOF)
		crc = ((crc>>8) & 0x00FFFFFF) ^ crcTable[(crc^c) & 0xFF];

	fseek(fp, 0, SEEK_SET);

	return (crc ^ 0xFFFFFFFF);
}

void tmp_digest(unsigned char *p, unsigned char *digest_tmp)
{
	int i;

	for (i = 0; i < 16; ++i)
		sprintf(digest_tmp+i*2, "%02x", *p++);
}

void show(FILE *out_p, char h, unsigned long crc, unsigned long crc_f, 
		unsigned char *digest, unsigned char *digest_f, 
		unsigned char *digest_tmp, unsigned char *digestsha1, 
		unsigned char *digestsha1_f, unsigned char *name)
{
	if (out_p) {
		if (h == '!') {
			fseek(out_p, 0l,0);
			crc=check_crc(out_p);
			
			if (crc == crc_f)
				printf("    crc32 = %08lx                                 %s\n", crc, OK);
			else {
				printf("    crc32 = "RED"%08lx                                 %s\n", crc_f, KO);
				printf("    crc32 > %08lx\n",crc);
			}
		}
		else if (h == '$') {
			mdfile(out_p, digest);
			tmp_digest(digest, digest_tmp);
			
			if(strcmp(digest_f,digest_tmp) == 0)
				printf("    md5 = %32s           %s\n", digest_f, OK);
			else {
				printf("    md5 = "RED"%32s           %s\n", digest_f, KO);
				printf("    md5 > %32s\n", digest_tmp);
			}   
		}
		else if (h == '%') {
			sha1file(out_p, digestsha1);
			
			if(strcmp(digestsha1, digestsha1_f) == 0)
				printf("    sha1 = %40s  %s\n", digestsha1, OK);
			else {
				printf("    sha1 = "RED"%40s  %s\n", digestsha1_f, KO);
				printf("    sha1 > %40s\n", digestsha1);
			}
		}
		fclose(out_p);
	}
	else {
		fprintf(stderr, "Can't close %s: %s\n", name, strerror(errno));
		//continue;
	}
}

int main(int argc, char **argv)
{
	char h = '\0';
	int i;
	unsigned long crc = 0, 
		      crc_f = 0;
	unsigned char b[256], 
		      *bp, 
		      *fn;
	unsigned char digest_f[32],
		      digest[16], 
		      digest_tmp[32], 
		      digestsha1[20], 
		      digestsha1_f[40];
	
	FILE *in_p, *out_p = NULL;
	struct f_name *fn_p = NULL, 
		      *head = NULL;

	if (argc < 2) {
		fprintf(stderr, "Usage: %s file\n", argv[0]);
		return 1;
	}

	for (i = 1; fn = argv[i++]; ) {
		if (!head) {
			head = (struct f_name *)malloc(sizeof(struct f_name));
			if (!head) {
				perror("malloc");
				return 1;
			}
			strncpy(head->name, fn, sizeof(head->name));
			head->next = NULL;
			fn_p = head;
		}
		else {
			fn_p->next = (struct f_name *)malloc(sizeof(struct f_name));
			if (!fn_p->next) {
				perror("malloc");
				return 1;
			}
			fn_p = fn_p->next;
			strncpy(fn_p->name, fn, sizeof(fn_p->name));
			fn_p->next = NULL;
		}
	}
	
	fn_p->next = (struct f_name *)malloc(sizeof(struct f_name));
	if (!fn_p->next) {
		perror("malloc");
		return 1;
	}

	fn_p = fn_p->next;
	fn_p->next = NULL;
	i = 0;

	for (fn_p = head; fn_p->next; fn_p = fn_p->next) {
		if (!(in_p = fopen(fn_p->name, "r"))) {
			fprintf(stderr, "I could not open %s: %s\n", fn_p->name, strerror(errno));
			continue;
		}

		crcgen();

		while (fgets(b, 256, in_p)) {
			if (!strncmp(b, BEGIN_TAG, BT_SIZE)) {
				b[strlen(b) - 1] = 0;
				i++;
				
				if (bp = strchr(b + BT_SIZE + 1, '/')) {
					while (bp) {
						*bp = 0;
						mkdir(b + BT_SIZE, 0700);
						*bp = '/';
						bp = strchr(bp + 1, '/');
					}
				}
				if (bp = strchr(b,'!')) {
					crc_f = strtoul((b + (strlen(b)-strlen(bp)) + 1),NULL,16);
					b[strlen(b) - strlen(bp) - 1 ] = 0;
					h = '!';
				}
				else if (bp = strchr(b,'$')) {
					memcpy(&digest_f,bp+1,32);
					digest_f[32] = '\0';
					b[strlen(b) - strlen(bp) - 1] = 0;
					h = '$';
				}
				else if (bp = strchr(b,'%')) {
					memcpy(&digestsha1_f,bp+1,40);
					digestsha1_f[40] = '\0';
					b[strlen(b) - strlen(bp) - 1] = 0;
					h = '%';
				}
				else
					h = '\0';

				if (out_p = fopen(b + BT_SIZE, "wb+"))
					printf(GREEN" * "NONE"Extracting "WHITE"%s\n"NONE, b+BT_SIZE);
				else {
					printf("I could not extract '%s'\n", b+BT_SIZE);
					continue;
				}
			}
			else if (!strncmp (b, END_TAG, ET_SIZE)) {
				show(out_p, h, crc, crc_f, digest, 
						digest_f, digest_tmp, 
						digestsha1, digestsha1_f, 
						 fn_p->name);
			}
			else if (out_p)
				fputs(b, out_p);
		}
	}
	printf("Extracted %d files\n", i);
	return 0;
}

