home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / waffle / allfil62.zip / ALLFILES.C next >
C/C++ Source or Header  |  1993-03-03  |  18KB  |  690 lines

  1. /*
  2. **    Create a set of BBS directory files and summarys from the Waffle
  3. **    dirs file and the raw files according to a summary config
  4. **
  5. **    Produce extra information on some special file types (gif, jpg, bin)
  6. **
  7. **    Otto J. Makela <otto@jyu.fi>, Jyvaskyla, Finland, 1993
  8. **    BBS V.32bis/HST +358 41 211 562
  9. **    Distributed under the GNU General Public Licence:
  10. **    see file COPYING for details
  11. */
  12.  
  13.     /* Default dirs file name */
  14. #define    DIRSFILE    "/waffle/system/dirs"
  15.  
  16.     /* Default input and output filenames */
  17. #define    RAWINFO        "@files.raw"
  18. #define    INFO        "@files"
  19. #define    NOINFO        0
  20.  
  21.     /* Default formats */
  22. #define    MASTERFORMAT    "%-12f%6L%3d/%02m/%02y %T\n"
  23. #define    PLUSFORMAT    "              Size  Updated %t\n"
  24. #define    INFOFORMAT    "%f\t%T\n"
  25. #define    BEGIN        "List of %t files on SomeBBS, Somewhere\n" \
  26.             "%A, %2d %B %Y %2H:%M\n"
  27. #define    HEADER        "\n*** %f ***\n\n"
  28. #define    NAMEHEADER    "\n*** %f (%t) ***\n\n"
  29. #define    FOOTER        "\nTotal %L in %n files\n"
  30. #define    ENDER        "\nGrand total %L in %n file%P in %N directories\n" \
  31.             "*** End of %t files listing ***\n"
  32.  
  33.     /* Max number of simultaneous summary files */
  34. #define    MAXSUMMARY    8
  35.  
  36.     /* Max number of tokens per parsed file line */
  37. #define    MAXTOKENS    25
  38.  
  39.     /* Characters verboten in filenames (not including wildcards) */
  40. #define    FORBIDDEN    ",;:+\"/<=>[\\]|"
  41. #define    NONFILENAME(c)    (isspace(c) || strchr(FORBIDDEN,c))
  42.  
  43. #include        <time.h>
  44. #include        <stdio.h>
  45. #include        <ctype.h>
  46. #include    <stdarg.h>
  47. #include        <stdlib.h>
  48. #include        <string.h>
  49. #include        "formatch.h"
  50. #include        "wafcfg.h"
  51.  
  52.     /* God, how I hate ANSI C */
  53. #define    READ_TEXT    "rt"
  54. #define    READ_BINARY    "rb"
  55. #define    WRITE_TEXT    "wt"
  56. #define    WRITE_BINARY    "wb"
  57.  
  58.     /* Some almost-ANSI compilers don't have __DATE__ */
  59. #ifndef    __DATE__
  60. #define    __DATE__
  61. #endif
  62.  
  63. #ifndef    min
  64. #define    min(a,b)    ((a)<(b)?(a):(b))
  65. #endif
  66.  
  67. #define    ever        (;;)
  68. #define KILOS(a)        ( ((a)>>10) + (((a)&0x3FF)?1L:0L) )
  69.  
  70.     /* Function prototypes */
  71. int showfile(struct file_match *match);
  72. int find_filenames(char *dirfile,FILE *files,int skiplines);
  73. char *fileinfo(char *format,struct file_match *match);
  74. char *string(char *format,...);
  75. int fputs2(char *string);
  76. int specialinfo(char *string,char *filename,char *extension);
  77. int macbinfo(char *string,char *filename);
  78. int gifinfo(char *string,char *filename);
  79. int jfifinfo(char *string,char *filename);
  80.  
  81.     /* Some common variables */
  82. char info[16]=INFO,rawinfo[16]=RAWINFO,dirsfile[80]=DIRSFILE,
  83.     masterformat[80]=MASTERFORMAT,plusformat[80]=PLUSFORMAT,
  84.     infoformat[80]=INFOFORMAT,begin[BUFSIZ]=BEGIN,header[80]=HEADER,
  85.     nameheader[80]=NAMEHEADER,footer[80]=FOOTER,ender[BUFSIZ]=ENDER;
  86. unsigned int summarys=0,scanrom=0,zeros=0,noinfo=NOINFO;
  87. char *description;
  88. unsigned long size,files,number;
  89. FILE *fi,*fo;
  90.  
  91.     /* Summary file information array */
  92. struct    {
  93.            char dir[65];        /* Directory */
  94.            char name[24];        /* Listing name */
  95.            char doprint;        /* Printing or not ? */
  96.            unsigned int dirs;    /* How many directories in listing */
  97.            unsigned long size;    /* How many bytes in listing */
  98.            unsigned long files;    /* How many files in listing */
  99.            FILE *file;        /* Output FILE * */
  100. } summary[MAXSUMMARY];
  101.  
  102.  
  103.  
  104. int main(int argc,char *argv[])    {
  105.         register char *p,*q;
  106.     int i,rom,skip,column,errors=0;
  107.     char dirsline[BUFSIZ],line[BUFSIZ];
  108.     char *dir;
  109.     FILE *config;
  110.     struct file_match nonfile;
  111.  
  112.         fprintf(stderr,"%s 6.2 "__DATE__
  113.         " by Otto J. Makela (BBS V.32bis/HST +358 41 211 562)\n"
  114.         "Distributed under the GNU General Public Licence: "
  115.         "see file COPYING for details\n",*argv="allfiles");
  116.  
  117.         if(argc<2)    {
  118.                 fprintf(stderr,"usage: %s config_file [config_params...]\n",
  119.             *argv);
  120.                 exit(2);
  121.     }
  122.  
  123.         /* Open configuration file */
  124.     if(!(config=fopen(argv[1],READ_TEXT)))    {
  125.         fprintf(stderr,"%s: can't open configuration file \"%s\"\n",
  126.             *argv,argv[1]);
  127.         exit(1);
  128.     }
  129.  
  130.         /* Parse configuration file lines */
  131.     while(fgets(line,sizeof(line),config))    {
  132. parse:        if(!parse_line(line)) continue;
  133.  
  134.         if(p=token_value("dirs",NULL))        strcpy(dirsfile,p);
  135.         if(p=token_value("masterformat",NULL))    strcpy(masterformat,p);
  136.         if(p=token_value("plusformat",NULL))    strcpy(plusformat,p);
  137.         if(p=token_value("infoformat",NULL))    strcpy(infoformat,p);
  138.         if(p=token_value("info",NULL))        strcpy(info,p);
  139.         if(p=token_value("rawinfo",NULL))    strcpy(rawinfo,p);
  140.         if(p=token_value("begin",NULL))        strcpy(begin,p);
  141.         if(p=token_value("header",NULL))    strcpy(header,p);
  142.         if(p=token_value("nameheader",NULL))    strcpy(nameheader,p);
  143.         if(p=token_value("footer",NULL))    strcpy(footer,p);
  144.         if(p=token_value("end",NULL))        strcpy(ender,p);
  145.  
  146.         if(token_set("rom")!=-1)
  147.             scanrom=atoi(token_value("rom","1"));
  148.         if(token_set("noinfo")!=-1)
  149.             noinfo=atoi(token_value("noinfo","1"))!=0;
  150.         if(token_set("zeros")!=-1)
  151.             zeros=atoi(token_value("zeros","1"));
  152.  
  153.         if(p=token_value("list",NULL))    {
  154.             strcpy(summary[summarys].dir,token_value("dir",""));
  155.             strcpy(summary[summarys].name,token_value("name",""));
  156.  
  157.             if(!(summary[summarys].file=fopen(p,WRITE_TEXT)))    {
  158.                 fprintf(stderr,
  159.                     "%s: can't open summary file \"%s\"\n",
  160.                     *argv,p);
  161.                 exit(1);
  162.             }
  163.             summary[summarys].dirs=0;
  164.             summary[summarys].size=0L;
  165.             summary[summarys].files=0L;
  166.  
  167. #ifdef    DEBUG
  168.             fprintf(stderr,"%s: %s files summary (%s) %s\n",*argv,
  169.                 summary[summarys].name,summary[summarys].dir,
  170.                 p);
  171. #endif
  172.             summary[summarys++].doprint=1;
  173.         }
  174.     }
  175.  
  176.         /* If extra arguments are given, fake a line and parse them */
  177.     if(argc>2)    {
  178.         for(p=line, i=2; i<argc; i++)
  179.             p+=sprintf(p," %s",argv[i]);
  180.         argc=2;
  181.         goto parse;
  182.     }
  183.  
  184.     fclose(config);
  185.  
  186.         /* Then read Waffle dirs config file for file area info */
  187.     if(!(config=fopen(dirsfile,READ_TEXT)))    {
  188.         fprintf(stderr,"%s: can't open Waffle dirs config file \"%s\"\n",
  189.             *argv,dirsfile);
  190.         exit(1);
  191.     }
  192.  
  193.         /* Load "nonfile" match pattern */
  194.     nonfile.attribute=0;
  195.     nonfile.filesize=0L;
  196.     nonfile.shortname=nonfile.filename;
  197.     nonfile.extension=nonfile.filename;
  198.     time(&nonfile.filetime);
  199.     nonfile.file_tm=*localtime(&nonfile.filetime);
  200.  
  201.         /* Print begin */
  202.     for(i=0; i<summarys; i++)    {
  203.         description=summary[i].name;
  204.         strcpy(nonfile.filename,summary[i].dir);
  205.         fputs(fileinfo(begin,&nonfile),summary[i].file);
  206.     }
  207.  
  208.         /* Loop through each line (directory) of Waffle dirs file */
  209.     init_match();
  210.     while(fgets(dirsline,sizeof(dirsline),config))    {
  211.         if(!parse_line(dirsline)) continue;
  212.         if(!(dir=token_value("dir",NULL))) continue;
  213.         if(token_set("name")==-1) continue;
  214.  
  215.         if(scanrom<2 && (rom=(token_set("rom")!=-1))!=scanrom)
  216.             continue;
  217.  
  218.         skip=atoi(token_value("skip","0"));
  219.         column=atoi(token_value("column","0"));
  220.  
  221.         p=dir; if(isalpha(p[0]) && p[1]==':') p+=2;
  222.         strcpy(nonfile.filename,p);
  223.         printf("%s ",p);
  224.  
  225.         for(i=0; i<summarys; i++)
  226.             if(summary[i].doprint =
  227.             !strncmp(summary[i].dir,p,strlen(summary[i].dir)))
  228.                 summary[i].dirs++;
  229.  
  230.         fputs2(fileinfo(*(description=token_value("name",""))?
  231.             nameheader:header,&nonfile));
  232.  
  233.         if(rom || noinfo)    {
  234.                 if(!(fi=fopen(strchr(p=token_value("info",info),'/')?
  235.                 p:(p=string("%s/%s",dir,p)),READ_TEXT)))    {
  236. noinput:            fprintf(stderr,"%s: can't open input file \"%s\"\n",
  237.                     *argv,p);
  238.                 exit(1);
  239.                 }
  240.             for(i=0; i<skip; i++)
  241.                 fgets(line,sizeof(line),fi);
  242.             fo=NULL;
  243.         } else    {
  244.             if(!(fi=fopen(strchr(p=token_value("rawinfo",rawinfo),'/')?
  245.                 p:(p=string("%s/%s",dir,p)),READ_TEXT)))
  246.                 goto noinput;
  247.             if(!(fo=fopen(strchr(p=token_value("info",info),'/')?
  248.                 p:(p=string("%s/%s",dir,p)),WRITE_TEXT)))    {
  249.                           fprintf(stderr,"%s: can't open output file \"%s\"\n",
  250.                                 *argv,p);
  251.                 exit(1);
  252.                 }
  253.                     /* It seems a bit silly, but... */
  254.             for(i=0; i<skip; i++)
  255.                 fputc('\n',fo);
  256.         }
  257.  
  258.             size=0L; files=0L;
  259.         while(fgets(line,sizeof(line),fi))    {
  260.                 /* Trim trailing whitespace (including \n) */
  261.             for(p=line+strlen(line)-1;
  262.                 p>=line && isspace(*p); *p--='\0');
  263.  
  264.                 /* Lines with leading '+' use plusformat */
  265.             if(*line=='+')    {
  266.                 description=line+1;
  267.                 fputs2(fileinfo(plusformat,&nonfile));
  268.                 continue;
  269.             }
  270.                 /* Null or leading illegal char lines get printed */
  271.             if(!*line || NONFILENAME(*line))    {
  272.                 p=line+strlen(line);
  273.                 *p++='\n'; *p='\0';
  274.                 fputs2(line);
  275.                 continue;
  276.             }
  277.  
  278.                 /* Normal file description line */
  279.                 /* Note: column only works if !noinfo */
  280.             for(i=strlen(description=line);
  281.                 *description && !NONFILENAME(*description);
  282.                     description++);
  283.             if(*description)    {
  284.                 *description='\0';
  285.                 if(column)
  286.                     description=line+min(i,column);
  287.                 else
  288.                     while(isspace(*++description));
  289.             }
  290.  
  291.             if(!(i=formatch(p=string("%s/%s",dir,line),
  292.                 READONLY|DIRECTORY,showfile)))    {
  293.                 putchar('\n');
  294.                 fprintf(stderr,
  295.                     "%s: file \"%s\" not found\n",*argv,p);
  296.                 errors++;
  297.             }
  298.         }
  299.         fclose(fi);
  300.         fclose(fo);
  301.  
  302.         if(size)
  303.             for(i=0; i<summarys; i++)
  304.                 if(summary[i].doprint)    {
  305.                     nonfile.filesize=size;
  306.                     fputs(fileinfo(footer,&nonfile),
  307.                         summary[i].file);
  308.                     summary[i].files += files;
  309.                     summary[i].size += size;
  310.                 }
  311.         printf("\r%-79s\r","");
  312.     }
  313.     fclose(config);
  314.  
  315.     for(i=0; i<summarys; i++)    {
  316.         strcpy(nonfile.filename,summary[i].dir);
  317.         nonfile.filesize=summary[i].size;
  318.         files=summary[i].files;
  319.         number=summary[i].dirs;
  320.         description=summary[i].name;
  321.         fputs(fileinfo(ender,&nonfile),summary[i].file);
  322.         fclose(summary[i].file);
  323.     }
  324.     exit(errors?1:0);
  325. }
  326.  
  327.  
  328. /*
  329. **    Create a string by the specified sprintf() rules
  330. */
  331. char *string(char *format,...)    {
  332.     static char buffer[BUFSIZ];
  333.     va_list arg_ptr;
  334.  
  335.     va_start(arg_ptr,format);
  336.     vsprintf(buffer,format,arg_ptr);
  337.     va_end(arg_ptr);
  338.     return buffer;
  339. }
  340.  
  341.  
  342. /*
  343. **    fputs the given string to all the conditional file handles
  344. */
  345. int fputs2(char *string)    {
  346.     register int i;
  347.  
  348.     for(i=0; i<summarys; i++)
  349.         if(summary[i].doprint)
  350.             fputs(string,summary[i].file);
  351. }
  352.  
  353.  
  354. /*
  355. **    Display revolving busy symbol to prevent terminal boredom
  356. */
  357. busy_symbol()    {
  358.     static unsigned char busy_phase=0;
  359.     printf("%c\b","/-\\|"[++busy_phase%4]);
  360. }
  361.  
  362.  
  363. /*
  364. **    Return character defined by given \escape sequence
  365. */
  366. char escapechar(char c,struct file_match *match)    {
  367.     char *p;
  368.  
  369.     switch(c)    {
  370.     case 'a':    return '\a';
  371.     case 'b':    return '\b';
  372.     case 'f':    return '\f';
  373.     case 'n':    return '\n';
  374.     case 'r':    return '\r';
  375.     case 't':    return '\t';
  376.     case 'v':    return '\v';
  377.  
  378.     case '1': case '2': case '3': case '4':
  379.     case '5': case '6': case '7': case '8':
  380.         if(strlen(match->shortname) > c-'1')
  381.             return match->shortname[c-'1'];
  382.         return ' ';
  383.  
  384.     case 'x': case 'y': case 'z':
  385.         if(strlen(match->extension) > c-'x')
  386.             return p[c-'x'];
  387.         return ' ';
  388.     }
  389.     return c;
  390. }
  391.  
  392. /* Conversion utilites: formats and arrays */
  393. char *nformat[4]={"%*ld","%0*ld","%-*ld","%-0*ld"};
  394. char *sformat[2]={"%*.*s","%-*.*s"};
  395.  
  396. char *wkdays[7]={"Sunday","Monday","Tuesday",
  397.     "Wednesday","Thursday","Friday","Saturday"};
  398. char *months[12]={"January","February","March","April","May","June",
  399.     "July","August","September","October","November","December"};
  400.  
  401. #define    PRINTNUM(where,number)    sprintf(where,nformat[2*justify+zerofill],\
  402.                     width,number)
  403. #define    PRINTSTR(where,string)    sprintf(where,sformat[justify],\
  404.                     width,maxwidth,string)
  405.  
  406. char *fileinfo(char *format,struct file_match *match)    {
  407.     char *p,*q,*r,buffer[BUFSIZ];
  408.     static char line[BUFSIZ];
  409.     int i,justify,zerofill,width,maxwidth;
  410.  
  411.     for(q=line; *format; *format?format++:0)    {
  412.         if(*format=='\\')    {
  413.             *q++=escapechar(*++format,match);
  414.             continue;
  415.         } else if(*format=='%')    {
  416.             justify=0; zerofill=0; width=0; maxwidth=999;
  417.  
  418.             again: switch(*++format)    {
  419.             case '-':    /* Right justify */
  420.                 justify=!justify;
  421.                 goto again;
  422.  
  423.             case '0':    /* Zero fill */
  424.                 if(!width)    {
  425.                     zerofill=!zerofill;
  426.                     goto again;
  427.                 }
  428.             case '1': case '2': case '3':    /* Number */
  429.             case '4': case '5': case '6':
  430.             case '7': case '8': case '9':
  431.                 width=width*10+*format-'0';
  432.                 goto again;
  433.  
  434.             case 'a':    /* Abbreviated weekday name */
  435.                 maxwidth=3;
  436.             case 'A':    /* Full weekday name */
  437.                 q+=PRINTSTR(q,wkdays[match->file_tm.tm_wday]);
  438.                 continue;
  439.  
  440.             case 'b':    /* Abbreviated month name */
  441.                 maxwidth=3;
  442.             case 'B':    /* Full month name */
  443.                 q+=PRINTSTR(q,months[match->file_tm.tm_mon]);
  444.                 continue;
  445.  
  446.             case 'd':    /* Day of month */
  447.                 q+=PRINTNUM(q,(long)match->file_tm.tm_mday);
  448.                 continue;
  449.  
  450.             case 'f':    /* Tail filename */
  451.                 q+=PRINTSTR(q,match->shortname);
  452.                 continue;
  453.  
  454.             case 'F':    /* Full filename */
  455.                 q+=PRINTSTR(q,match->filename);
  456.                 continue;
  457.  
  458.             case 'H':    /* Hour in 24-hour clock */
  459.                 q+=PRINTNUM(q,(long)match->file_tm.tm_hour);
  460.                 continue;
  461.  
  462.             case 'I':    /* Hour in 12-hour clock */
  463.                 q+=PRINTNUM(q,((i=match->file_tm.tm_hour)>12)?
  464.                     (long)(i-12):(long)(i));
  465.                 continue;
  466.  
  467.             case 'L':    /* File length either in bytes or k's */
  468.                 if(match->filesize>=100000L)    {
  469.                     if(width) width--;
  470.                     q+=PRINTNUM(q,KILOS(match->filesize));
  471.                     *q++='k';
  472.                     continue;
  473.                 }
  474.             case 'l':    /* File length in bytes */
  475.                 q+=PRINTNUM(q,match->filesize);
  476.                 continue;
  477.  
  478.             case 'M':    /* Minute */
  479.                 q+=PRINTNUM(q,(long)match->file_tm.tm_min);
  480.                 continue;
  481.  
  482.             case 'm':    /* Month */
  483.                 q+=PRINTNUM(q,(long)match->file_tm.tm_mon+1);
  484.                 continue;
  485.  
  486.             case 'N':    /* A number (from somewhere) */
  487.                 q+=PRINTNUM(q,(long)number);
  488.                 continue;
  489.  
  490.             case 'n':    /* Files */
  491.                 q+=PRINTNUM(q,(long)files);
  492.                 continue;
  493.  
  494.             case 'P':    /* Plural 's' if files>1 */
  495.                 if(files>1) *q++='s';
  496.                 continue;
  497.  
  498.             case 'p':    /* "am" or "pm" depending on hour */
  499.                 *q++=(match->file_tm.tm_hour<12)?'a':'p';
  500.                 *q++='m';
  501.                 continue;
  502.  
  503.             case 'S':    /* Second */
  504.                 q+=PRINTNUM(q,(long)match->file_tm.tm_sec);
  505.                 continue;
  506.  
  507.             case 'T':    /* File description with extra info */
  508.                 if(i=specialinfo(q,match->filename,match->extension))    {
  509.                     q+=i;
  510.                     *q++=' ';
  511.                 }
  512.             case 't':    /* Basic file description text */
  513.                 for(p=description, r=buffer; *p; p++)
  514.                     if(*p=='\\')
  515.                         *r++=escapechar(*++p,match);
  516.                     else
  517.                         *r++=*p;
  518.                 *r='\0';
  519.  
  520.                 q+=PRINTSTR(q,buffer);
  521.                 continue;
  522.  
  523.             case 'w':    /* Week day number, 0=Sunday */
  524.                 q+=PRINTNUM(q,match->file_tm.tm_wday);
  525.                 continue;
  526.  
  527.             case 'Y':    /* Year with century */
  528.                 q+=PRINTNUM(q,(long)match->file_tm.tm_year+1900);
  529.                 continue;
  530.  
  531.             case 'y':    /* Year without century */
  532.                 q+=PRINTNUM(q,(long)match->file_tm.tm_year);
  533.                 continue;
  534.             }
  535.         }
  536.         *q++=*format;
  537.     }
  538.  
  539.     while(q>line && isspace(q[-1]) && q[-1]!='\n') q--;
  540.     *q='\0';
  541.  
  542.     return line;
  543. }
  544.  
  545. /*
  546. ** The actual file information display routine
  547. */
  548. int showfile(struct file_match *match)    {
  549.         /* Should we give a warning ? */
  550.     if(*match->shortname == '@') return 0;
  551.  
  552.     if(!(match->attribute&DIRECTORY))    {
  553.         fputs2(fileinfo(masterformat,match));
  554.         size+=match->filesize;
  555.         files++;
  556.         if(!zeros && !match->filesize)
  557.             return 0;
  558.     }
  559.  
  560.     if(fo) fputs(fileinfo(infoformat,match),fo);
  561.  
  562.     return 0;
  563. }
  564.  
  565. /*
  566. ** Put special information on the given filename to given string; return
  567. ** amount of characters put (cache for repeated requests to same file)
  568. */
  569. int specialinfo(char *string,char *filename,char *extension)    {
  570.     static int lastlength=0;
  571.     static char lastinfo[50],lastfile[64]="";
  572.  
  573.         /* Cache last request */
  574.     if(!strcmp(filename,lastfile))    {
  575.         strcpy(string,lastinfo);
  576.         return lastlength;
  577.     }
  578.  
  579.     *lastinfo='\0';
  580.     if(!strcmp(extension,"bin"))        /* MacBinary files format */
  581.         lastlength=macbinfo(lastinfo,filename);
  582.     else if(!strcmp(extension,"gif"))    /* Compu$erve graphics format */
  583.         lastlength=gifinfo(lastinfo,filename);
  584.     else if(!strcmp(extension,"jpg"))    /* JPEG/JFIF graphics format */
  585.         lastlength=jfifinfo(lastinfo,filename);
  586.     else
  587.         lastlength=0;
  588.  
  589.     strcpy(lastfile,filename);
  590.     strcpy(string,lastinfo);
  591.     return lastlength;
  592. }
  593.  
  594.  
  595. /*
  596. **    Show special information on .bin (MacBinary) files: creator info
  597. */
  598. #define    BINSIZE    128
  599.  
  600. int macbinfo(char *string,char *filename)    {
  601.     int chs=0;
  602.     FILE *f;
  603.     char binheader[BINSIZE];
  604.  
  605.     busy_symbol();
  606.     if(    (f=fopen(filename,READ_BINARY)) &&
  607.         (fread(&binheader,1,sizeof(binheader),f)==sizeof(binheader)) &&
  608.         (!(binheader[0] || binheader[74] || binheader[82])) )    {
  609.             *string++=binheader[65+chs++];
  610.             *string++=binheader[65+chs++];
  611.             *string++=binheader[65+chs++];
  612.             *string++=binheader[65+chs++];
  613.         }
  614.     fclose(f);
  615.     return chs;
  616. }
  617.  
  618.  
  619. /*
  620. **    Show special information on .gif files: dimensions plus bit planes
  621. */
  622.  
  623. int gifinfo(char *string,char *filename)    {
  624.     int chs=0;
  625.     FILE *f;
  626.     char buffer[20];
  627.     struct    {
  628.         char version[6];
  629.         unsigned int width,heigth;
  630.         unsigned char colors,bits,reserved;
  631.     } gif_header;
  632.  
  633.     if(!(f=fopen(filename,READ_BINARY))) return 0;
  634.  
  635.     busy_symbol();
  636.     if(    (fread(&gif_header,1,sizeof(gif_header),f)==sizeof(gif_header)) &&
  637.         (!strncmp(gif_header.version,"GIF",3)) &&
  638.         (!(gif_header.colors & 8)) &&
  639.         (!gif_header.reserved))
  640.             chs=sprintf(string,"%ux%ux%u",
  641.                 gif_header.width,gif_header.heigth,
  642.                 (gif_header.colors & 7)+1);
  643.     fclose(f);
  644.     return chs;
  645. }
  646.  
  647.  
  648. /*
  649. **  Show dimensions and colors of JFIF files
  650. **
  651. **  Shamelessly cribbed from code by Charles Hannum (mycroft@ai.mit.edu).
  652. **  "You can do whatever you want with this, but I'd appreciate it if you'd
  653. **  attribute the original code to me.  B-)"
  654. */
  655.  
  656. int jfifinfo(char *string,char *filename)    {
  657.     int chs=0;
  658.     unsigned register int i,j;
  659.     unsigned long l;
  660.     char buffer[20];
  661.     FILE *f;
  662.  
  663.     if(!(f=fopen(filename,READ_BINARY))) return 0;
  664.  
  665.     busy_symbol();
  666.     for ever    {    
  667.         if((unsigned char)fgetc(f)!=0xFF) break;
  668.         if((i=fgetc(f))==-1 || i==0xD9) break;
  669.         if(i>=0xD0 && i<0xD9) continue;
  670.  
  671.         if((j=fgetc(f))==-1) break;    l=j;
  672.         if((j=fgetc(f))==-1) break;    l=(l<<8)+j-2;
  673.         
  674.         if(i==0xC0)    {
  675.             unsigned char sof[5];
  676.  
  677.             if(fread(sof,1,sizeof(sof),f)!=sizeof(sof)) break;
  678.             chs=sprintf(string,"%ux%ux%u",
  679.                 (sof[3]<<8) + sof[4],
  680.                 (sof[1]<<8) + sof[2],
  681.                 sof[0] * 3);
  682.             break;
  683.         }
  684.         fseek(f,l,SEEK_CUR);
  685.     }
  686.  
  687.     fclose(f);
  688.     return chs;
  689. }
  690.