/*******************************************************************************
**  fly:  On-the-fly GIF creation utility
**  Martin Gleeson, ITS, gleeson@unimelb.edu.au
**  Copyright (c), The University of Melbourne, 1994,1995,1996
**  Last Update: 23 Jan 1996
**
**  Uses the gd library by Thomas Boutell, boutell@netcom.com
**  gd: Copyright 1994, Quest Protein Database Centre, Cold Spring Harbour Labs
**
**  Contributions from:
**  John Bowe <bowe@osf.org>
**     addition of better argument parsing
**  Claus Hofmann <claush@ipfr.bau-verm.uni-karlsruhe.de>
**     addition of 'transparent' directive.
**     addtion of code to check if colour already allocated
**     addition of feature to copy whole image if all coords are -1
**
*******************************************************************************/

char *version = "1.31";
char *usage = "Usage : fly [-h] [-q] [-i inputfile] [-o outputfile.gif]";

char *help = "Quick Reference to Directives: \n\nline  x1,y1,x2,y2,R,G,B             dline        x1,y1,x2,y2,R,G,B \nrect  x1,y1,x2,y2,R,G,B             frect        x1,y1,x2,y2,R,G,B \npoly  R,G,B,x1,y1...,xn,yn          fpoly        R,G,B,x1,y1...,xn,yn \nfill  x,y,R,G,B                     filltoborder x,y,R1,G1,B1,R2,B2,G2 \narc   x1,y1,w,h,start,finish,R,G,B  circle       x,y,d,R,G,B \n\nstring   R,G,B,x,y,<size>,<string> \nstringup R,G,B,x,y,<size>,<string> \n(size = tiny, small, medium, large or giant) \n\ncopy         x,y,x1,y1,x2,y2,filename.gif \ncopyresized  x1,y1,x2,y2,dx1,dy1,dx2,dy2,filename.gif \n\nsetpixel    x,y,R,G,B \ntransparent R,G,B \ninterlace \n\nsetbrush    filename.gif                         killbrush \nsettile     filename.gif                         killtile \nsetstyle    R1,G1,B1,R2,G2,B2,...,Rn,Bn,Gn       killstyle \n\nsizex \nsizey \n";

/******************************************************************************/

#include "gd.h"
#include "gdfonts.h"
#include "gdfontl.h"
#include "gdfontmb.h"
#include "gdfontt.h"
#include "gdfontg.h"
#include "fly.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

/******************************************************************************
**  Internal Functions
******************************************************************************/

int         process_args(int argc, char *argv[]);
int         get_token(FILE *infile);
int         get_number(FILE *infile);
char       *get_string(FILE *infile);
void        sync_input(FILE *infile);
int         get_colour(FILE *infile, gdImagePtr img);
void        copy_to_gif(FILE *infile, gdImagePtr img, int resize);
gdImagePtr  get_image(int type, int argc, char *argv[]);
void       *my_newmem(size_t size);

/******************************************************************************
**  Global Variables 
******************************************************************************/

int   finished = FALSE,
          done = FALSE,
         quiet = FALSE,
   end_of_line = FALSE;

char     *input_file,
         *output_file;

FILE	 *outfile;
FILE	 *infile;
FILE	 *brushfile;
FILE	 *tilefile;
FILE     *verbose_out;

/******************************************************************************
** Main Program
******************************************************************************/

int main(int argc, char *argv[]){
	int colour, colour2, type;
	int status, size;
	int brush_on = 0, tile_on = 0, style_on = 0;
	int num_entries, up = 0;
	int i, n, c, x, y;
	int arg[4096], style[1024];
	char *s;
	gdPoint points[2048];
	gdImagePtr img, brush, tile;

	status = process_args(argc, argv);
	if (status == FALSE) exit(0);

	type = get_token(infile);
	while( type == COMMENT )
	{
		sync_input(infile);
		type = get_token(infile);
	}
	if( type != NEW	 && type != EXISTING)
	{
		fprintf(stderr,"Error: Must use 'new' or 'existing' directive first in input.\n");
		exit(1);
	}
	img=get_image(type, argc, argv);

	/* while more lines to process */
	do{
		type = get_token(infile);
		switch(type){

		case LINE:      /*      gdImageLine()           */
			for(i=1;i<=4;i++)
			{
				arg[i]=get_number(infile);
			}
			if (!quiet) fprintf(verbose_out,"## Line ## drawn from %d,%d to %d,%d. (",
			    arg[1],arg[2],arg[3],arg[4]);
			if( brush_on )
			{
				sync_input(infile);
				gdImageLine(img,arg[1],arg[2],arg[3],arg[4],gdBrushed);
				if (!quiet) fprintf(verbose_out,"colour = current brush");
			}
			else if ( style_on )
			{
				sync_input(infile);
				gdImageLine(img,arg[1],arg[2],arg[3],arg[4],gdStyled);
				if (!quiet) fprintf(verbose_out,"colour = current style");
			}
			else
			{
				colour=get_colour(infile,img);
				gdImageLine(img,arg[1],arg[2],arg[3],arg[4],colour);
			}
			if (!quiet) fprintf(verbose_out,")\n");
			break;


		case DLINE:     /*      gdImageDashedLine()     */
			for(i=1;i<=4;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet)
				fprintf(verbose_out,"## Dashed Line ## drawn from %d,%d to %d,%d. (",
			    arg[1],arg[2],arg[3],arg[4]);
			if( brush_on ){
				sync_input(infile);
				gdImageDashedLine(img,arg[1],arg[2],arg[3],arg[4],gdBrushed);
				if (!quiet) fprintf(verbose_out,"colour = current brush");
			} else if ( style_on ) {
				sync_input(infile);
				gdImageDashedLine(img,arg[1],arg[2],arg[3],arg[4],gdStyled);
				if (!quiet) fprintf(verbose_out,"colour = current style");
			} else{
				colour=get_colour(infile,img);
				gdImageDashedLine(img,arg[1],arg[2],arg[3],arg[4],colour);
			}
			if (!quiet) fprintf(verbose_out,")\n");
			break;

		case RECT:      /*      gdImageRectangle()      */
			for(i=1;i<=4;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet) fprintf(verbose_out,"## Rectangle ## drawn from %d,%d to %d,%d. (",
			    arg[1],arg[2],arg[3],arg[4]);
			if( brush_on ){
				sync_input(infile);
				gdImageRectangle(img,arg[1],arg[2],arg[3],arg[4],gdBrushed);
				if (!quiet) fprintf(verbose_out,"colour = current brush");
			} else if ( style_on ) {
				sync_input(infile);
				gdImageRectangle(img,arg[1],arg[2],arg[3],arg[4],gdStyled);
				if (!quiet) fprintf(verbose_out,"colour = current style");
			} else{
				colour=get_colour(infile,img);
				gdImageRectangle(img,arg[1],arg[2],arg[3],arg[4],colour);
			}
			if (!quiet) fprintf(verbose_out,")\n");
			break;


		case FRECT:     /*      gdImageFilledRectangle() */
			for(i=1;i<=4;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet)
				fprintf(verbose_out,"## Filled Rectangle ## drawn from %d,%d to %d,%d. (",
			    arg[1],arg[2],arg[3],arg[4]);
			if( tile_on ){
				sync_input(infile);
				gdImageFilledRectangle(img,arg[1],arg[2],arg[3],arg[4],gdTiled);
				if (!quiet) fprintf(verbose_out,"colour = current tile");
			} else{
				colour=get_colour(infile,img);
				gdImageFilledRectangle(img,arg[1],arg[2],arg[3],arg[4],colour);
			}
			if (!quiet) fprintf(verbose_out,")\n");
			break;

		case POLY:      /* gdImagePolygon() */
			done = FALSE; i=0;

			if (!quiet) fprintf(verbose_out,"## Polygon ## (");
			colour=get_colour(infile,img);
			if (!quiet) fprintf(verbose_out,") ");

			arg[i++] = get_number(infile); /* get first point */
			arg[i++] = get_number(infile);

			while( ! done ){     /* get next point until EOL*/
				for(c=0; c<=1 ;c++){
					arg[i++]=get_number(infile);
				}
				if (!quiet) fprintf(verbose_out,"%d,%d to %d,%d; ",
					arg[i-4],arg[i-3],arg[i-2],arg[i -1]);
			}

			num_entries = i / 2;  i=0;
			for(n=0; n<num_entries; n++)
			{
				points[n].x = arg[i++];
				points[n].y = arg[i++];
			}
			if( brush_on ) {
				gdImagePolygon(img, points, num_entries, gdBrushed);
			} else if ( style_on ) {
				gdImagePolygon(img, points, num_entries, gdStyled);
			} else {
				gdImagePolygon(img, points, num_entries, colour);
			}

			done = FALSE;
			if (!quiet) fprintf(verbose_out,"\n");
			break;

		case FPOLY:      /* gdImageFilledPolygon() */
			done = FALSE; i=0;

			if (!quiet) fprintf(verbose_out,"## Filled Polygon ## (");
			colour=get_colour(infile,img);
			if (!quiet) fprintf(verbose_out,") ");

			arg[i++] = get_number(infile); /* get first point */
			arg[i++] = get_number(infile);

			while( ! done ){     /* get next point until EOL*/
				for(c=0; c<=1 ;c++){
					arg[i++]=get_number(infile);
				}
				if (!quiet) fprintf(verbose_out,"%d,%d to %d,%d; ",
					arg[i-4],arg[i-3],arg[i-2],arg[i -1]);
			}

			num_entries = i / 2;  i=0;
			for(n=0; n<num_entries; n++)
			{
				points[n].x = arg[i++];
				points[n].y = arg[i++];
			}

			if( tile_on )
			{
				gdImageFilledPolygon(img, points, num_entries, gdTiled);
			}
			else
			{
				gdImageFilledPolygon(img, points, num_entries, colour);
			}
			done = FALSE;
			if (!quiet) fprintf(verbose_out,"\n");
			break;

		case ARC:       /*      gdImageArc()            */
			for(i=1;i<7;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet) {
				fprintf(verbose_out,"## Arc ## Centred at %d,%d, width %d, height %d,\n",
				    arg[1],arg[2],arg[3],arg[4]);
				fprintf(verbose_out,"          starting at %d deg, ending at %d deg. (",
				    arg[5],arg[6]);
			}
			if( brush_on ){
			sync_input(infile);
			gdImageArc(img,arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],gdBrushed);
			if(!quiet) fprintf(verbose_out,"colour = current brush");
			} else if ( style_on ) {
			sync_input(infile);
			gdImageArc(img,arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],gdStyled);
			if(!quiet) fprintf(verbose_out,"colour = current style");
			} else{
			colour=get_colour(infile,img);
			gdImageArc(img,arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],colour);
			}
			if (!quiet) fprintf(verbose_out,")\n");
			break;

		case CIRCLE:
			for(i=1;i<4;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet) {
				fprintf(verbose_out,"## Circle ## Centred at %d,%d, diameter %d (",
				    arg[1],arg[2],arg[3]);
			}
			if( brush_on ){
			sync_input(infile);
			gdImageArc(img,arg[1],arg[2],arg[3],arg[3],0,360,gdBrushed);
			if(!quiet) fprintf(verbose_out,"colour = current brush");
			} else if ( style_on ) {
			sync_input(infile);
			gdImageArc(img,arg[1],arg[2],arg[3],arg[3],0,360,gdStyled);
			if(!quiet) fprintf(verbose_out,"colour = current style");
			} else{
			colour=get_colour(infile,img);
			gdImageArc(img,arg[1],arg[2],arg[3],arg[3],0,360,colour);
			}
			if (!quiet) fprintf(verbose_out,")\n");
			break;


		case SETPIXEL:      /*	gdImageSetPixel  */
			for(i=1;i<=2;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet) fprintf(verbose_out,"## Set Pixel ## at %d,%d to ",arg[1],arg[2]);
			colour=get_colour(infile,img);
			gdImageSetPixel(img,arg[1],arg[2],colour);
			if (!quiet) fprintf(verbose_out,".\n");
			break;

		case FILL:      /*      gdImageFill() */
			for(i=1;i<=2;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet) fprintf(verbose_out,"## Fill ## from %d,%d. (", arg[1],arg[2]);
			if( tile_on ){
				sync_input(infile);
				gdImageFill(img,arg[1],arg[2],gdTiled);
			} else {
				colour=get_colour(infile,img);
				gdImageFill(img,arg[1],arg[2],colour);
			}
			if (!quiet) fprintf(verbose_out,")\n");
			break;

		case FILLTOBORDER:      /*	gdImageFillToBorder()	*/
			for(i=1;i<=2;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet) fprintf(verbose_out,"## Fill ## from %d,%d. (", arg[1],arg[2]);
			colour=get_colour(infile,img);
			if (!quiet) fprintf(verbose_out,") to Border of ");
			if( tile_on ){
				sync_input(infile);
				gdImageFillToBorder(img,arg[1],arg[2],colour,gdTiled);
			} else {
				colour2=get_colour(infile,img);
				gdImageFillToBorder(img,arg[1],arg[2],colour,colour2);
			}
			if (!quiet) fprintf(verbose_out,".\n");
			break;

		case STRINGUP:
			up = TRUE;
		case STRING:
			if (!quiet && up) fprintf(verbose_out,"## String (Up) ## (");
			if (!quiet && !up) fprintf(verbose_out,"## String ## (");
			colour=get_colour(infile,img);
			if (!quiet) fprintf(verbose_out,") at location ");
			for(i=1;i<=2;i++){
				arg[i]=get_number(infile);
			}
			if (!quiet) fprintf(verbose_out," %d,%d, ",arg[1],arg[2]);
			i=get_token(infile);
			switch(i)
			{
			case TINY:
				s = get_string(infile);
				if (!quiet) fprintf(verbose_out,"[size: tiny] contents: %s\n",s);
				if( !up ) {
				gdImageString(img, gdFontTiny, arg[1],arg[2], s, colour);
				} else {
				gdImageStringUp(img, gdFontTiny, arg[1],arg[2], s, colour);
				}
				break;
			case SMALL:
				s = get_string(infile);
				if (!quiet) fprintf(verbose_out,"[size: small] contents: %s\n",s);
				if( !up ) {
				gdImageString(img, gdFontSmall, arg[1],arg[2], s, colour);
				} else {
				gdImageStringUp(img, gdFontSmall, arg[1],arg[2], s, colour);
				}
				break;
			case MEDIUM:
				s = get_string(infile);
				if (!quiet) fprintf(verbose_out,"[size: medium-bold] contents: %s\n",s);
				if( !up ) {
				gdImageString(img,gdFontMediumBold, arg[1],arg[2],s,colour);
				} else {
				gdImageStringUp(img,gdFontMediumBold,arg[1],arg[2],s,colour);
				}
				break;
			case LARGE:
				s = get_string(infile);
				if (!quiet) fprintf(verbose_out,"[size: large] contents: %s\n",s);
				if( !up ) {
				gdImageString(img, gdFontLarge, arg[1],arg[2], s, colour);
				} else {
				gdImageStringUp(img, gdFontLarge, arg[1],arg[2], s, colour);
				}
				break;
			case GIANT:
				s = get_string(infile);
				if (!quiet) fprintf(verbose_out,"[size: giant] contents: %s\n",s);
				if( !up ) {
				gdImageString(img, gdFontGiant, arg[1],arg[2], s, colour);
				} else {
				gdImageStringUp(img, gdFontGiant, arg[1],arg[2], s, colour);
				}
				break;
			}
			up = FALSE;
			break;

		case SETBRUSH:
			s = get_string(infile);
			brushfile = fopen(s,"rb");
			brush = gdImageCreateFromGif(brushfile);
			gdImageSetBrush(img,brush);
			brush_on = 1;
			if (!quiet) fprintf(verbose_out,"## Brush Set ## to: %s\n",s);
			break;

		case KILLBRUSH:
			brush_on = 0;
			if (!quiet) fprintf(verbose_out,"## Brush Killed ##\n");
			break;

		case SETSTYLE:
			i=0;
			end_of_line = FALSE;
			if (!quiet) fprintf(verbose_out,"## Style Set ## Colours: (");
			while( ! end_of_line ){
				colour=get_colour(infile,img);
				if (!quiet && !end_of_line) fprintf(verbose_out,"), (");
				style[i++] = colour;
			}
			if (!quiet) fprintf(verbose_out,")\n");
			gdImageSetStyle(img, style, i-1);
			style_on = TRUE;
			end_of_line = FALSE;
			break;

		case KILLSTYLE:
			style_on = 0;
			if (!quiet) fprintf(verbose_out,"## Style Killed ##\n");
			break;

		case SETTILE:
			s = get_string(infile);
			tilefile = fopen(s,"rb");
			tile = gdImageCreateFromGif(tilefile);
			gdImageSetTile(img,tile);
			tile_on = TRUE;
			if (!quiet) fprintf(verbose_out,"## Tile Set ## to: %s\n",s);
			break;

		case KILLTILE:
			tile_on = 0;
			if (!quiet) fprintf(verbose_out,"## Tile Killed ##\n");
			break;

		case COPY:
			copy_to_gif(infile, img, 0);
			break;

		case COPYRESIZED:
			copy_to_gif(infile, img, 1);
			break;

		case TRANSPARENT:
			if (!quiet) fprintf(verbose_out,"## Make transparent [");
			colour=get_colour(infile,img);
			gdImageColorTransparent(img,colour);
			if (!quiet) fprintf(verbose_out,"]\n");
			break;

		case INTERLACE:
			gdImageInterlace(img,1);
			if (!quiet) fprintf(verbose_out,"Image is interlaced\n");
			break;

		case SIZEX:
			size = gdImageSX(img);
			if (!quiet) fprintf(verbose_out,"## Size - X ## is %d\n",size);
			break;

		case SIZEY:
			size = gdImageSY(img);
			if (!quiet) fprintf(verbose_out,"## Size - Y ## is %d\n",size);
			break;

		case COMMENT:
			sync_input(infile);
			break;

		default:
			if( ! finished )
			{
			    if (!quiet)
					fprintf(verbose_out,"Line Skipped, bad directive or syntax error\n");
			}
			else
			{
			    if (!quiet) fprintf(verbose_out,"EOF: fly finished.\n");
			}
			sync_input(infile);
			break;
		}
	}	while( ! finished );

	/*  Write the gd to the GIF output file and exit */
	gdImageGif(img,outfile);
	fclose(outfile);
	gdImageDestroy(img);
	exit(0);
}

/******************************************************************************
**
**  get_string
**
**  returns a string from the current input line: from the current point
**  to the end of line.
**
**  Used by:
**  string,stringup,chr,chrup,setbrush,settile
**
******************************************************************************/
char *get_string(FILE *infile){
	int     c,i=0;
	char    temp[1024], *string, *p;

	while(( (c=getc(infile)) != EOF ) && ( c != '\n') ){
		temp[i++]=c;
	}

	if( c == EOF ) {
		finished = TRUE;
	}
	temp[i]='\0';
	p=temp;
	string=(char *)my_newmem(strlen(p));
	sprintf(string,"%s",temp);

	return string;
}

/******************************************************************************
**
**  get_token
**
**  Gets the next "token" from the input line.
**
**  Used by:
**  all
**
******************************************************************************/
int get_token(FILE *infile){
	int     c,i=0;
	char    temp[80], *input_type, *p;
	char    *line="line", *poly="poly", *fpoly="fpoly", *rect="rect",
			*frect="frect", *dline="dline", *arc="arc", *size="size",
			*new="new", *existing="existing", *setpixel="setpixel",
			*filltoborder="filltoborder", *fill="fill", *string="string",
			*stringup="stringup", *copy="copy", *copyresized="copyresized",
			*transparent="transparent", *interlace="interlace", *sizex="sizex",
			*sizey="sizey", *setbrush="setbrush", *killbrush="killbrush",
			*settile="settile", *killtile="killtile", *setstyle="setstyle",
			*killstyle="killstyle", *tiny="tiny", *small="small",
			*medium="medium", *large="large", *giant="giant",
			*zero="0", *one="1", *circle="circle", *comment="#";

	while(((c=getc(infile))!=EOF)&&(c!=' ')&&(c!='\n')&&(c!=',')&&(c!='='))
	{
		temp[i++]=c;
		if(temp[0] == '#') break;
	}

	if( c == EOF )
	{
		finished = TRUE;
		return NULL;
	}
	temp[i]='\0';
	p=temp;
	input_type=(char*)my_newmem(strlen(p));
	sprintf(input_type,"%s",temp);

	if( strcmp(input_type, line) == 0 ){
		free(input_type);
		return LINE;
	}
	if( strcmp(input_type, rect) == 0 ){
		free(input_type);
		return RECT;
	}
	if( strcmp(input_type, dline) == 0 ){
		free(input_type);
		return DLINE;
	}
	if( strcmp(input_type, frect) == 0 ){
		free(input_type);
		return FRECT;
	}
	if( strcmp(input_type, circle) == 0 ){
		free(input_type);
		return CIRCLE;
	}
	if( strcmp(input_type, arc) == 0 ){
		free(input_type);
		return ARC;
	}
	if( strcmp(input_type, poly) == 0 ){
		free(input_type);
		return POLY;
	}
	if( strcmp(input_type, fpoly) == 0 ){
		free(input_type);
		return FPOLY;
	}
	if( strcmp(input_type, size) == 0 ){
		free(input_type);
		return SIZE;
	}
	if( strcmp(input_type, new) == 0 ){
		free(input_type);
		return NEW;
	}
	if( strcmp(input_type, existing) == 0 ){
		free(input_type);
		return EXISTING;
	}
	if( strcmp(input_type, copyresized) == 0 ){
		free(input_type);
		return COPYRESIZED;
	}
	if( strcmp(input_type, copy) == 0 ){
		free(input_type);
		return COPY;
	}
	if( strcmp(input_type, fill) == 0 ){
		free(input_type);
		return FILL;
	}
	if( strcmp(input_type, filltoborder) == 0 ){
		free(input_type);
		return FILLTOBORDER;
	}
	if( strcmp(input_type, setpixel) == 0 ){
		free(input_type);
		return SETPIXEL;
	}
	if( strcmp(input_type, string) == 0 ){
		free(input_type);
		return STRING;
	}
	if( strcmp(input_type, stringup) == 0 ){
		free(input_type);
		return STRINGUP;
	}
	if( strcmp(input_type, sizex) == 0 ){
		free(input_type);
		return SIZEX;
	}
	if( strcmp(input_type, sizey) == 0 ){
		free(input_type);
		return SIZEY;
	}
	if( strcmp(input_type, setbrush) == 0 ){
		free(input_type);
		return SETBRUSH;
	}
	if( strcmp(input_type, killbrush) == 0 ){
		free(input_type);
		return KILLBRUSH;
	}
	if( strcmp(input_type, settile) == 0 ){
		free(input_type);
		return SETTILE;
	}
	if( strcmp(input_type, killtile) == 0 ){
		free(input_type);
		return KILLTILE;
	}
	if( strcmp(input_type, setstyle) == 0 ){
		free(input_type);
		return SETSTYLE;
	}
	if( strcmp(input_type, killstyle) == 0 ){
		free(input_type);
		return KILLSTYLE;
	}
	if( strcmp(input_type, interlace) == 0 ){
		free(input_type);
		return INTERLACE;
	}
	if( strcmp(input_type, transparent) == 0 ){
		free(input_type);
		return TRANSPARENT;
	}
	if( strcmp(input_type, tiny) == 0 ){
		free(input_type);
		return TINY;
	}
	if( strcmp(input_type, zero) == 0 ){
		free(input_type);
		return SMALL;
	}
	if( strcmp(input_type, small) == 0 ){
		free(input_type);
		return SMALL;
	}
	if( strcmp(input_type, medium) == 0 ){
		free(input_type);
		return MEDIUM;
	}
	if( strcmp(input_type, one) == 0 ){
		free(input_type);
		return LARGE;
	}
	if( strcmp(input_type, large) == 0 ){
		free(input_type);
		return LARGE;
	}
	if( strcmp(input_type, giant) == 0 ){
		free(input_type);
		return GIANT;
	}
	if( strcmp(input_type, comment) == 0){
		free(input_type);
		return COMMENT;
	}
	free(input_type);
	return NULL;
}

/******************************************************************************
**
**  get_number
**
**  grabs a number from the current input line. Reads up to a comma or newline.
**
**  Used by:
**  line, dline, rect, frect, poly, fpoly, arc, setpixel, fill, filltoborder,
**  string, stringup, chr, chrup.
**
******************************************************************************/
int get_number(FILE *infile){
	int     c,i=0;
	char    tmp[80];

	while(( (c=getc(infile)) != EOF ) && ( c != ',') && (c != '\n')){
		tmp[i++]=c;
	}
	if( c != EOF ) {
		tmp[i]='\0';
		if( c == '\n') {
			done = TRUE;
		}
		return atoi(tmp);
	}
	else {
		tmp[i]='\0';
		finished = TRUE;
		return atoi(tmp);
	}
	return NULL;
}

/******************************************************************************
**
**  get_colour
**
**  Gets a R,G,B colour value from the current input line.
**  Returns the integer colour index.
**
**  Used by:
**  line, dline, rect, frect, poly, fpoly, arc, setpixel, fill, filltoborder,
**  string, stringup, chr, chrup, setstyle, transparent.
**
******************************************************************************/
int get_colour(FILE *infile, gdImagePtr img){
	int     c,i,count,colourIndex, colour[3];
	char    temp[5];

	for(count=0;count<3;count++){
		i=0;
		while(( (c=getc(infile)) != EOF )&&( c !=',')&&(c !='\n')){
			temp[i++]=c;
		}
		temp[i]='\0';
		if( c == '\n') end_of_line = TRUE;
		if( c == EOF ) finished = TRUE;
		colour[count]=atoi(temp);
	}
	if( (c=getc(infile)) != EOF )  { 
		ungetc(c,infile); 
	}
	else { 
		finished = TRUE; 
	}
	/* Original comments from Claus Hofmann. I don't have any idea what they
         * mean, but I'll put 'em here anyhow.
	 */
	/* zuerst nachschauen, ob es die gewuenschte Farbe schon in der 
	 * colortable gibt. Erst wenn es die Farbe nicht gibt einen neuen
	 * Index in der Tabelle allocieren.
	 */
	colourIndex=gdImageColorExact(img,colour[0],colour[1],colour[2]);
	if (-1 == colourIndex) {
		colourIndex=gdImageColorAllocate(img,colour[0],colour[1],colour[2]);
	}
	if (!quiet)
		fprintf(verbose_out,"colour: %d, %d, %d = %d",
			colour[0],colour[1],colour[2], colourIndex);
	return colourIndex;

}

/******************************************************************************
**
**  copy_to_gif
**
**  Copies a gif to the current image. Location of gif and coordinates are
**  specified on the input line.
**
**  Used by:
**  copy, copyresized.
**
******************************************************************************/
void copy_to_gif(FILE *infile, gdImagePtr img, int resize){
	int     c,i=0,arg[8];
	char    temp[1256], *filename;
	FILE	*img_to_copy;
	gdImagePtr	img_file;

	/*	Get the coordinates	*/
	for(i=0;i<=5;i++){
		arg[i]=get_number(infile);
	}
	if( resize == 1 ){
		arg[i]=get_number(infile);
		i++;
		arg[i]=get_number(infile);
	}
	i=0;
	/*	Get the filename	*/
	while(( (c=getc(infile)) != EOF ) && ( c != ' ') && ( c != '\n') ){
		temp[i++]=c;
	}
	temp[i]='\0';
	filename=(char*)my_newmem(i * sizeof(char));
	sprintf(filename,"%s",temp);
	
	if(!quiet) fprintf(verbose_out,"Copying GIF from existing file: %s\n",filename);

	if( (img_to_copy = fopen(filename, "rb")) == NULL ) {
		fprintf(stderr,"Error: Cannot read existing GIF file \"%s\"\n",
			filename);
		exit(0);
	}
	img_file = gdImageCreateFromGif(img_to_copy);
	fclose(img_to_copy);

	if ((arg[2] == -1)&&(arg[3] == -1) &(arg[4] == -1)&&(arg[5] == -1)) {
			/* another comment from Claus Hofmann. I'm getting curious now. */
			/* gesamtes Bild 
			*/
		arg[2] = arg[3] = 0;
		arg[4] = img_file->sx;
		arg[5] = img_file->sy;
	}
	if( resize == 1 )
	{
		if(!quiet) fprintf(verbose_out,"Copying %s (area %d,%d - %d,%d) to area %d,%d - %d,%d.\n",
			filename,arg[4],arg[5],arg[6], arg[7],arg[0],arg[1],arg[2],arg[3]);
		gdImageCopyResized(img, img_file, arg[4], arg[5], arg[0], arg[1],
			(arg[6] - arg[4]), (arg[7] - arg[5]), (arg[2] - arg[0]),
			(arg[3] - arg[1]));
	}
	else
	{
		if(!quiet) fprintf(verbose_out,"Copying %s to coordinates %d,%d\n",filename,arg[0],arg[1]);
		gdImageCopy(img, img_file, arg[0], arg[1], arg[2], arg[3],
					arg[4] - arg[2], arg[5] - arg[3]);
	}
	gdImageDestroy(img_file);

	return;
}

/******************************************************************************
**
**  sync_input
**
**  synchronises input line - reads to end of line, leaving file pointer
**  at first character of next line.
**
**  Used by:
**  main program - error handling.
**
******************************************************************************/
void
sync_input(FILE *infile)
{
	int c;

	while( ( (c=getc(infile)) != EOF ) && (c != '\n') ) ;
	if( c == EOF ) finished = TRUE;
	return;
}

/******************************************************************************
**
**  process_args
**
**  processes the command line arguments
**
**  Used by:
**  main program.
**
******************************************************************************/
int
process_args(int argc, char *argv[])
{
	char *check;
	int c, errflag=0;
	extern char *optarg;
	extern int optind;

	/* if( (check=strstr(argv[0],"flycgi")) != NULL ) 
	{
		quiet = TRUE;
		fprintf(stdout,"Content-type: image/gif\n\n");
	} */

	while ((c=getopt(argc, argv, "qhvi:o:")) != EOF)
	{
		switch (c) {
			case 'q': quiet = TRUE;
			        break;
			case 'v':
			case 'h': fprintf(stdout,"fly, version %s\n\n%s\n", version, help);
			          exit(0);
			        break;
			case 'o': output_file=(char *)my_newmem(strlen(optarg)*sizeof(char));
			          sprintf(output_file,"%s",optarg);
			        break;
			case 'i': input_file=(char *)my_newmem(strlen(optarg)*sizeof(char));
			          sprintf(input_file,"%s",optarg);
			        break;
			case '?': errflag = 1;
			        break;
		}
		if (errflag)
		{
			fprintf(stderr,"%s\n", usage);
			exit(1);
		}
	}
	if( input_file )
	{
		if ( (infile = fopen(input_file,"r")) == NULL )
		{
			fprintf(stderr, "Failed to open input file, %s.\n",
				input_file);
			return FALSE;
		}
	}
	else
	{
		infile = stdin;
	}
	if( output_file )
	{
		if ( (outfile = fopen(output_file,"wb")) == NULL )
		{
			fprintf(stderr, "Failed to open output file, %s.\n",
				output_file);
			return FALSE;
		}
		verbose_out = stdout;
	}
	else
	{
		outfile = stdout;
		verbose_out = stderr;
	}
	return TRUE;
}

/******************************************************************************
**
**  get_image
**
**  creates a new image or uses an existing one as a template.
**
**  Used by:
**  main program
**
******************************************************************************/
gdImagePtr get_image(int type, int argc, char *argv[]){
	FILE *in;
	int n=0, ch, num[10];
	char fname[1256], *filename;
	gdImagePtr image;
	int newtype;

	if( type == EXISTING ) {
		/* 	fprintf(stderr,"Creating GIF from existing file:"); */
		while(( (ch=getc(infile)) != EOF ) && ( ch != ' ') && ( ch != '\n')){
			fname[n++]=ch;
		}
		fname[n]='\0';
		filename = (char *) my_newmem( n );
		sprintf(filename,"%s",fname);
		/*  fprintf(stderr," %s\n",filename); */
		if( (in = fopen(filename, "rb")) == NULL ) {
			fprintf(stderr,"Error: Cannot read existing GIF file \"%s\"\n",
			    filename);
			exit(0);
		}
		else {
			if(!quiet) fprintf(verbose_out,"Creating image from existing gif <%s>\n",
			    filename);
			image = gdImageCreateFromGif(in);
			fclose(in);
		}
	}
	else if( type == NEW ){
		newtype = get_token(infile);
		while( (newtype == COMMENT) || (newtype != SIZE) )
		{
			sync_input(infile);
			newtype = get_token(infile);
		}
		if( newtype != SIZE ) {
			if( argc == 2){
				fprintf(stderr,"Error: <stdin> second line ");
				fprintf(stderr,"must have a 'size' command\n");
			}
			else{
				fprintf(stderr,"Error: %s second line must ");
				fprintf(stderr,"have a 'size' command\n",argv[1]);
			}
			exit(0);
		}
		for( n=1; n<=2; n++ ){
			num[n]=get_number(infile);
		}
		if (!quiet) fprintf(verbose_out,"Creating new %d by %d gif, <%s>\n",
				num[1],num[2],output_file);
		image = gdImageCreate(num[1],num[2]);
	}
	return image;
}

/******************************************************************************
**
**  my_newmem: grab some memory.
**
**  -  Concentrates memory error handling in one place.
**
**
**  Used by:
**  string,stringup,chr,chrup,setbrush,settile
**
******************************************************************************/
void *
my_newmem(size_t size)
{
	void	*p;

	if ((p = malloc(size +1)) == NULL)
	{
		fprintf(stderr, "fly: ran out of memory\n");
		exit(1);
	}

	return p;
}

/******************************************************************************/
