/*
 * MultiGIF  V1.0  28-Jun-96  Andy Wardley
 *
 * (C) 1996 Andy Wardley <abw@peritas.com>.  All rights reserved.
 *
 * gifinfo.c
 *
 * Stand-alone utility to read GIF file and print breakdown of GIF 
 * structure.
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gif.h"
#include "gifio.h"


#define PROGNAME   "gifinfo"

/* define fseek whence options if not set */
#ifndef SEEK_SET
#define SEEK_SET	0
#define SEEK_CUR	1
#define SEEK_END	2
#endif  /* SEEK_SET */


/* 
 * prototypes for functions in this module
 */

void gifinfo(char *filename);
static char *binary(char c);


/*
 * main
 */

int main(int argc, char **argv)
{
	int cmd;

	/* check usage */
	if (argc < 2)
	{
		(void) printf("usage: %s gifs\n", PROGNAME);
		exit(1);
	}

	/* process each file */
	for(cmd = 1; cmd < argc; cmd++)
		gifinfo(argv[cmd]);

	return 0;
}



/*
 * void gifinfo(char *filename)
 *
 * Prints information about a gif file.
 */

void gifinfo(char *filename)
{
	FILE *fp;
	gif_header          gh;
	gif_colour          gct[256];
	gif_img_desc        gid;
	gif_graphic_control ggc;
	gif_plain_text      gpt;
	gif_app_ext         gae;
	gif_comment         gc;
	int coltabsiz;
	int image = 0;
	int blocks, disposal;
	BYTE blocksize, label;
	BYTE identifier;
	BYTE garbage[256];
	BYTE *ptr;


	/* open file */
	if ((fp = fopen(filename, "rb")) == NULL)
	{
		(void) fprintf(stderr, "ERROR: cannot open %s\n", filename);
		return;
	}

	(void) printf("+- GIF HEADER ---------\n");
	(void) printf("|             filename: %s\n", filename);

	/* read gif header */
	if (read_gif_header(&gh, fp) != 0)
	{
		(void) fprintf(stderr, "ERROR: cannot read GIF header\n");
		(void) fclose(fp);
		return;
	}

	/* print GIF header info */
	(void) printf("|            signature: %.*s\n", 
			sizeof(gh.signature), gh.signature);
	(void) printf("|              version: %.*s\n", 
			sizeof(gh.version), gh.version);
	(void) printf("|         screen width: %3d\n", 
			gh.screen_width);
	(void) printf("|        screen height: %3d\n", 
			gh.screen_height);
	(void) printf("|               packed:  %02Xh (%s)\n", 
			gh.packed, binary(gh.packed));
		
	/* check for colour table */
	if (gh.packed & GIF_COLTAB)
	{
		coltabsiz = 1L << ((gh.packed & GIF_COLTABSIZ) + 1);
		(void) printf("|  global colour table: %3d %s entries of %d bits\n",
			coltabsiz,
			gh.packed & GIF_GCOLTABSORT ? "sorted" : "unsorted", 
			(gh.packed & GIF_COLTABRES >> 4) + 1);

		if (read_gif_colour_table(gct, coltabsiz, fp) != 0)
		{
			(void) printf("Error reading global colour table\n");
			(void) fclose(fp);
			return;
		}
	}	
	else
		(void) printf("|  global colour table: none\n");

	(void) printf("+----------------------\n"); 

	/* read a block at a time */
	while ((identifier = read_byte(fp)) != GIF_TRAILER)  /* trailer */
	{
		switch (identifier)
		{
		case GIF_IMAGE_DESC:
			gid.seperator = identifier;
			if (read_gif_img_desc(&gid, fp) != 0)
			{
				(void) printf("Error reading image descriptor\n");
				(void) fclose(fp);
				return;
			}

			(void) printf("+- IMAGE # %03d --------\n", ++image);
			(void) printf("|                 left: %3d\n", gid.left);
			(void) printf("|                  top: %3d\n", gid.top);
			(void) printf("|                width: %3d\n", gid.width);
			(void) printf("|               height: %3d\n", gid.height);
			(void) printf("|               packed:  %02Xh (%s)\n", 
						gid.packed, binary(gid.packed));
			(void) printf("|           interlaced: %3s\n", 
						gid.packed & GIF_INTERLACE ? "yes" : "no");

			if (gid.packed & GIF_COLTAB)
			{
				coltabsiz = 1L << ((gh.packed & GIF_COLTABSIZ) + 1);
				(void) printf("|   local colour table: %3d %s entries\n",
					coltabsiz,
					gh.packed & GIF_LCOLTABSORT ? "sorted" : "unsorted");

				if (read_gif_colour_table(gct, coltabsiz, fp) != 0)
				{
					(void) printf("Error reading local colour table\n");
					(void) fclose(fp);
					return;
				}	
			}
			else
				(void) printf("|   local colour table: none\n");

			(void) printf("|    LZW min code size: %3d\n", read_byte(fp));

			blocks = 0;
			while((blocksize = read_byte(fp)) != 0)
			{
				blocks++;

				/* read (and ignore) block data */
				if (fread(garbage, 1, blocksize, fp) != blocksize)
				{
					(void) printf("Error reading image block\n");
					(void) fclose(fp);
					return;
				}
			}

			(void) printf("|        # data blocks: %3d\n", blocks);
			(void) printf("+----------------------\n"); 

			break;


		case GIF_EXTENSION:
			label = read_byte(fp);

			switch(label)
			{
			case GIF_TEXT_EXT:	/* plain text */
				gpt.introducer = identifier;
				gpt.label = label;

				if (read_gif_plain_text(&gpt, fp) != 0)
				{
					(void) printf("Error reading plain text block\n");
					(void) fclose(fp);
					return;
				}

				(void) printf("+- PLAIN TEXT ---------\n");
				(void) printf("|            grid left: %3d\n", 
						gpt.text_grid_left);
				(void) printf("|             grid top: %3d\n", 
						gpt.text_grid_top);
				(void) printf("|           grid width: %3d\n", 
						gpt.text_grid_width);
				(void) printf("|          grid height: %3d\n", 
						gpt.text_grid_height);
				(void) printf("|           cell width: %3d\n", 
						gpt.cell_width);
				(void) printf("|          cell height: %3d\n", 
						gpt.cell_height);
				(void) printf("|      fg colour index: %3d\n", 
						gpt.text_fg_colour);
				(void) printf("|      bg colour index: %3d\n", 
						gpt.text_bg_colour);

				blocksize = *(gpt.plain_text_data);
				(void) printf("|                 text: %.*s\n", blocksize, 
							gpt.plain_text_data + 1);
				(void) printf("+----------------------\n");

				break;
		
			case GIF_COMMENT_EXT:
				gc.introducer = identifier;
				gc.label = label;

				if (read_gif_comment(&gc, fp) != 0)
				{
					(void) printf("Error reading comment\n");
					(void) fclose(fp);
					return;
				}

				blocksize = *(gc.comment_data);
				(void) printf("+- COMMENT ------------\n");
				(void) printf("|                 size: %3d\n", blocksize);
				(void) printf("|                 text: %.*s\n", blocksize, 
							gc.comment_data + 1);
				(void) printf("+----------------------\n"); 

				break;

			case GIF_GRAPHIC_EXT:
				ggc.introducer = identifier;
				ggc.label = label;

				if (read_gif_graphic_control(&ggc, fp) != 0)
				{
					(void) printf("Error reading graphic control\n");
					(void) fclose(fp);
					return;
				}

				(void) printf("+- GRAPHIC EXT --------\n");
				(void) printf("|           delay time: %3d (%d.%ds)\n", 
						ggc.delay, ggc.delay / 100, ggc.delay % 100);
				(void) printf("|   transparent colour: %3d\n", 
						ggc.transparent);
				(void) printf("|         transparency:	%3s\n",
						ggc.packed & GIF_TRANSPARENT ? "on" : "off");
				(void) printf("|           user input: %3s\n", 
						ggc.packed & GIF_USERINPUT ? "on" : "off");

				disposal = (int) (ggc.packed & GIF_DISPOSAL) >> 2;
				(void) printf("|      disposal method: %3d (%s)\n",
						disposal, 
						disposal == 0 ? "unspecified" :
						disposal == 1 ? "do nothing" :
						disposal == 2 ? "background" :
						disposal == 3 ? "restore" :
						"reserved");
				(void) printf("|               packed:  %02Xh (%s)\n",
						ggc.packed, binary(ggc.packed));
				(void) printf("+----------------------\n"); 

				break;

			case GIF_APP_EXT:
				gae.introducer = identifier;
				gae.label = label;

				if (read_gif_app_ext(&gae, fp) != 0)
				{
					(void) printf("Error reading application extension\n");
					(void) fclose(fp);
					return;
				}

			
				blocksize = *(gae.app_data);
				(void) printf("+- APPLICATION EXT ----\n");
				(void) printf("|           identifier: %.*s\n",
							sizeof(gae.identifier), gae.identifier);
				(void) printf("|             authcode: %.*s\n", 
							sizeof(gae.authcode), gae.authcode);
				(void) printf("|            data size: %3d\n", blocksize);
				(void) printf("|                 data: ");

				ptr = gae.app_data + 1;
				while (blocksize--)
					(void) printf("%d%c", *ptr++, blocksize ? ',' : '\n');

				(void) printf("+----------------------\n");

				break;

			default:
				(void) printf("Unknown extension: %d\n", label);
				break;
			}


			break;

		default:
			(void) printf("Unknown block identifier: %d\n", identifier);
			break;
		}
	}	

	(void) printf("\n");

	(void) fclose(fp);

}


static char *binary(char c)
{
	static char buffer[9];
	char *ptr = buffer;
	int factor = 0x80;

	while (factor)
	{
		if (c & factor)
			*ptr++ = '1';
		else 
			*ptr++ = '0';

		factor /= 2;
	}
	*ptr = '\0';

	return buffer;
}
