home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / graphics / gifbla20.zip / SOURCE / GIFBLAST.C < prev    next >
C/C++ Source or Header  |  1992-12-15  |  20KB  |  696 lines

  1.  
  2. /* gifblast.c - Special purpose GIF compressor, main program. */
  3.  
  4. #include <ctype.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7.  
  8. #include "ubasic.h"
  9. #include "uffile.h"
  10. #include "gifcode.h"
  11. #include "arith.h"
  12. #include "arithmod.h"
  13. #include "gbcon.h"
  14. #include "gb11code.h"
  15. #include "gb20code.h"
  16.  
  17. #define MAX_IM_WIDTH 3072
  18. #define MAX_IM_HEIGHT 3072
  19.  
  20. static char *usage_message1[] = {
  21.     "\n",
  22.     "/| I s a a c |/\n",
  23.     "/|Dimitrovsky|/ Presents GIFBLAST, a lossless GIF file compressor.\n",
  24.     "/|  L a b s  |/ Generic C Version 2.0, Copyright (C) 1992 Isaac Dimitrovsky.\n",
  25.     "GIFBLAST is a compressor designed especially for GIF files. The usual file\n",
  26.     "compressors (PKZIP, ARJ, ZOO, etc.) do not work on GIF files; GIFBLAST does.\n",
  27.     "When you run GIFBLAST X.GIF, you get a compressed file X.GFB that is usually\n",
  28.     "20-25% smaller. In order to view this file you have to run GIFBLAST -D X.GFB\n",
  29.     "to decompress X.GFB and get back the original X.GIF file. For convenience\n",
  30.     "you can omit the .GIF and .GFB suffixes and give several files.\n",
  31.     "\n",
  32.     "GIFBLAST is recommended for applications such as storing GIF files on BBS's\n",
  33.     "and posting GIF files to usenet. Because GIFBLAST perfectly preserves all\n",
  34.     "images and other information in GIF files, the user need not be concerned\n",
  35.     "with harming the images or introducing compression artifacts.\n",
  36.     "\n",
  37.     "This version of the GIFBLAST source is free and may be copied, distributed,\n",
  38.     "and uploaded to bulletin boards.\n",
  39.     "Version 3.0 of GIFBLAST will be available on March 31, 1993.\n",
  40.     "It will work faster and compress better than the current version.\n",
  41.     "To order, make a $20 check payable to Isaac Dimitrovsky Labs and send it to:\n",
  42.     "    Isaac Dimitrovsky Labs, 147 Second Ave #484, New York NY 10003\n",
  43.     "Be sure to include a full return address, and specify 3.5 or 5 inch disks.\n",
  44.     "Users who ordered version 2.0 will get version 3.0 free when it is ready.\n",
  45.     NULL
  46. };
  47. static char *more_message1 =
  48.     "      --(press return for a full list of command line options)--"
  49. ;
  50. static char *usage_message2[] = {
  51.     "\n",
  52.     "The full GIFBLAST command line is as follows:\n",
  53.     "    GIFBLAST [<option>] [<additional options>] file1 [file2 ... ]\n",
  54.     "With no option given this compresses the given GIF files, producing\n",
  55.     "a corresponding set of GFB files. The possible options are:\n",
  56.     "    -D      Decompresses GFB files, producing GIF files.\n",
  57.     "    -1.1    Compresses GIF files, producing GFB files in version 1.1 format.\n",
  58.     "    -C1.1   Converts GFB files from version 2.0 format to version 1.1 format.\n",
  59.     "            The old files are saved with the .BAK suffix.\n",
  60.     "    -C2.0   Converts GFB files from version 1.1 format to version 2.0 format.\n",
  61.     "            The old files are saved with the .BAK suffix.\n",
  62.     "The possible additional options are:\n",
  63.     "    -LOG logfile  Appends a one-line report on each file to logfile.\n"
  64.     "    -R      Removes the old file when it's done (careful with this one).\n",
  65.     "            Will not remove the old file if any problem was detected.\n",
  66.     "For convenience you can omit the .GIF and .GFB suffixes in the list of files.\n",
  67.     "\n",
  68.     "You may notice that a compressed and decompressed GIF file is not always\n",
  69.     "byte-for-byte identical to the original GIF file. This is nothing to worry\n",
  70.     "about, and the GIF images are not changed at all. The GIF standard allows\n",
  71.     "some variation in how images are encoded, so an identical image can be\n",
  72.     "encoded as two different GIF files. This is what is happening in this case.\n",
  73.     "If you want to confirm for yourself that the GIF images are not affected,\n",
  74.     "convert both files to an uncompressed format such as TIFF and compare them.\n",
  75.     NULL
  76. };
  77. static char *more_message2 =
  78.     "                --(press return for legal matters)--"
  79. ;
  80. static char *usage_message3[] = {
  81.     "\n",
  82.     "Legal Matters:\n",
  83.     "\n",
  84.     "This software is provided \"as is\" without any warranty express or implied,\n",
  85.     "including but not limited to implied warranties of merchantability and\n",
  86.     "fitness for a particular purpose.\n",
  87.     "\n",
  88.     "The Graphics Interchange Format(c) is the Copyright property of CompuServe\n",
  89.     "Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated.\n",
  90.     NULL
  91. };
  92. static char *header_message[] = {
  93.     "GIFBLAST Generic C Version 2.0, Copyright (C) 1992 Isaac Dimitrovsky.\n",
  94.     "Type GIFBLAST -H for instructions.\n",
  95.     NULL
  96. };
  97. static int usage=FALSE;
  98. static int v11_opt=FALSE;
  99. static int decompress_opt=FALSE;
  100. static int conv11_opt=FALSE;
  101. static int conv20_opt=FALSE;
  102. static char *logfname=NULL;
  103. static int remove_opt=FALSE;
  104. static OPTION opts[] = {
  105.     {"-usage",SWITCHONOPT,&usage},
  106.     {"-help",SWITCHONOPT,&usage},
  107.     {"/help",SWITCHONOPT,&usage},
  108.     {"-HELP",SWITCHONOPT,&usage},
  109.     {"/HELP",SWITCHONOPT,&usage},
  110.     {"-h",SWITCHONOPT,&usage},
  111.     {"/h",SWITCHONOPT,&usage},
  112.     {"-H",SWITCHONOPT,&usage},
  113.     {"/H",SWITCHONOPT,&usage},
  114.     {"-?",SWITCHONOPT,&usage},
  115.     {"/?",SWITCHONOPT,&usage},
  116.     {"-1.1",SWITCHONOPT,&v11_opt},
  117.     {"/1.1",SWITCHONOPT,&v11_opt},
  118.     {"-d",SWITCHONOPT,&decompress_opt},
  119.     {"/d",SWITCHONOPT,&decompress_opt},
  120.     {"-D",SWITCHONOPT,&decompress_opt},
  121.     {"/D",SWITCHONOPT,&decompress_opt},
  122.     {"-c1.1",SWITCHONOPT,&conv11_opt},
  123.     {"/c1.1",SWITCHONOPT,&conv11_opt},
  124.     {"-C1.1",SWITCHONOPT,&conv11_opt},
  125.     {"/C1.1",SWITCHONOPT,&conv11_opt},
  126.     {"-c2.0",SWITCHONOPT,&conv20_opt},
  127.     {"/c2.0",SWITCHONOPT,&conv20_opt},
  128.     {"-C2.0",SWITCHONOPT,&conv20_opt},
  129.     {"/C2.0",SWITCHONOPT,&conv20_opt},
  130.     {"-log",STRARGOPT,&logfname},
  131.     {"/log",STRARGOPT,&logfname},
  132.     {"-LOG",STRARGOPT,&logfname},
  133.     {"/LOG",STRARGOPT,&logfname},
  134.     {"-r",SWITCHONOPT,&remove_opt},
  135.     {"/r",SWITCHONOPT,&remove_opt},
  136.     {"-R",SWITCHONOPT,&remove_opt},
  137.     {"/R",SWITCHONOPT,&remove_opt},
  138.     {NULL}
  139. };
  140.  
  141. static unsigned char buf[MAX_IM_WIDTH];
  142. static char msg[2048];
  143. static int in_image;
  144. static GIF_CODER *gc;
  145. static GB11_CODER *gb11;
  146. static GB20_CODER *gb20;
  147. enum {TY_GIF,TY_V11,TY_V20};
  148. int intype,outtype;
  149.  
  150. static int
  151. has_suff(str,ploc)
  152. char *str; int *ploc;
  153. {
  154.     int res,i;
  155.  
  156.     res = FALSE;
  157.     for (i=strlen(str)-1; i>=0; i--) {
  158.         res = (str[i] == '.');
  159.         if (res || str[i]=='\\' || str[i]=='/' || str[i]==':')
  160.             break;
  161.     }
  162.     (*ploc) = i;
  163.     return res;
  164. }
  165.  
  166. static int
  167. is_lowercase_fname(fname)
  168. char *fname;
  169. {
  170.     for (; *fname!='\0'; fname++) {
  171.         if (isascii(*fname) && islower(*fname))
  172.             return TRUE;
  173.     }
  174.     return FALSE;
  175. }
  176.  
  177. static void
  178. uppercase_suff(arg,suff)
  179. char *arg; char *suff;
  180. {
  181.     if (!is_lowercase_fname(arg)) {
  182.         for (; (*suff)!='\0'; suff++)
  183.             if (isascii(*suff) && islower(*suff))
  184.                 (*suff) = toupper(*suff);
  185.     }
  186. }
  187.  
  188. static void
  189. add_suffs(argc,argv)
  190. int argc; char **argv;
  191. {
  192.     int i,j,arglen;
  193.     char *newarg;
  194.  
  195.     for (i=1; i<argc; i++) {
  196.         arglen = strlen(argv[i]);
  197.         if ((newarg=basic_alloc(arglen+5)) == NULL)
  198.             uhalt(("out of memory while parsing command line arguments"));
  199.         strcpy(newarg,argv[i]);
  200.         if (!has_suff(newarg,&j)) {
  201.             strcpy(newarg+arglen,
  202.                 ((decompress_opt||conv11_opt||conv20_opt) ? ".gfb" : ".gif"));
  203.             uppercase_suff(argv[i],newarg+arglen);
  204.         }
  205.         basic_free(argv[i]);
  206.         argv[i] = newarg;
  207.     }
  208. }
  209.  
  210. static char *
  211. change_suff(arg,newsuff)
  212. char *arg; char *newsuff;
  213. {
  214.     int i;
  215.     char *newarg;
  216.  
  217.     if (!has_suff(arg,&i))
  218.         uhalt(("impossible command line argument %s",arg));
  219.     if ((newarg=basic_alloc(strlen(arg)+5)) == NULL)
  220.         return NULL;
  221.     strcpy(newarg,arg);
  222.     strcpy(newarg+i,newsuff);
  223.     uppercase_suff(arg,newarg+i);
  224.     return newarg;
  225. }
  226.  
  227. static char *
  228. change_to_backup_suff(arg)
  229. char *arg;
  230. {
  231.     return change_suff(arg,".bak");
  232. }
  233.  
  234. static char *
  235. change_to_out_suff(arg)
  236. char *arg;
  237. {
  238.     return change_suff(arg,(decompress_opt ? ".gif" : ".gfb"));
  239. }
  240.  
  241. static int
  242. init_files(inff,outff)
  243. FFILE *inff; FFILE *outff;
  244. {
  245.     in_image = FALSE;
  246.     if ((intype==TY_V11 && gb11_start_decoding(gb11,inff)<0)
  247.         || (intype==TY_V20 && gb20_start_decoding(gb20,inff)<0)
  248.         || (outtype==TY_V11 && gb11_start_encoding(gb11,outff)<0)
  249.         || (outtype==TY_V20 && gb20_start_encoding(gb20,outff)<0))
  250.         return -1;
  251.     return 0;
  252. }
  253.  
  254. static int
  255. end_files()
  256. {
  257.     if ((intype==TY_V11 && gb11_end_decoding(gb11)<0)
  258.         || (intype==TY_V20 && gb20_end_decoding(gb20)<0)
  259.         || (outtype==TY_V11 && gb11_end_encoding(gb11)<0)
  260.         || (outtype==TY_V20 && gb20_end_encoding(gb20)<0))
  261.         return -1;
  262.     return 0;
  263. }
  264.  
  265. static void
  266. init_image(inff,outff,datasize,im_bpp)
  267. FFILE *inff; FFILE *outff; int datasize; int im_bpp;
  268. {
  269.     in_image = TRUE;
  270.     if (intype == TY_GIF)
  271.         gif_start_decoding(gc,inff,datasize);
  272.     if (outtype == TY_GIF)
  273.         gif_start_encoding(gc,outff,datasize);
  274.     if (intype==TY_V20 || outtype==TY_V20)
  275.         gb20_init_image(gb20,im_bpp);
  276. }
  277.  
  278. static int
  279. end_image()
  280. {
  281.     in_image = FALSE;
  282.     if ((intype==TY_GIF && gif_end_decoding(gc)<0)
  283.         || (outtype==TY_GIF && gif_end_encoding(gc)<0))
  284.         return -1;
  285.     if (intype==TY_V20 || outtype==TY_V20)
  286.         gb20_end_image(gb20);
  287.     return 0;
  288. }
  289.  
  290. static int
  291. inff_getc(inff)
  292. FFILE *inff;
  293. {
  294.     if (intype==TY_GIF && in_image)
  295.         return gif_decode_c(gc);
  296.     else if (intype == TY_GIF)
  297.         return ff_getc(inff);
  298.     else if (intype == TY_V20)
  299.         return gb20_decode_c(gb20);
  300.     else
  301.         return gb11_decode_c(gb11);
  302. }
  303.  
  304. static size_t
  305. inff_read(buf,nbytes,inff)
  306. unsigned char *buf; size_t nbytes; FFILE *inff;
  307. {
  308.     register size_t i;
  309.     int c;
  310.  
  311.     if (intype==TY_GIF && in_image) {
  312.         for (i=0; i<nbytes; i++) {
  313.             if ((c=gif_decode_c(gc)) < 0)
  314.                 break;
  315.             buf[i] = c;
  316.         }
  317.     } else if (intype == TY_GIF)
  318.         i = ff_read(buf,nbytes,inff);
  319.     else if (intype == TY_V20) {
  320.         for (i=0; i<nbytes; i++) {
  321.             if ((c=gb20_decode_c(gb20)) < 0)
  322.                 break;
  323.             buf[i] = c;
  324.         }
  325.     } else {
  326.         for (i=0; i<nbytes; i++) {
  327.             if ((c=gb11_decode_c(gb11)) < 0)
  328.                 break;
  329.             buf[i] = c;
  330.         }
  331.     }
  332.     return i;
  333. }
  334.  
  335. static int
  336. outff_putc(c,outff)
  337. int c; FFILE *outff;
  338. {
  339.     if (outtype == TY_V20)
  340.         return gb20_encode_c(c,gb20);
  341.     else if (outtype == TY_V11)
  342.         return gb11_encode_c(c,gb11);
  343.     else if (in_image)
  344.         return gif_encode_c(c,gc);
  345.     else
  346.         return ff_putc(c,outff);
  347. }
  348.  
  349. static size_t
  350. outff_write(buf,nbytes,outff)
  351. unsigned char *buf; size_t nbytes; FFILE *outff;
  352. {
  353.     register size_t i;
  354.  
  355.     if (outtype == TY_V20) {
  356.         for (i=0; i<nbytes; i++)
  357.             if (gb20_encode_c(buf[i],gb20) < 0)
  358.                 break;
  359.     } else if (outtype == TY_V11) {
  360.         for (i=0; i<nbytes; i++)
  361.             if (gb11_encode_c(buf[i],gb11) < 0)
  362.                 break;
  363.     } else if (in_image) {
  364.         for (i=0; i<nbytes; i++)
  365.             if (gif_encode_c(buf[i],gc) < 0)
  366.                 break;
  367.     } else
  368.         i = ff_write(buf,nbytes,outff);
  369.     return i;
  370. }
  371.  
  372. static int
  373. copyfile(inff,outff)
  374. FFILE *inff; FFILE *outff;
  375. {
  376.     int c;
  377.  
  378.     while ((c=ff_getc(inff)) != EOF)
  379.         if (ff_putc(c,outff) == EOF)
  380.             return FALSE;
  381.     return TRUE;
  382. }
  383.  
  384. static void
  385. prlog(message)
  386. char *message;
  387. {
  388.     FILE *logf;
  389.  
  390.     if (logfname==NULL || (logf=fopen(logfname,"a"))==NULL)
  391.         return;
  392.     fprintf(logf,"%s",message);
  393.     fclose(logf);
  394. }
  395.  
  396. static void
  397. gifblast(infname)
  398. char *infname;
  399. {
  400. #define fail(args) {sprintf args ; failed=TRUE; goto endlabel;}
  401.     FFILE *inff,*testff,*outff;
  402.     char *outfname,*backfname,*orig_infname;
  403.     int g_bpp,g_colmapsize;
  404.     int im_no,im_width,im_height,im_bpp,im_colmapsize,im_datasize,im_v;
  405.     int c,extcode,size,pct_done,pct_saved,failed;
  406.     long im_totbytes;
  407.  
  408.     sprintf(msg,"file %s:",infname);
  409.     printf("%s\n",msg);
  410.     prlog(msg);
  411.     failed = FALSE;
  412.     inff = outff = NULL;
  413.     outfname = backfname = NULL;
  414.     if (conv11_opt || conv20_opt) {
  415.         if ((backfname=change_to_backup_suff(infname)) == NULL)
  416.             fail((msg,"out of memory while copying filename\n"));
  417.         if ((testff=ff_open(backfname,FF_READ)) != NULL) {
  418.             ff_close(testff);
  419.             fail((msg,"will not overwrite existing backup file %s\n",
  420.                 backfname));
  421.         }
  422.         if (rename(infname,backfname) < 0)
  423.             fail((msg,"unable to rename file to %s\n",backfname));
  424.         orig_infname = infname;
  425.         infname = backfname;
  426.     }
  427.     if ((inff=ff_open(infname,FF_READ)) == NULL)
  428.         fail((msg,"unable to open file\n"));
  429.     if ((outfname=change_to_out_suff(infname)) == NULL)
  430.         fail((msg,"out of memory while copying filename\n"));
  431.     if ((testff=ff_open(outfname,FF_READ)) != NULL) {
  432.         ff_close(testff);
  433.         fail((msg,"will not overwrite existing output file %s\n",outfname));
  434.     }
  435.     if (ff_read(buf,6,inff) != 6)
  436.         fail((msg,"incomplete header\n"));
  437.     if (strncmp(buf,"GIF",3) == 0) {
  438.         if (decompress_opt) {
  439.             if ((outff=ff_open(outfname,FF_WRITE)) == NULL)
  440.                 fail((msg,"unable to open output file %s\n",outfname));
  441.             if (ff_write(buf,6,outff) != 6)
  442.                 fail((msg,"unable to write header\n"));
  443.             if (!copyfile(inff,outff))
  444.                 fail((msg,"unable to write output file\n"));
  445.             sprintf(msg,"completed\n");
  446.             printf("\t%s",msg);
  447.             prlog(" ");
  448.             prlog(msg);
  449.             goto endlabel;
  450.         }
  451.         intype = TY_GIF;
  452.     } else if (strncmp(buf,"GB",2) != 0) {
  453.         fail((msg,"invalid header\n"));
  454.     } else if ((buf[2]==0x11 && conv11_opt) || (buf[2]==0x20 && conv20_opt)) {
  455.         ff_close(inff);
  456.         inff = NULL;
  457.         fail((msg,"file is already version %s format%s\n",
  458.             (conv11_opt ? "1.1" : "2.0"),
  459.             (rename(infname,orig_infname)<0
  460.                 ? " (unable to restore name)" : "")));
  461.     } else if (buf[2]!=0x11 && buf[2]!=0x20) {
  462.         fail((msg,"unable to decompress this code version (%d.%d)\n",
  463.             buf[2]>>4,buf[2]&0xF));
  464.     } else if (decompress_opt || conv11_opt || conv20_opt)
  465.         intype = (buf[2]==0x11 ? TY_V11 : TY_V20);
  466.     else
  467.         fail((msg,"file appears to be compressed already\n"));
  468.     if (decompress_opt) {
  469.         buf[1] = 'I';
  470.         buf[2] = 'F';
  471.         outtype = TY_GIF;
  472.     } else if (v11_opt || conv11_opt) {
  473.         buf[1] = 'B';
  474.         buf[2] = 0x11;
  475.         outtype = TY_V11;
  476.     } else {
  477.         buf[1] = 'B';
  478.         buf[2] = 0x20;
  479.         outtype = TY_V20;
  480.     }
  481.     if ((outff=ff_open(outfname,FF_WRITE)) == NULL)
  482.         fail((msg,"unable to open output file %s\n",outfname));
  483.     if (ff_write(buf,6,outff) != 6)
  484.         fail((msg,"unable to write header\n"));
  485.     if (init_files(inff,outff) < 0)
  486.         fail((msg,"unable to initialize coder\n"));
  487.     if (inff_read(buf,7,inff) != 7)
  488.         fail((msg,"unable to read screen descriptor\n"));
  489.     if (outff_write(buf,7,outff) != 7)
  490.         fail((msg,"unable to write screen descriptor\n"));
  491.     g_bpp = (buf[4]&0x7)+1;
  492.     if ((buf[4]&0x80) != 0) {
  493.         g_colmapsize = 3*(1<<g_bpp);
  494.         if (inff_read(buf,g_colmapsize,inff) != g_colmapsize)
  495.             fail((msg,"unable to read global color map\n"));
  496.         if (outff_write(buf,g_colmapsize,outff) != g_colmapsize)
  497.             fail((msg,"unable to write global color map\n"));
  498.     }
  499.     im_no = 0;
  500.     do {
  501.         if ((c=inff_getc(inff)) < 0)
  502.             fail((msg,"unexpected end of file\n"));
  503.         if (outff_putc(c,outff) < 0)
  504.             fail((msg,"unable to write file\n"));
  505.         switch (c) {
  506.         case ',':
  507.             if (inff_read(buf,9,inff) != 9)
  508.                 fail((msg,"unable to read image descriptor\n"));
  509.             im_width = ((((int)buf[4])&0xFF) | ((((int)buf[5])&0xFF)<<8));
  510.             im_height = ((((int)buf[6])&0xFF) | ((((int)buf[7])&0xFF)<<8));
  511.             if (im_width<=0 || MAX_IM_WIDTH<im_width
  512.                 || im_height<=0 || MAX_IM_HEIGHT<im_height)
  513.                 fail((msg,"image size out of range (%dx%d)\n",
  514.                     im_width,im_height));
  515.             if ((buf[8]&0x80) != 0) {
  516.                 im_bpp = (buf[8]&0x7)+1;
  517.                 im_colmapsize = 3*(1<<im_bpp);
  518.             } else {
  519.                 im_bpp = g_bpp;
  520.                 im_colmapsize = 0;
  521.             }
  522.             printf("\tprocessing image %d, %dx%d, %d bpp: ",
  523.                 ++im_no,im_width,im_height,im_bpp);
  524.             if (im_colmapsize>0
  525.                 && inff_read(buf+9,im_colmapsize,inff)!=im_colmapsize) {
  526.                 printf("\n\t");
  527.                 fail((msg,"unable to read image color map\n"));
  528.             }
  529.             if (outff_write(buf,im_colmapsize+9,outff) != im_colmapsize+9) {
  530.                 printf("\n\t");
  531.                 fail((msg,"unable to write image descriptor\n"));
  532.             }
  533.             im_datasize = inff_getc(inff);
  534.             if (im_datasize != (im_bpp==1 ? 2 : im_bpp)) {
  535.                 printf("\n\t");
  536.                 fail((msg,"invalid number of data bits in image (%d)\n",
  537.                     im_datasize));
  538.             }
  539.             if (outff_putc(im_datasize,outff) < 0) {
  540.                 printf("\n\t");
  541.                 fail((msg,"unable to write image data bits\n"));
  542.             }
  543.             init_image(inff,outff,im_datasize,im_bpp);
  544.             im_totbytes = 0L;
  545.             printf("     ");
  546.             for (im_v=0; im_v<im_height; im_v++) {
  547.                 if (inff_read(buf,im_width,inff) != im_width) {
  548.                     printf("\n\t");
  549.                     fail((msg,"error detected reading image pixels\n"));
  550.                 }
  551.                 if (outff_write(buf,im_width,outff) != im_width) {
  552.                     printf("\n\t");
  553.                     fail((msg,"unable to write image pixels\n"));
  554.                 }
  555.                 im_totbytes += im_width;
  556.                 if (im_v<im_height-1 && (im_v%4)!=0)
  557.                     continue;
  558.                 pct_done =
  559.                     (int)((im_totbytes*100L)/(im_height*(long)im_width));
  560.                 printf("\b\b\b\b\b%c%c%c%% ",(pct_done==100?'1':' '),
  561.                     '0'+((pct_done/10)%10),'0'+(pct_done%10));
  562.                 fflush(stdout);
  563.             }
  564.             putchar('\n');
  565.             if (end_image() < 0)
  566.                 fail((msg,"error detected at end of image pixels\n"));
  567.             break;
  568.         case ';':
  569.             break;
  570.         case '!':
  571.             if ((extcode=inff_getc(inff)) < 0)
  572.                 fail((msg,"unexpected end of file\n"));
  573.             if (outff_putc(extcode,outff) < 0)
  574.                 fail((msg,"unable to write file\n"));
  575.             do {
  576.                 if ((size=inff_getc(inff)) < 0)
  577.                     fail((msg,"unexpected end of file\n"));
  578.                 buf[0] = size;
  579.                 if (size>0 && inff_read(buf+1,size,inff)!=size)
  580.                     fail((msg,"unable to read extension block\n"));
  581.                 if (outff_write(buf,size+1,outff) != size+1)
  582.                     fail((msg,"unable to write extension block\n"));
  583.             } while (size > 0);
  584.             break;
  585.         default:
  586.             fail((msg,"unexpected character\n"));
  587.         }
  588.     } while (c != ';');
  589.     if (end_files() < 0)
  590.         fail((msg,"error detected at end of file\n"));
  591.     if (copy_if_larger && intype==TY_GIF && ff_tell(outff)>ff_tell(inff)) {
  592.         ff_close(outff);
  593.         if ((outff=ff_open(outfname,FF_WRITE)) == NULL)
  594.             fail((msg,"unable to reset output file\n"));
  595.         ff_close(inff);
  596.         if ((inff=ff_open(infname,FF_READ)) == NULL)
  597.             fail((msg,"unable to reset input file\n"));
  598.         if (!copyfile(inff,outff))
  599.             fail((msg,"unable to rewrite output file\n"));
  600.     }
  601.     sprintf(msg,"completed, %d image%s",im_no,(im_no==1?"":"s"));
  602.     if (outtype != TY_GIF) {
  603.         pct_saved = (int)
  604.             (((ff_tell(inff)-ff_tell(outff))*100L)/ff_tell(inff));
  605.         sprintf(msg+strlen(msg)," (%d%% savings)",pct_saved);
  606.     }
  607.     strcat(msg,"\n");
  608.     printf("\t%s",msg);
  609.     prlog(" ");
  610.     prlog(msg);
  611. endlabel:
  612.     if (failed) {
  613.         printf("\t%s",msg);
  614.         prlog(" failed - ");
  615.         prlog(msg);
  616.     } else if (remove_opt)
  617.         unlink(infname);
  618.     if (inff != NULL)
  619.         ff_close(inff);
  620.     if (outff != NULL) {
  621.         ff_close(outff);
  622.         if (failed)
  623.             unlink(outfname);
  624.     }
  625.     if (outfname != NULL)
  626.         basic_free(outfname);
  627.     if (backfname != NULL)
  628.         basic_free(backfname);
  629. }
  630.  
  631. static void
  632. erase_str(char *message)
  633. {
  634. }
  635.  
  636. static int
  637. get_return_or_escape()
  638. {
  639.     int c;
  640.  
  641.     do {
  642.         c = getchar();
  643.     } while (c != '\n');
  644.     return c;
  645. }
  646.  
  647. main(argc,argv)
  648. int argc; char **argv;
  649. {
  650.     int c,i;
  651.  
  652.     process_command_line(&argc,&argv,opts);
  653.     if (usage || argc<=1) {
  654.         for (i=0; usage_message1[i]!=NULL; i++)
  655.             printf("%s",usage_message1[i]);
  656.         printf("%s",more_message1);
  657.         c = get_return_or_escape();
  658.         erase_str(more_message1);
  659.         if (c=='\r' || c=='\n') {
  660.             for (i=0; usage_message2[i]!=NULL; i++)
  661.                 printf("%s",usage_message2[i]);
  662.             printf("%s",more_message2);
  663.             c = get_return_or_escape();
  664.             erase_str(more_message2);
  665.             if (c=='\r' || c=='\n') {
  666.                 for (i=0; usage_message3[i]!=NULL; i++)
  667.                     printf("%s",usage_message3[i]);
  668.             }
  669.         }
  670.         uhalt((""));
  671.     }
  672.     if (v11_opt+decompress_opt+conv11_opt+conv20_opt > 1)
  673.         uhalt(("invalid command line - type GIFBLAST -H for instructions"));
  674.     for (i=0; header_message[i]!=NULL; i++)
  675.         printf("%s",header_message[i]);
  676.     add_suffs(argc,argv);
  677.     if ((gc=basic_alloc(sizeof(GIF_CODER)))==NULL
  678.         || (gb11=gb11_alloc_coder())==NULL || (gb20=gb20_alloc_coder())==NULL)
  679.         uhalt(("unable to allocate enough memory to start up"));
  680.     prlog(header_message[0]);
  681.     if (v11_opt)
  682.         sprintf(msg,"Compressing GIF files to version 1.1 format.\n");
  683.     else if (decompress_opt)
  684.         sprintf(msg,"Decompressing GFB files.\n");
  685.     else if (conv11_opt)
  686.         sprintf(msg,"Converting GFB files to version 1.1 format.\n");
  687.     else if (conv20_opt)
  688.         sprintf(msg,"Converting GFB files to version 2.0 format.\n");
  689.     else
  690.         sprintf(msg,"Compressing GIF files to version 2.0 format.\n");
  691.     printf("%s",msg);
  692.     prlog(msg);
  693.     for (i=1; i<argc; i++)
  694.         gifblast(argv[i]);
  695. }
  696.