"Fossies" - the Fresh Open Source Software Archive

Member "littleutils-1.2.5/imageutils/jpgcom.c" (29 Oct 2021, 10795 Bytes) of package /linux/privat/littleutils-1.2.5.tar.lz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "jpgcom.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.4_vs_1.2.5.

    1 /* jpgcom.c:  print any comments present in the specified JPEG image files
    2  *
    3  * This program is a heavily-stripped version of the "rdjpgcom" program
    4  * distributed with version 9 of the Indendent JPEG Group's software.  The
    5  * original version (along with a copy of the Indendent JPEG Group's README
    6  * file) can be found in the directory orig/.
    7  *
    8  * Copyright (C) 1994-1997, Thomas G. Lane.
    9  * Modified 2009 by Bill Allombert, Guido Vollbeding.
   10  * This file is part of the Independent JPEG Group's software.
   11  * For conditions of distribution and use, see the accompanying README file.
   12  *
   13  * This file contains a very simple stand-alone application that displays
   14  * the text in COM (comment) markers in a JFIF file.
   15  * This may be useful as an example of the minimum logic needed to parse
   16  * JPEG markers.
   17  *
   18  * Modifications Copyright (C) 2004-2021 by Brian Lindholm.  All rights
   19  * reserved except as specified above.
   20  */
   21 
   22 #include <config.h>
   23 
   24 #include <ctype.h>      /* to declare isupper(), tolower() */
   25 #include <limits.h>     /* to determine PATH_MAX */
   26 #ifdef HAVE_LOCALE_H
   27 # include <locale.h>        /* Bill Allombert: use locale for isprint */
   28 #endif
   29 #ifdef HAVE_STDIO_H
   30 # include <stdio.h>     /* other I/O functions */
   31 #endif
   32 #ifdef HAVE_STDLIB_H
   33 # include <stdlib.h>        /* other I/O functions */
   34 #endif
   35 #ifdef HAVE_STRING_H
   36 # include <string.h>        /* other I/O functions */
   37 #endif
   38 
   39 #ifdef HAVE_UNISTD_H
   40 # include <unistd.h>
   41 # define OPTEND -1
   42 #else
   43 # define OPTEND EOF
   44 #endif
   45 #ifdef HAVE_GETOPT_H
   46 # include <getopt.h>
   47 #endif
   48 
   49 #ifdef __MINGW32__
   50 extern int getopt (int argc, char * const *argv, const char *optstring);
   51 extern char *optarg;
   52 extern int optind;
   53 #endif
   54 
   55 #ifndef PATH_MAX
   56 # define PATH_MAX 256
   57 #endif
   58 
   59 /*
   60  * These macros are used to read the input file.
   61  * To reuse this code in another application, you might need to change these.
   62  */
   63 
   64 static FILE * infile;       /* input JPEG file */
   65 
   66 /* Return next input byte, or EOF if no more */
   67 #define NEXTBYTE()  getc(infile)
   68 
   69 /* Error handler */
   70 #define ERREXIT(msg)  { fprintf(stderr, "jpgcom error: %s\n", msg); return(EOF); }
   71 
   72 
   73 /* Read one byte, testing for EOF */
   74 static int
   75 read_1_byte (void)
   76 {
   77   int c;
   78 
   79   c = NEXTBYTE();
   80   if (c == EOF)
   81     ERREXIT("Premature EOF in JPEG file");
   82   return c;
   83 }
   84 
   85 /* Read 2 bytes, convert to unsigned int */
   86 /* All 2-byte quantities in JPEG markers are MSB first */
   87 static unsigned int
   88 read_2_bytes (void)
   89 {
   90   int c1, c2;
   91 
   92   c1 = NEXTBYTE();
   93   if (c1 == EOF)
   94     ERREXIT("Premature EOF in JPEG file");
   95   c2 = NEXTBYTE();
   96   if (c2 == EOF)
   97     ERREXIT("Premature EOF in JPEG file");
   98   return (((unsigned int) c1) << 8) + ((unsigned int) c2);
   99 }
  100 
  101 
  102 /*
  103  * JPEG markers consist of one or more 0xFF bytes, followed by a marker
  104  * code byte (which is not an FF).  Here are the marker codes of interest
  105  * in this program.  (See jdmarker.c for a more complete list.)
  106  */
  107 
  108 #define M_SOF0  0xC0        /* Start Of Frame N */
  109 #define M_SOF1  0xC1        /* N indicates which compression process */
  110 #define M_SOF2  0xC2        /* Only SOF0-SOF2 are now in common use */
  111 #define M_SOF3  0xC3
  112 #define M_SOF5  0xC5        /* NB: codes C4 and CC are NOT SOF markers */
  113 #define M_SOF6  0xC6
  114 #define M_SOF7  0xC7
  115 #define M_SOF9  0xC9
  116 #define M_SOF10 0xCA
  117 #define M_SOF11 0xCB
  118 #define M_SOF13 0xCD
  119 #define M_SOF14 0xCE
  120 #define M_SOF15 0xCF
  121 #define M_SOI   0xD8        /* Start Of Image (beginning of datastream) */
  122 #define M_EOI   0xD9        /* End Of Image (end of datastream) */
  123 #define M_SOS   0xDA        /* Start Of Scan (begins compressed data) */
  124 #define M_APP0  0xE0        /* Application-specific marker, type N */
  125 #define M_APP12 0xEC        /* (we don't bother to list all 16 APPn's) */
  126 #define M_COM   0xFE        /* COMment */
  127 
  128 
  129 /*
  130  * Find the next JPEG marker and return its marker code.
  131  * We expect at least one FF byte, possibly more if the compressor used FFs
  132  * to pad the file.
  133  * There could also be non-FF garbage between markers.  The treatment of such
  134  * garbage is unspecified; we choose to skip over it but emit a warning msg.
  135  * NB: this routine must not be used after seeing SOS marker, since it will
  136  * not deal correctly with FF/00 sequences in the compressed image data...
  137  */
  138 
  139 static int
  140 next_marker (void)
  141 {
  142   int c;
  143   int discarded_bytes = 0;
  144 
  145   /* Find 0xFF byte; count and skip any non-FFs. */
  146   c = read_1_byte();
  147   if (c == EOF)
  148     return EOF;
  149   while (c != 0xFF) {
  150     discarded_bytes++;
  151     c = read_1_byte();
  152     if (c == EOF)
  153       return EOF;
  154   }
  155   /* Get marker code byte, swallowing any duplicate FF bytes.  Extra FFs
  156    * are legal as pad bytes, so don't count them in discarded_bytes.
  157    */
  158   do {
  159     c = read_1_byte();
  160     if (c == EOF)
  161       return EOF;
  162   } while (c == 0xFF);
  163 
  164   if (discarded_bytes != 0) {
  165     fprintf(stderr, "jpgcom warning: garbage data found in JPEG file\n");
  166   }
  167 
  168   return c;
  169 }
  170 
  171 
  172 /*
  173  * Read the initial marker, which should be SOI.
  174  * For a JFIF file, the first two bytes of the file should be literally
  175  * 0xFF M_SOI.  To be more general, we could use next_marker, but if the
  176  * input file weren't actually JPEG at all, next_marker might read the whole
  177  * file and then return a misleading error message...
  178  */
  179 
  180 static int
  181 first_marker (void)
  182 {
  183   int c1, c2;
  184 
  185   c1 = NEXTBYTE();
  186   if (c1 == EOF)
  187     ERREXIT("Not a JPEG file");
  188   c2 = NEXTBYTE();
  189   if (c2 == EOF)
  190     ERREXIT("Not a JPEG file");
  191   if (c1 != 0xFF || c2 != M_SOI)
  192     ERREXIT("Not a JPEG file");
  193   return c2;
  194 }
  195 
  196 
  197 /*
  198  * Most types of marker are followed by a variable-length parameter segment.
  199  * This routine skips over the parameters for any marker we don't otherwise
  200  * want to process.
  201  * Note that we MUST skip the parameter segment explicitly in order not to
  202  * be fooled by 0xFF bytes that might appear within the parameter segment;
  203  * such bytes do NOT introduce new markers.
  204  */
  205 
  206 static int
  207 skip_variable (void)
  208 /* Skip over an unknown or uninteresting variable-length marker */
  209 {
  210   unsigned int length;
  211   int rc;
  212 
  213   /* Get the marker parameter length count */
  214   length = read_2_bytes();
  215   /* Length includes itself, so must be at least 2 */
  216   if (length < 2)
  217     ERREXIT("Erroneous JPEG marker length");
  218   length -= 2;
  219   /* Skip over the remaining bytes */
  220   rc = fseek(infile, (long) length, 1);
  221   if (rc < 0)
  222     ERREXIT("Erroneous JPEG marker length");
  223   return 0;
  224 }
  225 
  226 
  227 /*
  228  * Process a COM marker.
  229  * We want to print out the marker contents as legible text;
  230  * we must guard against non-text junk and varying newline representations.
  231  */
  232 
  233 static int
  234 process_COM (int raw)
  235 {
  236   unsigned int length;
  237   int ch;
  238 
  239   /* Bill Allombert: set locale properly for isprint */
  240 #ifdef HAVE_LOCALE_H
  241   setlocale(LC_CTYPE, "");
  242 #endif
  243 
  244   /* Get the marker parameter length count */
  245   length = read_2_bytes();
  246   /* Length includes itself, so must be at least 2 */
  247   if (length < 2)
  248     ERREXIT("Erroneous JPEG marker length");
  249   length -= 2;
  250 
  251   fprintf (stdout, "\t");
  252   while (length > 0) {
  253     ch = read_1_byte();
  254     if (raw) {
  255       putc(ch, stdout);
  256     /* Emit the character in a readable form.
  257      * Nonprintables are converted to hexidecimal form */
  258     } else if (isprint(ch)) {
  259       putc(ch, stdout);
  260     } else if (ch != 0) {
  261       fprintf(stdout, "[%02x]", (unsigned int) ch);
  262     }
  263     length--;
  264   }
  265 
  266   /* Bill Allombert: revert to C locale */
  267 #ifdef HAVE_LOCALE_H
  268   setlocale(LC_CTYPE, "C");
  269 #endif
  270   return 0;
  271 }
  272 
  273 
  274 /*
  275  * Parse the marker stream until SOS or EOI is seen;
  276  * display any COM markers.
  277  * While the companion program wrjpgcom will always insert COM markers before
  278  * SOFn, other implementations might not, so we scan to SOS before stopping.
  279  */
  280 
  281 static int
  282 scan_JPEG_header (int raw)
  283 {
  284   int marker;
  285   int rc = 0;
  286 
  287   /* Expect SOI at start of file */
  288   if (first_marker() != M_SOI)
  289     ERREXIT("Expected SOI marker first");
  290 
  291   /* Scan miscellaneous markers until we reach SOS. */
  292   for (;;) {
  293     marker = next_marker();
  294     switch (marker) {
  295     case M_SOS:         /* stop before hitting compressed data */
  296       return marker;
  297 
  298     case M_EOI:         /* in case it's a tables-only JPEG stream */
  299       return marker;
  300 
  301     case M_COM:
  302       rc = process_COM(raw);
  303       if (rc == EOF)
  304         return EOF;
  305       break;
  306 
  307     case M_APP12:
  308       fprintf (stdout, "APP12: ");
  309       rc = process_COM(raw);
  310       if (rc == EOF)
  311         return EOF;
  312       break;
  313 
  314     default:            /* Anything else just gets skipped */
  315       rc = skip_variable(); /* we assume it has a parameter count... */
  316       if (rc == EOF)
  317         return EOF;
  318       break;
  319     }
  320   } /* end loop */
  321   return 0;
  322 }
  323 
  324 
  325 /*
  326  * A simple help function.
  327  */
  328 
  329 static void
  330 help (FILE *where)
  331 {
  332   fprintf (where,
  333     "jpgcom " PACKAGE_VERSION "\n"
  334     "usage: jpgcom [-f file_list] [-h(elp)] [-p(ipe)] [-r(aw)] infile...\n");
  335 }
  336 
  337 /*
  338  * Print the filename along with any comments.
  339  */
  340 
  341 static void
  342 print_com (char *filename, int raw)
  343 {
  344   if ((infile = fopen(filename, "rb")) == NULL)
  345     fprintf(stderr, "jpgcom error: can't open %s\n", filename);
  346   else {
  347     fprintf(stdout, "%s", filename);
  348     scan_JPEG_header(raw);
  349     fprintf(stdout, "\n");
  350     (void) fclose(infile);
  351   }
  352 }
  353 
  354 
  355 /*
  356  * The main program.
  357  */
  358 
  359 int
  360 main (int argc, char **argv)
  361 {
  362   FILE *infile;
  363   char filename[PATH_MAX], *listname, *newline, *rc;
  364   int argn, opt, raw, use_file, use_pipe;
  365 
  366   /* parse options */
  367 
  368   listname = "";
  369   use_file = 0;
  370   use_pipe = 0;
  371   raw = 0;
  372   while ((opt = getopt(argc, argv, "f:hpr")) != OPTEND)
  373     switch (opt) {
  374       case 'f':
  375         use_file = 1;
  376         listname = optarg;
  377         break;
  378       case 'h':
  379         help(stdout);
  380         return 0;
  381       case 'p':
  382         use_pipe = 1;
  383         break;
  384       case 'r':
  385         raw = 1;
  386         break;
  387       case '?':
  388         help(stderr);
  389         return 1;
  390     }
  391   if ((optind == argc) && (use_file == 0) && (use_pipe == 0)) {
  392     help(stdout);
  393     return 0;
  394   }
  395 
  396   /* process files in listed in file specified by -f option */
  397 
  398   if (use_file) {
  399     infile = fopen(listname, "r");
  400     if (infile == NULL)
  401       fprintf(stderr, "jpgcom error: can't open %s!\n", listname);
  402     else {
  403       while (!feof(infile)) {
  404         rc = fgets(filename, PATH_MAX - 1, infile);
  405         if (rc != NULL) {
  406           newline = strchr (filename, '\n');
  407           if (newline != NULL)
  408             *newline = '\0';
  409           if (strlen(filename) != 0)
  410             print_com(filename, raw);
  411         }
  412       }
  413       fclose(infile);
  414     }
  415   }
  416 
  417   /* process files listed on stdin (i.e., the -p option) */
  418 
  419   if (use_pipe)
  420     while (!feof(stdin)) {
  421       rc = fgets(filename, PATH_MAX - 1, stdin);
  422       if (rc != NULL) {
  423         newline = strchr (filename, '\n');
  424         if (newline != NULL)
  425           *newline = '\0';
  426         if (strlen(filename) != 0)
  427           print_com(filename, raw);
  428       }
  429     }
  430 
  431   /* process files given in the argument list */
  432 
  433   for (argn = 1; argn < argc; argn++)
  434     print_com(argv[argn], raw);
  435 
  436   /* All done. */
  437 
  438   return 0;
  439 }