summaryrefslogtreecommitdiffstats
path: root/tdefile-plugins/gif/gif-info.c
diff options
context:
space:
mode:
Diffstat (limited to 'tdefile-plugins/gif/gif-info.c')
-rw-r--r--tdefile-plugins/gif/gif-info.c561
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 <ley@rz.uni-karlsruhe.de>
+**
+*******************************************************************************
+**
+** Original distribution site is
+** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.c
+** A man-page by knordlun@fltxa.helsinki.fi (Kai Nordlund) is at
+** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.1
+** An online version by taylor@intuitive.com (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
+