diff options
Diffstat (limited to 'tdefile-plugins/gif/gif-info.c')
-rw-r--r-- | tdefile-plugins/gif/gif-info.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/tdefile-plugins/gif/gif-info.c b/tdefile-plugins/gif/gif-info.c new file mode 100644 index 00000000..2f64a2f2 --- /dev/null +++ b/tdefile-plugins/gif/gif-info.c @@ -0,0 +1,561 @@ +/* +** $Id$ +** +** Minimal GIF parser, for use in extracting and setting metadata. +** Modified for standalone & KDE calling by Bryce Nesbitt +** +** TODO: +** Support gif comments that span more than one comment block. +** Verify that Unicode utf-8 is fully unmolested by this code. +** Implement gif structure verifier. +** +** Based on: GIFtrans v1.12.2 +** Copyright (C) 24.2.94 by Andreas Ley <[email protected]> +** +******************************************************************************* +** +** Original distribution site is +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.c +** A man-page by [email protected] (Kai Nordlund) is at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.1 +** An online version by [email protected] (Dave Taylor) is at +** http://www.intuitive.com/coolweb/Addons/giftrans-doc.html +** To compile for MS-DOS or OS/2, you need getopt: +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/getopt.c +** MS-DOS executable can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.exe +** OS/2 executable can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.os2.exe +** A template rgb.txt for use with the MS-DOS version can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/rgb.txt +** Additional info can be found on +** http://melmac.corp.harris.com/transparent_images.html +** +** The GIF file format is documented in +** ftp://ftp.uu.net/doc/literary/obi/Standards/Graphics/Formats/gif89a.doc.Z +** A good quick reference is at: +** http://www.goice.co.jp/member/mo/formats/gif.html +** +******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#define STANDALONE_COMPILE + +extern int set_gif_comment( const char * original_filename, char * comment ); +extern void * get_gif_info( const char * original_filename ); +extern int validate_gif_structure( const char * original_filename ); +int giftrans( FILE * src, FILE * dest); + +#define WARNING_GARBAGE 1 /* Original file had some unspecified content */ +#define ERROR_NOT_A_JPEG 5 /* Original file not a proper jpeg (must be 1st) */ +#define ERROR_TEMP_FILE 6 /* Problem writing temporay file */ +#define ERROR_SCREWUP 7 /* Original file is now damaged. Ooops. */ +#define ERROR_PREMATURE_EOF 8 /* Unexpected end of file */ +#define ERROR_BAD_MARKER 9 /* Marker with illegal length */ +#define ERROR_MARKER_ORDER 10 /* File seems to be mixed up */ + +/*****************************************************************************/ +#ifndef FALSE +#define FALSE (0) /* This is the naked Truth */ +#define TRUE (1) /* and this is the Light */ +#endif + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ +#define READ_BINARY "r" +#define WRITE_BINARY "w" +#else +#ifdef VMS /* VMS is very nonstandard */ +#define READ_BINARY "rb", "ctx=stm" +#define WRITE_BINARY "wb", "ctx=stm" +#else /* standard ANSI-compliant case */ +#define READ_BINARY "rb" +#define WRITE_BINARY "wb" +#endif +#endif + +#define SUCCESS (0) +#define FAILURE (1) + +static char skipcomment,list,verbose,output,debug; +char *global_comment; +static long int pos; + +static char true[] = "True"; +static char false[] = "False"; +static char id[] = "$Id$"; + +/*****************************************************************************/ + +#define readword(buffer) ((buffer)[0]+256*(buffer)[1]) +#define readflag(buffer) ((buffer)?true:false) +#define hex(c) ('a'<=(c)&&(c)<='z'?(c)-'a'+10:'A'<=(c)&&(c)<='Z'?(c)-'A'+10:(c)-'0') + +void dump(adr,data,len) +long int adr; +unsigned char *data; +size_t len; +{ + int i; + + while (len>0) { + (void)fprintf(stderr,"%08lx:%*s",adr,(int)((adr%16)*3+(adr%16>8?1:0)),""); + for (i=adr%16;i<16&&len>0;i++,adr++,data++,len--) + (void)fprintf(stderr,"%s%02x",i==8?" ":" ",*data); + (void)fprintf(stderr,"\n"); + } +} + +void writedata(dest,data,len) +FILE *dest; +unsigned char *data; +size_t len; +{ + unsigned char size; + + while (len) { + size=len<256?len:255; + (void)fwrite((void *)&size,1,1,dest); + (void)fwrite((void *)data,(size_t)size,1,dest); + data+=size; + len-=size; + } + size=0; + (void)fwrite((void *)&size,1,1,dest); +} + +void skipdata(src) +FILE *src; +{ + unsigned char size,buffer[256]; + + do { + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + if (debug) { + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + dump(pos,buffer,(size_t)size); + } + else + (void)fseek(src,(long int)size,SEEK_CUR); + } while (!feof(src)&&size>0); +} + +void transblock(src,dest) +FILE *src; +FILE *dest; +{ + unsigned char size,buffer[256]; + + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + if (output) + (void)fwrite((void *)&size,1,1,dest); + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + if (debug) + dump(pos,buffer,(size_t)size); + if (output) + (void)fwrite((void *)buffer,(size_t)size,1,dest); +} + +void dumpcomment(src) +FILE *src; +{ + unsigned char size,buffer[256]; + size_t i; + + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + (void)fread((void *)buffer,(size_t)size,1,src); + if (debug) + dump(pos+1,buffer,(size_t)size); + for (i=0; i<(size_t)size; i++) + { + if ( buffer[i] >= 0x20 || buffer[i] == '\t' || + buffer[i] =='\r' || buffer[i] == '\n') + (void)putc(buffer[i],stderr); + else + (void)fprintf(stderr,"\\%03o",buffer[i]); + } + (void)fseek(src,(long int)pos,SEEK_SET); +} + +void transdata(src,dest) +FILE *src; +FILE *dest; +{ + unsigned char size,buffer[256]; + + do { + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + if (output) + (void)fwrite((void *)&size,1,1,dest); + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + if (debug) + dump(pos,buffer,(size_t)size); + if (output) + (void)fwrite((void *)buffer,(size_t)size,1,dest); + } while (!feof(src)&&size>0); +} + + +/*****************************************************************************/ + +int giftrans(src,dest) +FILE *src; +FILE *dest; +{ + unsigned char buffer[3*256],lsd[7],gct[3*256]; + unsigned int cnt,cols,size,gct_size; + + /* Header */ + pos=ftell(src); + (void)fread((void *)buffer,6,1,src); + if (strncmp((char *)buffer,"GIF",3)) { + (void)fprintf(stderr,"Not GIF file!\n"); + return(1); + } + if (verbose && debug) { + buffer[6]='\0'; + (void)fprintf(stderr,"Header: \"%s\"\n",buffer); + } + if (debug) + dump(pos,buffer,6); + if (output) { + (void)fwrite((void *)buffer,6,1,dest); + } + + /* Logical Screen Descriptor */ + pos=ftell(src); + (void)fread((void *)lsd,7,1,src); + if (verbose) { + //(void)fprintf(stderr,"Logical Screen Descriptor:\n"); + (void)fprintf(stderr,"Size : %dx%d pixels\n",readword(lsd), readword(lsd+2)); + //(void)fprintf(stderr,"Global Color Table Flag: %s\n",readflag(lsd[4]&0x80)); + (void)fprintf(stderr,"Depth : %d bits\n",(lsd[4]&0x70>>4)+1); + //if (lsd[4]&0x80) { + // (void)fprintf(stderr,"\tSort Flag: %s\n",readflag(lsd[4]&0x8)); + // (void)fprintf(stderr,"\tSize of Global Color Table: %d colors\n",2<<(lsd[4]&0x7)); + // (void)fprintf(stderr,"\tBackground Color Index: %d\n",lsd[5]); + //} + if (lsd[6]) + (void)fprintf(stderr,"Pixel Aspect Ratio: %d (Aspect Ratio %f)\n",lsd[6],((double)lsd[6]+15)/64); + } + if (debug) + dump(pos,lsd,7); + if (output) + (void)fwrite((void *)lsd,7,1,dest); + + /* Global Color Table */ + if (lsd[4]&0x80) { + gct_size=2<<(lsd[4]&0x7); + pos=ftell(src); + (void)fread((void *)gct,gct_size,3,src); + if (output) + (void)fwrite((void *)gct,gct_size,3,dest); + } + + do { + pos=ftell(src); + (void)fread((void *)buffer,1,1,src); + switch (buffer[0]) { + case 0x2c: /* Image Descriptor */ + if (verbose && debug) + (void)fprintf(stderr,"Image Descriptor:\n"); + (void)fread((void *)(buffer+1),9,1,src); + if (debug) + dump(pos,buffer,10); + if (output) + (void)fwrite((void *)buffer,10,1,dest); + /* Local Color Table */ + if (buffer[8]&0x80) { + size=2<<(buffer[8]&0x7); + pos=ftell(src); + (void)fread((void *)buffer,size,3,src); + if (verbose && debug) { + (void)fprintf(stderr,"Local Color Table:\n"); + for(cnt=0;cnt<size;cnt++) + (void)fprintf(stderr,"\tColor %d: Red %d, Green %d, Blue %d\n",cnt,buffer[3*cnt],buffer[3*cnt+1],buffer[3*cnt+2]); + } + if (debug) + dump(pos,buffer,size*3); + if (output) + (void)fwrite((void *)buffer,size,3,dest); + } + /* Table Based Image Data */ + pos=ftell(src); + (void)fread((void *)buffer,1,1,src); + if (verbose && debug) { + (void)fprintf(stderr,"Table Based Image Data:\n"); + (void)fprintf(stderr,"\tLZW Minimum Code Size: 0x%02x\n",buffer[0]); + } + if (debug) + dump(pos,buffer,1); + if (output) + (void)fwrite((void *)buffer,1,1,dest); + transdata(src,dest); + break; + case 0x21: /* Extension */ + (void)fread((void *)(buffer+1),1,1,src); + switch (buffer[1]) { + case 0xfe: /* Comment Extension */ + if (verbose) + { + (void)fprintf(stderr,"Comment: "); + dumpcomment(src); + (void)fprintf(stderr,"\n"); + } + if (debug) + dump(pos,buffer,2); + if (skipcomment) + skipdata(src); + else { + if (output) + (void)fwrite((void *)buffer,2,1,dest); + transdata(src,dest); + } + break; + case 0x01: /* Plain Text Extension */ + case 0xf9: /* Graphic Control Extension */ + case 0xff: /* Application Extension */ + default: + if (verbose && debug) + (void)fprintf(stderr,"Extension type: 0x%02x\n",buffer[1]); + if (debug) + dump(pos,buffer,2); + if (output) + (void)fwrite((void *)buffer,2,1,dest); + transblock(src,dest); + transdata(src,dest); + break; + } + break; + case 0x3b: /* Trailer (write comment just before here) */ + if (verbose && debug) + (void)fprintf(stderr,"Trailer %o\n", pos); + if (debug) + dump(pos,buffer,1); + if (global_comment && *global_comment && output) { + (void)fputs("\041\376",dest); + writedata(dest,(unsigned char *)global_comment,strlen(global_comment)); + } + if (output) + (void)fwrite((void *)buffer,1,1,dest); + break; + default: + (void)fprintf(stderr,"0x%08lx: Error, unknown block 0x%02x!\n",ftell(src)-1,buffer[0]); + if (debug) + dump(pos,buffer,1); + return(1); + } + } while (buffer[0]!=0x3b&&!feof(src)); + return(buffer[0]==0x3b?SUCCESS:FAILURE); +} + + +/****************************************************************************/ +extern int validate_gif_structure( const char * original_filename ) +{ +FILE * infile; +int rc; + + if ((infile = fopen(original_filename, READ_BINARY)) == NULL) { + fprintf(stderr, "can't open gif image '%s'\n", original_filename); + return( 1 ); + } + output = FALSE; + verbose = FALSE; + debug = FALSE; + skipcomment = FALSE; + rc = giftrans( infile, NULL ); + fclose( infile ); + return( rc ); +} + + +/****************************************************************************/ +extern void * get_gif_info( const char * original_filename ) +{ +FILE * infile; + + if ((infile = fopen(original_filename, READ_BINARY)) == NULL) { + fprintf(stderr, "can't open gif image '%s'\n", original_filename); + return( NULL ); + } + + output = FALSE; + verbose = TRUE; + debug = FALSE; + skipcomment = FALSE; + giftrans( infile, NULL ); + return( NULL ); +} + + +/***************************************************************************** + Modify the file in place, but be paranoid and safe about it. + It's worth a few extra CPU cycles to make sure we never + destory an original image: + + 1) Validate the input file. + 2) Open a temporary file in same directory (filenameXX). + 3) Copy the data, writing a new comment block. + 4) Sync everything to disc. + 5) Validate the temporary file. + 6) Move the temporary file over the original. +*/ +extern int set_gif_comment( const char * original_filename, char * comment ) +{ +int i; +int rc; +char * temp_filename; +int temp_filename_length; +struct stat statbuf; +FILE * infile; +FILE * outfile; + + + /* + * Make sure we're dealing with a proper input file. Safety first! + */ + if( validate_gif_structure( original_filename ) ) { + fprintf(stderr, "error validating gif image '%s'\n", original_filename); + return(ERROR_NOT_A_JPEG); + } + + /* Get a unique temporary file in the same directory. Hopefully + * if things go wrong, this file will still be left for recovery purposes. + * + * NB: I hate these stupid unsafe string functions in C... + */ + outfile = NULL; + temp_filename_length = strlen( original_filename) + 4; + temp_filename = (char *)calloc( temp_filename_length, 1 ); + for( i=0; i<10; i++ ) { + snprintf( temp_filename, temp_filename_length, "%s%d", original_filename, i ); + if( (stat( temp_filename, &statbuf )) && (outfile = fopen(temp_filename, WRITE_BINARY)) ) { + //fprintf(stderr, "opened temporary file '%s'\n", temp_filename); + break; + } + } + if( !outfile ) { + fprintf(stderr, "failed opening temporary file '%s'\n", temp_filename); + return(ERROR_TEMP_FILE); + } + + if ((infile = fopen(original_filename, READ_BINARY)) == NULL) { + fclose( outfile ); + fprintf(stderr, "can't open gif image '%s'\n", original_filename); + return(ERROR_NOT_A_JPEG); + } + /* Let's do it */ + output = TRUE; + verbose = FALSE; + debug = FALSE; + skipcomment = TRUE; + global_comment = comment; + rc = giftrans( infile, outfile ); + fclose( infile ); + fsync( fileno( outfile) ); /* Flush it to disk. IMPORTANT!! */ + /* We really should also flush the directory */ + + /* If everything is OK, and if the new file validates, move + it over the top of the original */ + if ( fclose( outfile ) || validate_gif_structure( temp_filename ) ) { + fprintf(stderr, "error in temporary file '%s'\n", temp_filename); + return(ERROR_TEMP_FILE); + } + if( rc ) { + unlink( temp_filename ); + return( rc ); + } + if( rename( temp_filename, original_filename ) ) { + fprintf(stderr, "error renaming '%s' to '%s'\n", temp_filename, original_filename); + return(ERROR_TEMP_FILE); + } + + return(0); +} + + +/*****************************************************************************/ +#ifdef STANDALONE_COMPILE +int +main (int argc, char **argv) +{ + char * progname; + char * filename; + char * comment; + FILE * fp; + int error; + + /* Process command line arguments... */ + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "gif-info"; /* in case C library doesn't provide it */ + if( argc < 2 || argc > 3) { + fprintf(stderr, "Usage: %s <filename> [\"<comment>\"]\nReads gif image info or sets comment.\n", progname); + return(5); + } + filename = argv[1]; + comment = argv[2]; + + + /* Check if file is readable... */ + if ((fp = fopen(filename, READ_BINARY)) == NULL) { + fprintf(stderr, "Error: Can't open file '%s'\n", filename); + return(5); + } + fclose(fp); + + /* Check if we really have a commentable image file here... */ + if( validate_gif_structure( filename ) ) { + fprintf(stderr, "Error: error parsing file '%s' as a gif image\n", filename); + return(5); + } + + if( argc == 2 ) { + get_gif_info( filename ); + } + else { + set_gif_comment( filename, comment ); + } + + return( 0 ); +} +#endif + |