/*
   jpegdump.c - dump JPEG marker information

   v1.00  1992.06.09  initial release
   v1.01  1992.06.22  non-JFIF APP0 markers didn't display first five bytes
   v1.02  1992.07.20  eliminated fseeks which seem to be broken, even for
                      forward seeks only, on pipes on some systems
   v1.03  1992.08.24  added offset and stop at eoi options
   v1.04  1992.08.24  added parsing of JFIF extension markers as per JFIF
                      v1.02 draft of 1992.08.21
   v1.05  1992.08.25  added -r to recurse into JPEG compressed thumbnails
                      (completely untested; probably doesn't work)
   v1.06  1992.09.14  incorporated Phil Richards' code to guess cjpeg quality
                      factor
   v1.07  1992.09.15  added extension 0x13 per final JFIF 1.02; repaired bugs
                      in thumbnail-related code
   v1.08  1992.12.23  Provide multiple levels of verbosity; miscellaneous
                      cleanups
   v1.09  1993.03.01  Add switch for hex/char printout of COM,APPn data
   v1.10  1993.10.26  Identify -q 100 quant tables correctly; more tweaking
                      of verbosity levels

Copyright (c) 1992 Handmade Software, Inc.
by Allan N. Hessenflow

Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear in
supporting documentation.  This software is provided "as is" without
express or implied warranty.


Usage:
  jpegdump [-help] [-q] [-v] [-o n] [-c] [-noc] [-r] [-nor] [-hex] [-nohex]
           [file1 [file2...]]

  -q       quiet: minimal information printed
  -v       verbose: more information printed (multiple -v's give more output)
  -o n     skip first n bytes of file (1234 is decimal, 0x1234 for hex)
  -c       continue past EOI to actual end of file (default)
  -noc     stop upon seeing EOI marker
  -r       recurse into jpeg compressed thumbnails (use at your own risk)
  -nor     don't recurse into thumbnails (default)
  -hex     show COM and APPn data in hex
  -nohex   show COM and APPn data in ASCII when possible (default)

The numbers printed in the `Approximate quality factor' line are as follows:
  Quality: an estimate of the quality factor used when cjpeg was run.
  Scaling factor (scale): mean ratio between quantization table entries
        and JPEG sample table entries, times 100.
  Variance (var): squared standard deviation of the above ratio.
        If this is larger than about 2, then the table is not a simple
        multiple of the standard's sample table, so the file was NOT
        generated by cjpeg and the quality estimate is dubious.

Bugs:
  garbage in, garbage out


Build instructions:
     MSC v7.00 (substitute the path to setargv.obj for \c700\lib\):
       cl jpegdump.c \c700\lib\setargv.obj /link /NOE
     Watcom C/386 v9.0 (substitute the path to wildargv.c for \watcom\src\starup\)
       wcl386 jpegdump.c \watcom\src\startup\wildargv.c
     acc:
       acc jpegdump.c -o jpegdump
     gcc:
       gcc jpegdump.c -o jpegdump
     others:
       any ANSI C compiler should work.  If you don't define CASESENSITIVE
       then you may need to provide your own strnicmp on some systems; this
       is just a case insensitive version of strncmp.  If you're too lazy
       to do that, just define CASESENSITIVE and be sure to enter the
       switches in lower case.


Bug reports, comments to:
     allanh@netcom.com
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef MSDOS
#include <fcntl.h>              /* for setmode() call */
#endif

#ifdef CASESENSITIVE            /* set to disable use of strnicmp */
#define strnicmp(a,b,len)       strncmp(a,b,len)
#endif


/* JPEG marker codes */
#define TEM  0x01
#define SOF  0xc0
#define DHT  0xc4
#define JPGA 0xc8
#define DAC  0xcc
#define RST  0xd0
#define SOI  0xd8
#define EOI  0xd9
#define SOS  0xda
#define DQT  0xdb
#define DNL  0xdc
#define DRI  0xdd
#define DHP  0xde
#define EXP  0xdf
#define APP  0xe0
#define JPG  0xf0
#define COM  0xfe

/* Sample quantization tables from JPEG spec --- only needed for
 * guesstimate of quality factor
 */
static int std_luminance_quant_tbl[64] = {
  16,  11,  12,  14,  12,  10,  16,  14,
  13,  14,  18,  17,  16,  19,  24,  40,
  26,  24,  22,  22,  24,  49,  35,  37,
  29,  40,  58,  51,  61,  60,  57,  51,
  56,  55,  64,  72,  92,  78,  64,  68,
  87,  69,  55,  56,  80, 109,  81,  87,
  95,  98, 103, 104, 103,  62,  77, 113,
 121, 112, 100, 120,  92, 101, 103,  99
};

static int std_chrominance_quant_tbl[64] = {
  17,  18,  18,  24,  21,  24,  47,  26,
  26,  47,  99,  66,  56,  66,  99,  99,
  99,  99,  99,  99,  99,  99,  99,  99,
  99,  99,  99,  99,  99,  99,  99,  99,
  99,  99,  99,  99,  99,  99,  99,  99,
  99,  99,  99,  99,  99,  99,  99,  99,
  99,  99,  99,  99,  99,  99,  99,  99,
  99,  99,  99,  99,  99,  99,  99,  99
};

static int *deftabs[2] =
    { std_luminance_quant_tbl, std_chrominance_quant_tbl };


typedef enum {false, true} boolean;

/* switch table */
static struct opt {
  char *string;
  enum { helpO, quietO, verboseO, offsetO, continueO, noContinueO,
         recurseO, noRecurseO, hexcharsO, nohexcharsO } id;
} options[] = {
  {"?", helpO},
  {"help", helpO},
  {"quiet", quietO},
  {"verbose", verboseO},
  {"offset", offsetO},
  {"continueaftereoi", continueO},
  {"nocontinueaftereoi", noContinueO},
  {"recurseintojpegthumbnails", recurseO},
  {"norecurseintojpegthumbnails", noRecurseO},
  {"hex", hexcharsO},
  {"nohex", nohexcharsO}
};

static boolean hexchars;        /* state of hex-chars switch */


static long dumpMarkers(FILE *, char *, int, boolean, boolean, long);
static unsigned int getWordMoto(FILE *);


int
main (int argc, char **argv)
{
  FILE *inFile;
  int numFileNames;
  int i;
  int verbosity;
  boolean skipeoi, recurse;
  long offset, j;

  /* default switch settings */
  offset=0L;
  skipeoi=true;
  recurse=false;
  verbosity=0;
  hexchars=false;

  /* skip invocation name */
  argc--;
  argv++;

  /* scan arguments */
  numFileNames=0;

  while (argc) {
    if (**argv=='-'
#ifdef MSDOS
        || **argv=='/'
#endif
        ) {
      /* Process a switch */
      size_t length;
      int selectedOption;
      boolean multipleMatches;

      length=strlen((*argv)+1);
      selectedOption=-1;
      multipleMatches=false;
      for (i=0; i<sizeof(options)/sizeof(struct opt); i++) {
        if (strnicmp((*argv)+1, options[i].string, length)==0) {
          if (selectedOption>=0)
            if (options[i].id!=options[selectedOption].id)
              multipleMatches=true;
          selectedOption=i;
          if (length==strlen(options[i].string)) {
            multipleMatches=false;
            break;
          }
        }
      }
      if (selectedOption>=0 && !multipleMatches) {
        switch(options[selectedOption].id) {
        case helpO:
          fprintf(stderr,
                  "usage: jpegdump [-help] [-q] [-v] [-o n] [-c] [-noc] [-r] [-nor] [-hex] [-nohex] [file1 [file2...]]\n");
          exit(0);
          break;
        case quietO:
          verbosity--;
          break;
        case verboseO:
          verbosity++;
          break;
        case offsetO:
          if (argc>1) {
            argc--;
            argv++;
            offset=atol(*argv);
          }
          break;
        case continueO:
          skipeoi=true;
          break;
        case noContinueO:
          skipeoi=false;
          break;
        case recurseO:
          recurse=true;
          break;
        case noRecurseO:
          recurse=false;
          break;
        case hexcharsO:
          hexchars=true;
          break;
        case nohexcharsO:
          hexchars=false;
          break;
        }
      } else if (multipleMatches) {
        fprintf(stderr, "ambiguous option %s\n", *argv);
        exit(-1);
      } else {
        fprintf(stderr, "unrecognized option %s\n", *argv);
        exit(-1);
      }
    } else {
      /* not a switch, process input file with current switch settings */
      numFileNames++;
      inFile=fopen(*argv, "rb");
      if (inFile==NULL) {
        fprintf(stderr, "can't open file %s\n", *argv);
      } else {
        printf("\n%s:\n", *argv);
        for (j=0L; j<offset; j++) /* skip specified offset */
          getc(inFile);
        dumpMarkers(inFile, "", verbosity, skipeoi, recurse, offset);
        fclose(inFile);
      }
    }
    argv++;
    argc--;
  }

  /* Process standard input if no file names were found */
  if (numFileNames==0) {
    inFile=stdin;
#ifdef MSDOS
    /* turn off newline translation */
    setmode(fileno(inFile), O_BINARY);
#endif
    for (j=0L; j<offset; j++)   /* skip specified offset */
      getc(inFile);
    dumpMarkers(inFile, "", verbosity, skipeoi, recurse, offset);
  }

  return 0;
}


static void
printChar (int c)
{
  if (hexchars) {
    printf("$%02x ", c);
  } else {
    if (c == '\\')
      printf("\\\\");
    else if (isprint(c) || c == '\n')
      putchar(c);
    else
      printf("\\%03o", c);
  }
}


static long
dumpMarkers (FILE *stream, char *prefix, int verbosity,
             boolean continueAfterEOI, boolean recurseIntoThumbnails,
             long startingOffset)
{
  int marker;
  int c, c2, c3, c4, c5, row, col, i;
  unsigned int height, aword;
  long length;
  unsigned char huff[16];
  long totalBytesRead;
  char *str;
  char *newPrefix;

  totalBytesRead=0L;

  while (1) {
    c=getc(stream);
    if (c==EOF)
      break;
    totalBytesRead++;
    /* marker prefix? */
    if (c!=0xff)
      continue;
    /* get marker code, skipping fill bytes */
    do {
      c=getc(stream);
      totalBytesRead++;
    } while (c==0xff);
    if (c==EOF)
      break;
    /* ignore stuffed FF/00 sequences */
    if (c==0)
      continue;
    /* OK, we've apparently found a marker */
    marker=c;
    /* Minimum verbosity to print marker is 2 for RSTs, 0 for rest */
    if (verbosity>0 && (marker<RST+0 || marker>RST+7 || verbosity>2))
      printf("%soffset $%lx ", prefix, totalBytesRead+startingOffset-2L);
    length=0L;
    switch (marker) {
    case SOI:
      if (verbosity>0)
        printf("SOI\n");
      break;
    case DRI:
      length=(long) getWordMoto(stream);
      aword = getWordMoto(stream);
      if (verbosity>0) {
        printf("DRI (length %ld)\n", length);
        printf("%s  restart interval %u MCUs\n", prefix, aword);
      }
      totalBytesRead+=4L;
      length-=4L;
      break;
    case APP+0:
    case APP+1:
    case APP+2:
    case APP+3:
    case APP+4:
    case APP+5:
    case APP+6:
    case APP+7:
    case APP+8:
    case APP+9:
    case APP+10:
    case APP+11:
    case APP+12:
    case APP+13:
    case APP+14:
    case APP+15:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("APP%d (length %ld)\n", marker-APP, length);
      totalBytesRead+=2L;
      length-=2L;

      if (marker==APP+0 && length>=14L) {
        char signature[5];

        fread(signature, 1, 5, stream);
        totalBytesRead+=5L;
        length-=5L;
        if (memcmp(signature, "JFIF", 5)==0) {
          unsigned int dpiX, dpiY, thumbX, thumbY;
          long thumbBytes;

          aword = getWordMoto(stream);
          c=getc(stream);
          dpiX=getWordMoto(stream);
          dpiY=getWordMoto(stream);
          thumbX=(unsigned int) getc(stream);
          thumbY=(unsigned int) getc(stream);
          totalBytesRead+=9L;
          length-=9L;
          if (verbosity>0) {
            printf("%s  JFIF version %04x, ", prefix, aword);
            switch (c) {
            case 0:
              printf("aspect ratio %u:%u\n", dpiX, dpiY);
              break;
            case 1:
              printf("%u x %u dpi\n", dpiX, dpiY);
              break;
            case 2:
              printf("%u x %u dpcm\n", dpiX, dpiY);
              break;
            default:
              printf("bogus aspect ratio code %d %u %u\n", c, dpiX, dpiY);
              break;
            }
            printf("%s  thumbnail size %u x %u\n", prefix, thumbX, thumbY);
          }
          thumbBytes = (long)thumbX*(long)thumbY*3L;
          length-=thumbBytes;
          totalBytesRead+=thumbBytes;
          while (thumbBytes--)
            getc(stream);
        } else if (memcmp(signature, "JFXX", 5)==0) {
          int extension;
          unsigned int thumbX, thumbY;
          long thumbBytes;

          extension=getc(stream);
          totalBytesRead++;
          length--;
          switch (extension) {
          case 0x10:
            if (verbosity>0)
              printf("%s  JFIF extension JPEG thumbnail\n", prefix);
            if (recurseIntoThumbnails) {
              long bytes;

              newPrefix=(char *) malloc(strlen(prefix)+5);
              if (newPrefix==NULL) {
                fprintf(stderr, "out of memory\n");
                exit(-1);
              }
              strcpy(newPrefix, prefix);
              strcat(newPrefix, "    ");
              bytes=dumpMarkers(stream, newPrefix, verbosity-1,
                                false, true, startingOffset+totalBytesRead);
              totalBytesRead+=bytes;
              length-=bytes;
              free(newPrefix);
            } else {
              while (length>0L) {
                getc(stream);
                totalBytesRead++;
                length--;
              }
            }
            break;
          case 0x11:
            if (verbosity>0)
              printf("%s  JFIF extension 1 byte/pixel thumbnail\n", prefix);
            thumbX=(unsigned int) getc(stream);
            thumbY=(unsigned int) getc(stream);
            totalBytesRead+=2L;
            length-=2L;
            if (verbosity>0)
              printf("%s    thumbnail size %u x %u\n", prefix, thumbX, thumbY);
            if (verbosity>2)
              printf("%s    palette:\n", prefix);
            for (i=0; i<256; i++) {
              int red, green, blue;
              red=getc(stream);
              green=getc(stream);
              blue=getc(stream);
              totalBytesRead+=3L;
              length-=3L;
              if (verbosity>2)
                printf("%s      %3d %3d %3d\n", prefix, red, green, blue);
            }
            thumbBytes = (long)thumbX*(long)thumbY;
            length-=thumbBytes;
            totalBytesRead+=thumbBytes;
            while (thumbBytes--)
              getc(stream);
            break;
          case 0x13:
            if (verbosity>0)
              printf("%s  JFIF extension 3 byte/pixel thumbnail\n", prefix);
            thumbX=(unsigned int) getc(stream);
            thumbY=(unsigned int) getc(stream);
            totalBytesRead+=2L;
            length-=2L;
            if (verbosity>0)
              printf("%s    thumbnail size %u x %u\n", prefix, thumbX, thumbY);
            thumbBytes = (long)thumbX*(long)thumbY*3L;
            length-=thumbBytes;
            totalBytesRead+=thumbBytes;
            while (thumbBytes--)
              getc(stream);
            break;
          default:
            if (verbosity>0)
              printf("%s  JFIF extension $%02x\n", prefix, extension);
            break;
          }
        } else {
          /* Unrecognized APP0 marker */
          if (verbosity>0) {
            printf("%s  ", prefix);
            for (i=0; i<5; i++)
              printChar((int) signature[i]);
          }
          while (length>0L) {
            c = getc(stream);
            if (verbosity>0)
              printChar(c);
            totalBytesRead++;
            length--;
          }
          if (verbosity>0)
            printf("\n");
        }
      }
      /* Print any remaining data in the APP marker */
      if (length) {
        if (verbosity>0)
          printf("%s  ", prefix);
        while (length>0L) {
          c = getc(stream);
          if (verbosity>0)
            printChar(c);
          totalBytesRead++;
          length--;
        }
        if (verbosity>0)
          printf("\n");
      }
      break;
    case COM:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("COM (length %ld)\n  ", length);
      totalBytesRead+=2L;
      length-=2L;
      while (length>0L) {
        c = getc(stream);
        if (verbosity>0)
          printChar(c);
        totalBytesRead++;
        length--;
      }
      if (verbosity>0)
        printf("\n");
      break;
    /* the following all have the same syntax and fall through to one parser */
    case SOF+0:
      str="SOF0 (baseline DCT Huffman)";
      goto frame;
    case SOF+1:
      str="SOF1 (extended sequential DCT Huffman)";
      goto frame;
    case SOF+2:
      str="SOF2 (progressive DCT Huffman)";
      goto frame;
    case SOF+3:
      str="SOF3 (spatial lossless Huffman)";
      goto frame;
    case SOF+9:
      str="SOF9 (extended sequential DCT arithmetic)";
      goto frame;
    case SOF+10:
      str="SOF10 (progressive DCT arithmetic)";
      goto frame;
    case SOF+11:
      str="SOF11 (spatial lossless arithmetic)";
      goto frame;
      /* the following SOF markers are for differential coding;
         they are listed on page B-2 of CD 10918-1, but for some
         reason are not listed on B-6 and B-7, where the SOF syntax
         is given.  This suggests that the syntax may be different
         for these markers, but it doesn't seem to be defined
         anywhere else in the document.  */
    case SOF+5:
      str="SOF5 (differential sequential DCT Huffman)";
      goto frame;
    case SOF+6:
      str="SOF6 (differential progressive DCT Huffman)";
      goto frame;
    case SOF+7:
      str="SOF7 (differential spatial Huffman)";
      goto frame;
    case SOF+13:
      str="SOF13 (differential sequential DCT arithmetic)";
      goto frame;
    case SOF+14:
      str="SOF14 (differential progressive DCT arithmetic)";
      goto frame;
    case SOF+15:
      str="SOF15 (differential spatial arithmetic)";
      goto frame;
    case DHP:
      str="DHP";

    frame:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("%s (length %ld)\n", str, length);
      totalBytesRead+=2L;
      length-=2L;
      c=getc(stream);
      height=getWordMoto(stream);
      aword=getWordMoto(stream);
      c2=getc(stream);
      totalBytesRead+=6L;
      length-=6L;
      if (verbosity>0)
        printf("%s  sample precision %d\n", prefix, c);
      if (verbosity>= -1)      /* even very terse output includes dimensions */
        printf("%s  width %u, height %u  components %d\n", prefix,
               aword, height, c2);
      while (c2--) {
        c3=getc(stream);
        c4=getc(stream);
        c5=getc(stream);
        totalBytesRead+=3L;
        length-=3L;
        if (verbosity>=0)       /* terse output includes sampling factors */
          printf("%s    id %d horizontal sampling %d, vertical sampling %d, quantization table %d\n", prefix, c3, c4>>4, c4&0x0f, c5);
      }
      break;
    case SOS:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("SOS (length %ld)\n", length);
      totalBytesRead+=2L;
      length-=2L;
      c2=getc(stream);
      totalBytesRead++;
      length--;
      if (verbosity>0)
        printf("%s  components %d\n", prefix, c2);
      while (c2--) {
        c3=getc(stream);
        c4=getc(stream);
        totalBytesRead+=2L;
        length-=2L;
        if (verbosity>0)
          printf("%s    id %d dc table %d, ac table %d\n", prefix,
                 c3, c4>>4, c4&0x0f);
      }
      c=getc(stream);
      c2=getc(stream);
      c3=getc(stream);
      totalBytesRead+=3L;
      length-=3L;
      if (verbosity>0)
        printf("%s  spectral selection %d to %d, bit position high %d, low %d\n",
               prefix, c, c2, c3>>4, c3&0x0f);
      break;
    case DQT:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("DQT (length %ld)\n", length);
      totalBytesRead+=2L;
      length-=2L;

      while (length>0L) {
        int tableindex;
        int *table = NULL;
        double cumsf = 0.0, cumsf2 = 0.0;
        int allones = 1;

        c=getc(stream);
        totalBytesRead++;
        length--;
        tableindex = c & 0x0f;
        if (verbosity>0)
          printf("%s  table %d precision %d\n", prefix, tableindex,
                 (c>>4) ? 16 : 8);
        if (tableindex < 2)
          table = deftabs[tableindex];

        for (row=0; row<8; row++) {
          if (verbosity>1)
            printf("%s    ", prefix);
          for (col=0; col<8; col++) {
            unsigned int val;

            if (c>>4) {
              val=getWordMoto(stream);
              totalBytesRead+=2L;
              length-=2L;
            } else {
              val=(unsigned int) getc(stream);
              totalBytesRead++;
              length--;
            }
            if (verbosity>1)
              printf("%5u ", val);
            if (table) {
              double x;
              /* scaling factor in percent */
              x = 100.0 * (double)val / (double)table[row*8+col];
              cumsf += x;
              cumsf2 += x * x;
              /* separate check for all-ones table (Q 100) */
              if (val != 1) allones = 0;
            }
          }
          if (verbosity>1)
            printf("\n");
        }
        if (table) {
          double qual, var;

          cumsf /= 64.0;        /* mean scale factor */
          cumsf2 /= 64.0;
          var = cumsf2 - (cumsf * cumsf); /* variance */
          if (allones)          /* special case for all-ones table */
            qual = 100.0;
          else if (cumsf <= 100.0)
            qual = (200.0 - cumsf) / 2.0;
          else
            qual = 5000.0 / cumsf;
          if (verbosity>=0)     /* terse output includes quality */
            printf("%s  Approximate quality factor for qtable %d: %.0f (scale %.2f, var %.2f)\n", prefix,
                   tableindex, qual, cumsf, var);
        }
      }
      break;
    case DHT:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("DHT (length %ld)\n", length);
      totalBytesRead+=2L;
      length-=2L;
      while (length>0L) {
        c=getc(stream);
        totalBytesRead++;
        length--;
        if (verbosity>0)
          printf("%s  table %d\n", prefix, c);
        for (i=0; i<16; i++) {
          huff[i]=(unsigned char) getc(stream);
        }
        totalBytesRead+=16L;
        length-=16L;
        for (i=0; i<16; i++) {
          if (verbosity>1)
            printf("%s    bits %2d (codes=%3u) ", prefix, i+1,
                   (unsigned int) huff[i]);
          while (huff[i]--) {
            c2 = getc(stream);
            totalBytesRead++;
            length--;
            if (verbosity>1)
              printf("$%02x ", c2);
          }
          if (verbosity>1)
            printf("\n");
        }
      }
      break;
    case DAC:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("DAT (length %ld)\n", length);
      totalBytesRead+=2L;
      length-=2L;
      while (length>0L) {
        c=getc(stream);
        c2=getc(stream);
        totalBytesRead+=2L;
        length-=2L;
        if (verbosity>0)
          printf("%s  id %d conditioning %d\n", prefix, c, c2);
      }
      break;
    case RST+0:
    case RST+1:
    case RST+2:
    case RST+3:
    case RST+4:
    case RST+5:
    case RST+6:
    case RST+7:
      if (verbosity>2)
        printf("RST%d\n", marker-RST);
      break;
    case DNL:
      length=(long) getWordMoto(stream);
      aword = getWordMoto(stream);
      totalBytesRead+=4L;
      length-=4L;
      if (verbosity>0) {
        printf("DNL (length %ld)\n", length);
        printf("%s  lines %u\n", prefix, aword);
      }
      break;
    case EOI:
      if (verbosity>0)
        printf("EOI\n");
      break;
    case EXP:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("DHP (length %ld)\n", length);
      c=getc(stream);
      if (verbosity>0)
        printf("%s  horizontal expansion %d, vertical expansion %d\n", prefix,
               c>>4, c&0x0f);
      totalBytesRead+=3L;
      length-=3L;
      break;
    case TEM:
      if (verbosity>0)
        printf("TEM\n");
      break;
    default:
      length=(long) getWordMoto(stream);
      if (verbosity>0)
        printf("marker $%02x (length %ld)\n", c, length);
      totalBytesRead+=2L;
      length-=2L;
      if (verbosity>0)
        printf("%s  ", prefix);
      while (length>0L) {
        c=getc(stream);
        totalBytesRead++;
        length--;
        if (verbosity>0)
          printChar(c);
      }
      if (verbosity>0)
        printf("\n");
      break;
    } /* end switch (marker) */
    if (length && verbosity>0)
      printf("%s  bad length (residual=%ld)\n", prefix, length);
    if (marker==EOI && !continueAfterEOI)
      break;
  } /* end while */

  return totalBytesRead;
}


static unsigned int
getWordMoto (FILE *stream)
{
  register unsigned int temp;

  temp=(unsigned int) (getc(stream)<<8);
  return (unsigned int) getc(stream) | temp;
}
