/***********************************************************************\ | Copyright © 1997,2004, by Philip David Howard - all rights reserved | | | | author Philip D. Howard | | email libh at ipal dot org | \***********************************************************************/ /***********************************************************************\ | program gifscan | | | | purpose Scan a GIF file to see what is in it. | | | | usage gifscan < file.gif | | | | note This program is "patent free" in that it contains no | | code to implement any aspect of the LZW compression | | algorithm whose patent is held by Unisys. | | | | note GIF is a service mark, property of CompuServe, Inc. | | | | note The GIF standard is being deprecated in favor of the | | new PNG (Portable Network Graphics) standard. | \***********************************************************************/ #include /***********************************************************************\ | getbyte | | macro to read a byte and quit with an error message if EOF | \***********************************************************************/ #define getbyte(v,m) if(((v)=getc((infp)))==EOF)return "." m; #define getbytepr(v,m) if(((v)=getc((infp)))==EOF)return "." m;printf(" %02x",(v)); #define getword(v,m,n) if((l=getc((infp)))==EOF)return "." m;printf(" %02x",l);if((h=getc((infp)))==EOF)return "." n;printf(" %02x",h);(v)=l+(h<<8); /***********************************************************************\ | prch | | function to print a character only as printable characters | \***********************************************************************/ int prch( int ch ) { switch ( ch ) { case '\a': fputs( "\\a" , stdout ); break; case '\b': fputs( "\\b" , stdout ); break; case 27: fputs( "\\e" , stdout ); break; case '\f': fputs( "\\f" , stdout ); break; case '\n': fputs( "\\n" , stdout ); break; case '\r': fputs( "\\r" , stdout ); break; case '\t': fputs( "\\t" , stdout ); break; default: if ( ch < 32 ) { putc( '^' , stdout ); putc( ch + 0x40 , stdout ); return 2; } else if ( ch >= 127 ) { putc( '\\' , stdout ); putc( "01234567"[ ( ch >> 6 ) & 7 ] , stdout ); putc( "01234567"[ ( ch >> 3 ) & 7 ] , stdout ); putc( "01234567"[ ch & 7 ] , stdout ); return 4; } else if ( ch == '^' || ch == '\\' ) { putc( ch , stdout ); putc( ch , stdout ); return 2; } else putc( ch , stdout ); } return 1; } const char * gifscan ( int argc , char * * argv ) { static const char * cnames[ 3 ] = { "red" , "green" , "blue" }; unsigned char buffer[ 256 ]; FILE * infp; unsigned long pos; /* current byte position in GIF file */ int arat; /* aspect ratio */ int bgci; /* background color index */ int blkid; /* block id */ int bsize; /* block size of data block */ int ccw; /* character cell width */ int cch; /* character cell height */ int cres; /* color resolution */ int delay; /* delay in 100ms units between images */ int disp; /* image disposal method */ int eof; /* end of file flag */ int extid; /* extension id */ int gcsf; /* global color sort flag */ int gctp; /* global color table present */ int gcts; /* global color table size */ int h; /* hi byte */ int i; /* loop index */ int ih; /* image height */ int ilp; /* image left position */ int intf; /* interlace flag */ int itp; /* image top position */ int iw; /* image width */ int j; /* loop index */ int l; /* lo byte */ int lcsf; /* local color sort flag */ int lctp; /* local color table present */ int lcts; /* local color table size */ int lsh; /* logical screen height */ int lsw; /* logical screen width */ int mcs; /* minimum code size */ int resv; /* reserved value */ int tbci; /* text background color index */ int tfci; /* text foreground color index */ int tgh; /* text grid height */ int tgw; /* text grid width */ int tglp; /* text grid left position */ int tgtp; /* text grid top position */ int trcf; /* transparent color flag */ int trci; /* transparent color index */ int uinf; /* user input flag */ int vers; /* version number, 87 or 89 */ trcf = 0; trci = 0; eof = 0; pos = 0; infp = stdin; /********\ | header | \********/ printf("%06lu:",pos); for ( i = 0 ; i < 6 ; ++ i ) { l = getc( infp ); if ( l == EOF ) { eof = 1; break; } printf(" %02x",l); buffer[ i ] = l; } pos += 6; printf("\n"); if ( eof ) { printf("EOF in GIF header, got %d byte%s\n",i,i==1?"":"s"); return ""; } if ( buffer[0] != 'G' || buffer[1] != 'I' || buffer[2] != 'F' ) { printf("Did not find \"GIF\" at beginning of file\n"); return ""; } if ( buffer[3] == '8' && buffer[4] == '7' && buffer[5] == 'a' ) { printf("GIF version 87a\n\n"); vers = 87; } else if ( buffer[3] == '8' && buffer[4] == '9' && buffer[5] == 'a' ) { printf("GIF version 89a\n\n"); vers = 89; } else { printf("Unrecognized GIF version %c%c%c\n", buffer[3],buffer[4],buffer[5]); } /***************************\ | logical screen descriptor | \***************************/ /* screen width */ printf("%06lu:",pos); getword(lsw,"in LSD at width lo byte","in LSD at width hi byte"); pos += 2; printf("\nscreen width = %d\n\n",lsw); /* screen height */ printf("%06lu:",pos); getword(lsh,"in LSD at height lo byte","in LSD at height hi byte"); pos += 2; printf("\nscreen height = %d\n\n",lsh); /* packed fields */ printf("%06lu:",pos); getbytepr(l,"in LSD at packed fields"); ++ pos; gctp = ( l >> 7 ) & 1; cres = ( l >> 4 ) & 7; gcsf = ( l >> 3 ) & 1; gcts = l & 7; printf("\n%d....... global color table is %ssent\n", (l>>7)&1,gctp?"pre":"ab"); printf(".%d%d%d.... color resolution bits = %d+1 (%d values)\n", (l>>6)&1,(l>>5)&1,(l>>4)&1,cres,1<<(cres+1)); printf("....%d... global color table is%s sorted\n", (l>>3)&1,gcsf?" ":" not"); printf(".....%d%d%d global color table size = 3*2^(%d+1)" " (%d entries, %d bytes)\n\n", (l>>2)&1,(l>>1)&1,l&1,gcts,1<<(gcts+1),3<<(gcts+1)); /* background color */ printf("%06lu:",pos); getbytepr(bgci,"in LSD at background color index"); ++ pos; printf("\nbackground color index = %d\n",bgci); if ( bgci > ( 1 << ( gcts + 1 ) ) ) { printf(" exceeds size of global color table (%d)\n",1<<(gcts+1)); } printf("\n"); /* aspect ratio */ printf("%06lu:",pos); getbytepr(arat,"in LSD at aspect ratio"); ++ pos; if ( arat ) { double farat; farat = (double) arat; farat += 15.0; farat /= 64.0; printf("\naspect ratio factor = %d (ratio is %10.6f)\n\n",arat,farat); } else { printf("\naspect ratio factor = 0 (no ratio information)\n\n"); } /********************\ | global color table | \********************/ if ( gctp ) { printf("global color table detail: (* indicates background color)\n"); h = 1 << ( gcts + 1 ); for ( i = 0 ; i < h ; ++ i ) { if ( i % 4 == 0 ) { if ( i ) printf("\n"); printf("%06lu:",pos); } else { printf(" "); } for ( j = 0 ; j < 3 ; ++ j ) { if ( ( l = getc( infp ) ) == EOF ) { printf("EOF\nunexpected EOF in LSD global color table," " index %d %s\n",i,cnames[j]); return ""; } printf(" %02x",l); } printf("%c(%03d)",i==bgci?'*':' ',i); pos += 3; } printf("\n\n"); } /**********************\ | various other blocks | \**********************/ for (;;) { int data; int out; data = 0; out = 0; printf("%06lu:",pos); getbytepr(blkid,"between data blocks (terminator missing)"); ++ pos; switch ( blkid ) { case 0x21: /* extension introducer */ printf("\nextension introducer:\n\n"); /**************************\ | various extension blocks | \**************************/ printf("%06lu:",pos); getbytepr(extid,"at extension ID"); ++ pos; getbytepr(bsize,"at extension block size"); ++ pos; switch ( extid ) { case 0x01: /* plain text extension */ printf("\nplain text extension, block size = %d ",bsize); if ( bsize < 12 ) { printf("(too small, should be %d, skipping block)\n\n",12); break; } else if ( bsize > 12 ) { printf("(too large, should be %d, will skip excess)\n\n",12); } else { printf("(valid size)\n\n"); } /* text grid left pos */ getword(tglp,"in PTE at TGLP lo byte","in PTE at TGLP hi byte"); pos += 2; bsize -= 2; printf("\ntext grid left position = %d\n\n",tglp); getword(tgtp,"in PTE at TGTP lo byte","in PTE at TGTP hi byte"); pos += 2; bsize -= 2; printf("\ntext grid top position = %d\n\n",tgtp); getword(tgw,"in PTE at TGW lo byte","in PTE at TGW hi byte"); pos += 2; bsize -= 2; printf("\ntext grid width = %d\n\n",tgw); getword(tgh,"in PTE at TGH lo byte","in PTE at TGH hi byte"); pos += 2; bsize -= 2; printf("\ntext grid height = %d\n\n",tgh); getbyte(ccw,"in PTE at CCW"); ++ pos; -- bsize; printf("\ncharacter cell width = %d\n\n",ccw); getbyte(cch,"in PTE at CCH"); ++ pos; -- bsize; printf("\ncharacter cell height = %d\n\n",cch); getbyte(tfci,"in PTE at TFCI"); ++ pos; -- bsize; printf("\ntext foreground color index = %d\n\n",tfci); getbyte(tbci,"in PTE at TBCI"); ++ pos; -- bsize; printf("\ntext background color index = %d\n\n",tbci); data = 1; break; case 0xf9: /* graphic control extension */ printf("\ngraphics control extension, block size = %d ",bsize); if ( bsize < 4 ) { printf("(too small, should be %d, skipping block)\n\n",4); break; } else if ( bsize > 4 ) { printf("(too large, should be %d, will skip excess)\n\n",4); } else { printf("(valid size)\n\n"); } /* packed fields */ printf("%06lu:",pos); getbytepr(l,"in GCE at packed fields"); ++ pos; -- bsize; resv = ( 1 >> 5 ) & 7; disp = ( l >> 2 ) & 7; uinf = ( l >> 1 ) & 1; trcf = l & 1; printf("\n%d%d%d..... reserved value = %d\n", (l>>7)&1,(l>>6)&1,(l>>5)&1,resv); printf("...%d%d%d.. disposal method = %d (", (l>>4)&1,(l>>3)&1,(l>>2)&1,disp); switch ( disp ) { case 0: printf("no disposal specified, no action required)\n"); break; case 1: printf("do not dispose, leave graphic in place)\n"); break; case 2: printf("restore to background color)\n"); break; case 3: printf("restore to previous contents)\n"); break; default: printf("undefined disposal method)\n"); break; } printf("......%d. user input flag = %s\n", (l>>1)&1,uinf?"yes":"no"); printf(".......%d transparent color flag = %ssent\n\n", l&1,trcf?"pre":"ab"); /* delay time */ printf("%06lu:",pos); getword(delay,"in GCE at delay lo byte","in GCE at delay hi byte"); pos += 2; bsize -= 2; printf("\ndelay time = %d (%d.%02d secs)\n\n",delay, delay/100,delay%100); /* transparent color index */ printf("%06lu:",pos); getbytepr(trci,"in GCE at transparent color index"); ++ pos; -- bsize; printf("\ntransparent color index = %d\n\n",trci); data = 1; break; case 0xfe: /* comment extension */ for (;;) { printf("\ncomment extension, block size = %d\n",bsize); if ( bsize == 0 ) break; h = 0; while ( bsize -- ) { if ( h % 16 == 0 ) printf("%06lu: ",pos); getbyte(l,"in comment data"); prch( l ); ++ pos; ++ h; if ( h % 16 == 0 ) printf("\n"); } getbyte(bsize,"in comment size"); ++ pos; } printf("\n"); data = 0; break; case 0xff: /* application extension */ printf("\napplication extension block, size= %d ",bsize); if ( bsize < 11 ) { printf("(too small, should be %d, skipping block)\n\n",11); break; } else if ( bsize > 11 ) { printf("(too large, should be %d, will skip excess\n\n",11); } else { printf("(valid size)\n\n"); } printf("%06lu:",pos); for ( i = 0 ; i < 8 ; ++ i ) { getbytepr(l,"in AEB at application identifier"); buffer[ i ] = l; } printf("\napplication identifier = \""); for ( i = 0 ; i < 8 ; ++ i ) { prch( buffer[ i ] ); } printf("\"\n\n"); pos += 8; bsize -= 8; printf("%06lu:",pos); for ( i = 0 ; i < 3 ; ++ i ) { getbytepr(l,"in AEB at application authentication code"); buffer[ i ] = l; } printf("\napplication authentication code = \""); for ( i = 0 ; i < 3 ; ++ i ) { prch( buffer[ i ] ); } printf("\"\n\n"); pos += 3; bsize -= 3; data = 1; break; default: /* unrecognized */ printf("\nunrecognized extension type: 0x%02x" " (unable to proceed with scan)\n", extid); out = 1; break; } /****************************************************\ | skip to end of extension block if anything remains | \****************************************************/ if ( bsize ) { printf("\nskipping %d bytes to end of extension block\n", bsize); h = 0; while ( bsize -- ) { if ( h % 32 == 0 ) printf("%06lu:",pos); getbytepr(l,"while skipping to end of extension block"); ++ pos; ++ h; } } /*******************************************************\ | skip additional data blocks until terminator is found | \*******************************************************/ if ( data ) for (;;) { printf("%06lu:",pos); getbytepr(bsize,"at data block size"); ++ pos; if ( bsize == 0 ) { printf("\nempty data block terminator\n\n"); break; } printf("\ndata block size = %d\n",bsize); h = 0; while ( bsize -- ) { if ( h % 16 == 0 ) printf("%06lu:",pos); getbytepr(l,"in data block"); ++ pos; ++ h; if ( h % 16 == 0 ) printf("\n"); } printf("\n\n"); } break; case 0x2c: /* image separator */ printf("\nimage separator:\n\n"); /* image left position */ printf("%06lu:",pos); getword(ilp,"in ID at ILP lo byte","in ID at ILP hi byte"); printf("\nimage left position = %d\n\n",ilp); pos += 2; /* image top position */ printf("%06lu:",pos); getword(itp,"in ID at ITP lo byte","in ID at ITP hi byte"); printf("\nimage top position = %d\n\n",itp); pos += 2; /* image width */ printf("%06lu:",pos); getword(iw,"in ID at IW lo byte","in ID at IW hi byte"); printf("\nimage width = %d\n\n",iw); pos += 2; /* image height */ printf("%06lu:",pos); getword(ih,"in ID at IH lo byte","in ID at IH hi byte"); printf("\nimage height = %d\n",ih); pos += 2; /* check image against screen */ if ( ilp + iw > lsw ) { printf("this image is off the right of the screen\n"); } if ( itp + ih > lsh ) { printf("this image is off the bottom of the screen\n"); } printf("\n"); /* packed fields */ printf("%06lu:",pos); getbytepr(l,"in ID at packed fields"); ++ pos; lctp = ( l >> 7 ) & 1; intf = ( l >> 6 ) & 1; lcsf = ( l >> 5 ) & 1; resv = ( l >> 3 ) & 3; lcts = l & 7; printf("\n%d....... local color table is %ssent\n", (l>>7)&1,lctp?"pre":"ab"); printf(".%d...... image is%s interlaced\n", (l>>6)&1,intf?" ":" not"); printf("..%d..... local color table is%s sorted\n", (l>>5)&1,lcsf?" ":" not"); printf(".....%d%d%d local color table size = 3*2^(%d+1)" " (%d entries, %d bytes)\n\n", (l>>2)&1,(l>>1)&1,l&1,lcts,1<<(lcts+1),3<<(lcts+1)); /*******************\ | local color table | \*******************/ if ( lctp ) { if ( trcf && trci == bgci ) { printf("local color table detail: (# indicates BG and TRANS color)\n"); } else { printf("local color table detail: (* indicates background color)\n"); if ( trcf ) { printf(" (+ indicates transparent color)\n"); } } h = 1 << ( lcts + 1 ); for ( i = 0 ; i < h ; ++ i ) { if ( i % 4 == 0 ) { if ( i ) printf("\n"); printf("%06lu:",pos); } else { printf(" "); } for ( j = 0 ; j < 3 ; ++ j ) { if ( ( l = getc( infp ) ) == EOF ) { printf("EOF\nunexpected EOF in ID local color table," " index %d %s\n",i,cnames[j]); return ""; } printf(" %02x",l); } if ( trcf && trci == bgci ) putc(i==trci?'#':' ',stdout); else putc(i==trci?'+':(i==bgci?'*':' '),stdout); printf("(%03d)",i); pos += 3; } printf("\n\n"); } trcf = 0; /* invalidate the translate for another image */ /* image data min code size */ printf("%06lu:",pos); getbytepr(mcs,"after ID (and LCT) at LZW min code size"); ++ pos; printf("\nLZW minimum code size = %d",mcs); for (;;) { printf("\n\n%06lu:",pos); getbytepr(bsize,"at data block size"); ++ pos; printf("\nimage data block size = %d",bsize); if ( bsize == 0 ) break; h = 0; while ( bsize -- ) { if ( h % 20 == 0 ) printf("\n%06lu:",pos); getbytepr(l,"in data block"); ++ pos; ++ h; } } printf("\n\n"); break; case 0x3b: /* trailer */ printf("\ntrailer - end of GIF file\n"); /* we want an EOF here */ h = 0; while ( ( l = getc( infp ) ) != EOF ) { if ( h % 320 == 0 ) printf("\nGIF ended, Expecting EOF:\n"); if ( h % 20 == 0 ) printf("%06lu:",pos); printf(" %02x",l); ++ h; if ( h % 20 == 0 ) printf("\n"); } out = 1; break; default: /* unrecognized */ printf("\nunrecognized block type: 0x%02x" " (unable to proceed with scan)\n", blkid); out = 1; break; } if ( out ) break; } return 0; } /***********************************************************************\ | main | \***********************************************************************/ int main ( int argc , char * * argv ) { const char * msg; msg = gifscan( argc , argv ); if ( ! msg ) return 0; if ( ! msg[0] ) return 1; if ( msg[0] == '.' ) { printf( " EOF\nunexpected end of file encountered %s\n" , msg+1 ); } else { printf( "%s\n" , msg ); } return 1; }