Blame src/tools/oggz-validate.c

Packit a38265
/*
Packit a38265
   Copyright (C) 2003 Commonwealth Scientific and Industrial Research
Packit a38265
   Organisation (CSIRO) Australia
Packit a38265
Packit a38265
   Redistribution and use in source and binary forms, with or without
Packit a38265
   modification, are permitted provided that the following conditions
Packit a38265
   are met:
Packit a38265
Packit a38265
   - Redistributions of source code must retain the above copyright
Packit a38265
   notice, this list of conditions and the following disclaimer.
Packit a38265
Packit a38265
   - Redistributions in binary form must reproduce the above copyright
Packit a38265
   notice, this list of conditions and the following disclaimer in the
Packit a38265
   documentation and/or other materials provided with the distribution.
Packit a38265
Packit a38265
   - Neither the name of CSIRO Australia nor the names of its
Packit a38265
   contributors may be used to endorse or promote products derived from
Packit a38265
   this software without specific prior written permission.
Packit a38265
Packit a38265
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit a38265
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit a38265
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
Packit a38265
   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
Packit a38265
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
Packit a38265
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
Packit a38265
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
Packit a38265
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
Packit a38265
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit a38265
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit a38265
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit a38265
*/
Packit a38265
Packit a38265
#include "config.h"
Packit a38265
Packit a38265
#include <stdio.h>
Packit a38265
#include <stdlib.h>
Packit a38265
#include <string.h>
Packit a38265
#include <getopt.h>
Packit a38265
#include <errno.h>
Packit a38265
Packit a38265
#ifdef HAVE_INTTYPES_H
Packit a38265
#  include <inttypes.h>
Packit a38265
#else
Packit a38265
#  define PRId64 "I64d"
Packit a38265
#endif
Packit a38265
Packit a38265
#include "oggz/oggz.h"
Packit a38265
Packit a38265
#include "oggz_tools.h"
Packit a38265
Packit a38265
#define MAX_ERRORS 10
Packit a38265
Packit a38265
#define SUBSECONDS 1000.0
Packit a38265
Packit a38265
/* #define DEBUG */
Packit a38265
Packit a38265
typedef ogg_int64_t timestamp_t;
Packit a38265
Packit a38265
typedef struct _OVData {
Packit a38265
  OGGZ * writer;
Packit a38265
  OggzTable * missing_eos;
Packit a38265
  OggzTable * packetno;
Packit a38265
Packit a38265
  int theora_count;
Packit a38265
  int audio_count;
Packit a38265
Packit a38265
  int chain_ended;
Packit a38265
} OVData;
Packit a38265
Packit a38265
typedef struct {
Packit a38265
  int error;
Packit a38265
  char * description;
Packit a38265
} error_text;
Packit a38265
Packit a38265
static error_text errors[] = {
Packit a38265
  {-20, "Packet belongs to unknown serialno"},
Packit a38265
  {-24, "Granulepos decreasing within track"},
Packit a38265
  {-5, "Multiple bos pages"},
Packit a38265
  {-6, "Multiple eos pages"},
Packit a38265
  {0, NULL}
Packit a38265
};
Packit a38265
Packit a38265
static char * progname;
Packit a38265
static int max_errors = MAX_ERRORS;
Packit a38265
static int multifile = 0;
Packit a38265
static char * current_filename = NULL;
Packit a38265
static timestamp_t current_timestamp = 0;
Packit a38265
static int exit_status = 0;
Packit a38265
static int nr_errors = 0;
Packit a38265
static int prefix = 0, suffix = 0;
Packit a38265
Packit a38265
static void
Packit a38265
list_errors (void)
Packit a38265
{
Packit a38265
  int i = 0;
Packit a38265
Packit a38265
  printf ("  File contains no Ogg packets\n");
Packit a38265
  printf ("  Packets out of order\n");
Packit a38265
  for (i = 0; errors[i].error; i++) {
Packit a38265
    printf ("  %s\n", errors[i].description);
Packit a38265
  }
Packit a38265
  printf ("  eos marked but no bos\n");
Packit a38265
  printf ("  Missing eos pages\n");
Packit a38265
  printf ("  eos marked on page with no completed packets\n");
Packit a38265
  printf ("  Granulepos on page with no completed packets\n");
Packit a38265
  printf ("  Theora video bos page after audio bos page\n");
Packit a38265
  printf ("  Terminal header page has non-zero granulepos\n");
Packit a38265
  printf ("  Terminal header page contains non-header packet\n");
Packit a38265
  printf ("  Terminal header page contains non-header segment\n");
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
usage (char * progname)
Packit a38265
{
Packit a38265
Packit a38265
  printf ("Usage: %s [options] filename ...\n", progname);
Packit a38265
  printf ("Validate the Ogg framing of one or more files\n");
Packit a38265
  printf ("\n%s detects the following errors in Ogg framing:\n", progname);
Packit a38265
Packit a38265
  list_errors ();
Packit a38265
Packit a38265
  printf ("\nError reporting options\n");
Packit a38265
  printf ("  -M num, --max-errors num\n");
Packit a38265
  printf ("                         Exit after the specified number of errors.\n");
Packit a38265
  printf ("                         A value of 0 specifies no maximum. Default: %d\n", MAX_ERRORS);
Packit a38265
  printf ("  -p, --prefix           Treat input as the prefix of a stream; suppress\n");
Packit a38265
  printf ("                         warnings about missing end-of-stream markers\n");
Packit a38265
  printf ("  -s, --suffix           Treat input as the suffix of a stream; suppress\n");
Packit a38265
  printf ("                         warnings about missing beginning-of-stream markers\n");
Packit a38265
  printf ("                         on the first chain\n");
Packit a38265
  printf ("  -P, --partial          Treat input as a the middle portion of a stream;\n");
Packit a38265
  printf ("                         equivalent to both --prefix and --suffix\n");
Packit a38265
Packit a38265
  printf ("\nMiscellaneous options\n");
Packit a38265
  printf ("  -h, --help             Display this help and exit\n");
Packit a38265
  printf ("  -E, --help-errors      List known types of error and exit\n");
Packit a38265
  printf ("  -v, --version          Output version information and exit\n");
Packit a38265
  printf ("\n");
Packit a38265
  printf ("Exit status is 0 if all input files are valid, 1 otherwise.\n\n");
Packit a38265
  printf ("Please report bugs to <ogg-dev@xiph.org>\n");
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
exit_out_of_memory (void)
Packit a38265
{
Packit a38265
  fprintf (stderr, "%s: Out of memory\n", progname);
Packit a38265
  exit (1);
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
log_error (void)
Packit a38265
{
Packit a38265
  if (multifile && nr_errors == 0) {
Packit a38265
    fprintf (stderr, "%s: Error:\n", current_filename);
Packit a38265
  }
Packit a38265
Packit a38265
  exit_status = 1;
Packit a38265
Packit a38265
  nr_errors++;
Packit a38265
  if (max_errors && nr_errors > max_errors)
Packit a38265
    return OGGZ_STOP_ERR;
Packit a38265
Packit a38265
  return OGGZ_STOP_OK;
Packit a38265
}
Packit a38265
Packit a38265
static ogg_int64_t
Packit a38265
gp_to_granule (OGGZ * oggz, long serialno, ogg_int64_t granulepos)
Packit a38265
{
Packit a38265
  int granuleshift;
Packit a38265
  ogg_int64_t iframe, pframe, granule;
Packit a38265
Packit a38265
  granuleshift = oggz_get_granuleshift (oggz, serialno);
Packit a38265
Packit a38265
  iframe = granulepos >> granuleshift;
Packit a38265
  pframe = granulepos - (iframe << granuleshift);
Packit a38265
  granule = iframe+pframe;
Packit a38265
Packit a38265
  if (oggz_stream_get_content (oggz, serialno) == OGGZ_CONTENT_DIRAC)
Packit a38265
    granule >>= 9;
Packit a38265
Packit a38265
  return granule;
Packit a38265
}
Packit a38265
Packit a38265
static timestamp_t
Packit a38265
gp_to_time (OGGZ * oggz, long serialno, ogg_int64_t granulepos)
Packit a38265
{
Packit a38265
  ogg_int64_t gr_n, gr_d;
Packit a38265
  ogg_int64_t granule;
Packit a38265
Packit a38265
  if (granulepos == -1) return -1.0;
Packit a38265
  if (oggz_get_granulerate (oggz, serialno, &gr_n, &gr_d) != 0) return -1.0;
Packit a38265
Packit a38265
  granule = gp_to_granule (oggz, serialno, granulepos);
Packit a38265
Packit a38265
  return (timestamp_t)((double)(SUBSECONDS * granule * gr_d) / (double)gr_n);
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
ovdata_init (OVData * ovdata)
Packit a38265
{
Packit a38265
  int flags;
Packit a38265
Packit a38265
  current_timestamp = 0;
Packit a38265
Packit a38265
  flags = OGGZ_WRITE|OGGZ_AUTO;
Packit a38265
  if (prefix) flags |= OGGZ_PREFIX;
Packit a38265
  if (suffix) flags |= OGGZ_SUFFIX;
Packit a38265
Packit a38265
  if ((ovdata->writer = oggz_new (flags)) == NULL) {
Packit a38265
    fprintf (stderr, "oggz-validate: unable to create new writer\n");
Packit a38265
    exit (1);
Packit a38265
  }
Packit a38265
Packit a38265
  if ((ovdata->missing_eos = oggz_table_new ()) == NULL)
Packit a38265
    exit_out_of_memory();
Packit a38265
Packit a38265
  if ((ovdata->packetno = oggz_table_new ()) == NULL)
Packit a38265
    exit_out_of_memory();
Packit a38265
Packit a38265
  ovdata->theora_count = 0;
Packit a38265
  ovdata->audio_count = 0;
Packit a38265
  ovdata->chain_ended = 0;
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
ovdata_clear (OVData * ovdata)
Packit a38265
{
Packit a38265
  long serialno;
Packit a38265
  int i, nr_missing_eos = 0;
Packit a38265
Packit a38265
  oggz_close (ovdata->writer);
Packit a38265
Packit a38265
  if (!prefix && (max_errors == 0 || nr_errors <= max_errors)) {
Packit a38265
    nr_missing_eos = oggz_table_size (ovdata->missing_eos);
Packit a38265
    for (i = 0; i < nr_missing_eos; i++) {
Packit a38265
      log_error ();
Packit a38265
      oggz_table_nth (ovdata->missing_eos, i, &serialno);
Packit a38265
      fprintf (stderr, "serialno %010lu: missing *** eos\n", serialno);
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_table_delete (ovdata->missing_eos);
Packit a38265
  oggz_table_delete (ovdata->packetno);
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
read_page (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
Packit a38265
{
Packit a38265
  OVData * ovdata = (OVData *)user_data;
Packit a38265
  ogg_int64_t gpos = ogg_page_granulepos((ogg_page *)og);
Packit a38265
  OggzStreamContent content_type;
Packit a38265
  int packets, packetno, headers, ret = 0;
Packit a38265
Packit a38265
  if (ovdata->chain_ended) {
Packit a38265
    ovdata_clear (ovdata);
Packit a38265
    ovdata_init (ovdata);
Packit a38265
    suffix = 0;
Packit a38265
  }
Packit a38265
Packit a38265
  if (ogg_page_bos ((ogg_page *)og)) {
Packit a38265
    /* Register this serialno as needing eos */
Packit a38265
    if (oggz_table_insert (ovdata->missing_eos, serialno, (void *)0x1) == NULL)
Packit a38265
      exit_out_of_memory();
Packit a38265
Packit a38265
    /* Handle ordering of Theora vs. audio packets */
Packit a38265
    content_type = oggz_stream_get_content (oggz, serialno);
Packit a38265
Packit a38265
    switch (content_type) {
Packit a38265
      case OGGZ_CONTENT_THEORA:
Packit a38265
	ovdata->theora_count++;
Packit a38265
	if (ovdata->audio_count > 0) {
Packit a38265
	  log_error ();
Packit a38265
	  fprintf (stderr, "serialno %010lu: Theora video bos page after audio bos page\n", serialno);
Packit a38265
	}
Packit a38265
        break;
Packit a38265
      case OGGZ_CONTENT_VORBIS:
Packit a38265
      case OGGZ_CONTENT_SPEEX:
Packit a38265
      case OGGZ_CONTENT_PCM:
Packit a38265
      case OGGZ_CONTENT_FLAC0:
Packit a38265
      case OGGZ_CONTENT_FLAC:
Packit a38265
      case OGGZ_CONTENT_CELT:
Packit a38265
	ovdata->audio_count++;
Packit a38265
        break;
Packit a38265
      default:
Packit a38265
        break;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  packets = ogg_page_packets ((ogg_page *)og);
Packit a38265
Packit a38265
  /* Check header constraints */
Packit a38265
  if (!suffix) {
Packit a38265
    if (oggz_table_lookup (ovdata->missing_eos, serialno) == NULL) {
Packit a38265
      ret = log_error ();
Packit a38265
      fprintf (stderr, "serialno %010lu: missing *** bos\n", serialno);
Packit a38265
    }
Packit a38265
Packit a38265
    packetno = (int)oggz_table_lookup (ovdata->packetno, serialno);
Packit a38265
    headers = oggz_stream_get_numheaders (oggz, serialno);
Packit a38265
    if (packetno < headers-1) {
Packit a38265
      /* The previous page was headers, and more are expected */
Packit a38265
      packetno += packets;
Packit a38265
      if (oggz_table_insert (ovdata->packetno, serialno, (void *)packetno) == NULL)
Packit a38265
        exit_out_of_memory();
Packit a38265
Packit a38265
      if (packetno == headers && gpos != 0) {
Packit a38265
        ret = log_error ();
Packit a38265
        fprintf (stderr, "serialno %010lu: Terminal header page has non-zero granulepos\n", serialno);
Packit a38265
      } else if (packetno > headers) {
Packit a38265
        ret = log_error ();
Packit a38265
        fprintf (stderr, "serialno %010lu: Terminal header page contains non-header packet\n", serialno);
Packit a38265
      }
Packit a38265
    } else if (packetno == headers) {
Packit a38265
      /* This is the next page after the page on which the last header finished */
Packit a38265
      if (ogg_page_continued (og)) {
Packit a38265
        ret = log_error ();
Packit a38265
        fprintf (stderr, "serialno %010lu: Terminal header page contains non-header segment\n", serialno);
Packit a38265
      }
Packit a38265
Packit a38265
      /* Mark packetno as greater than headers to avoid these checks for this serialno */
Packit a38265
      if (oggz_table_insert (ovdata->packetno, serialno, (void *)(headers+1)) == NULL)
Packit a38265
        exit_out_of_memory();
Packit a38265
    }
Packit a38265
Packit a38265
  }
Packit a38265
Packit a38265
  /* Check EOS */
Packit a38265
  if (ogg_page_eos((ogg_page *)og)) {
Packit a38265
    int removed = oggz_table_remove (ovdata->missing_eos, serialno);
Packit a38265
    if (!suffix && removed == -1) {
Packit a38265
      ret = log_error ();
Packit a38265
      fprintf (stderr, "serialno %010lu: *** eos marked but no bos\n",
Packit a38265
  	       serialno);
Packit a38265
    }
Packit a38265
Packit a38265
    if (packets == 0) {
Packit a38265
      ret = log_error ();
Packit a38265
      fprintf (stderr, "serialno %010lu: *** eos marked on page with no completed packets\n",
Packit a38265
  	       serialno);
Packit a38265
    }
Packit a38265
Packit a38265
    if (oggz_table_size (ovdata->missing_eos) == 0) {
Packit a38265
      ovdata->chain_ended = 1;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
Packit a38265
  if(gpos != -1 && packets == 0) {
Packit a38265
    ret = log_error ();
Packit a38265
    fprintf (stderr, "serialno %010lu: granulepos %" PRId64 " on page with no completed packets, must be -1\n", serialno, gpos);
Packit a38265
  }
Packit a38265
Packit a38265
  return ret;
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
read_packet (OGGZ * oggz, oggz_packet * zp, long serialno, void * user_data)
Packit a38265
{
Packit a38265
  OVData * ovdata = (OVData *)user_data;
Packit a38265
  ogg_packet * op = &zp->op;
Packit a38265
  timestamp_t timestamp;
Packit a38265
  int flush;
Packit a38265
  int ret = 0, feed_err = 0, i;
Packit a38265
Packit a38265
  timestamp = gp_to_time (oggz, serialno, op->granulepos);
Packit a38265
  if (timestamp != -1.0 && oggz_stream_get_content (oggz, serialno) != OGGZ_CONTENT_DIRAC) {
Packit a38265
    if (timestamp < current_timestamp) {
Packit a38265
      ret = log_error();
Packit a38265
      ot_fprint_time (stderr, (double)timestamp/SUBSECONDS);
Packit a38265
      fprintf (stderr, ": serialno %010lu: Packet out of order (previous ",
Packit a38265
	       serialno);
Packit a38265
      ot_fprint_time (stderr, (double)current_timestamp/SUBSECONDS);
Packit a38265
      fprintf (stderr, ")\n");
Packit a38265
    }
Packit a38265
    current_timestamp = timestamp;
Packit a38265
  }
Packit a38265
Packit a38265
  if (op->granulepos == -1) {
Packit a38265
    flush = 0;
Packit a38265
  } else {
Packit a38265
    flush = OGGZ_FLUSH_AFTER;
Packit a38265
  }
Packit a38265
Packit a38265
  if ((feed_err = oggz_write_feed (ovdata->writer, op, serialno, flush, NULL)) != 0) {
Packit a38265
    ret = log_error ();
Packit a38265
    if (timestamp == -1.0) {
Packit a38265
      fprintf (stderr, "%" PRId64 , oggz_tell (oggz));
Packit a38265
    } else {
Packit a38265
      ot_fprint_time (stderr, (double)timestamp/SUBSECONDS);
Packit a38265
    }
Packit a38265
    fprintf (stderr, ": serialno %010lu: ", serialno);
Packit a38265
    for (i = 0; errors[i].error; i++) {
Packit a38265
      if (errors[i].error == feed_err) {
Packit a38265
	fprintf (stderr, "%s\n", errors[i].description);
Packit a38265
	break;
Packit a38265
      }
Packit a38265
    }
Packit a38265
    if (errors[i].error == 0) {
Packit a38265
      fprintf (stderr,
Packit a38265
	       "Packet violates Ogg framing constraints: %d\n",
Packit a38265
	       feed_err);
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  return ret;
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
validate (char * filename)
Packit a38265
{
Packit a38265
  OGGZ * reader;
Packit a38265
  OVData ovdata;
Packit a38265
  unsigned char buf[1024];
Packit a38265
  long n, nout = 0, bytes_written = 0;
Packit a38265
  int active = 1;
Packit a38265
Packit a38265
  current_filename = filename;
Packit a38265
  current_timestamp = 0;
Packit a38265
  nr_errors = 0;
Packit a38265
Packit a38265
  /*printf ("oggz-validate: %s\n", filename);*/
Packit a38265
Packit a38265
  if (!strncmp (filename, "-", 2)) {
Packit a38265
    if ((reader = oggz_open_stdio (stdin, OGGZ_READ|OGGZ_AUTO)) == NULL) {
Packit a38265
      fprintf (stderr, "oggz-validate: unable to open stdin\n");
Packit a38265
      return -1;
Packit a38265
    }
Packit a38265
  } else if ((reader = oggz_open (filename, OGGZ_READ|OGGZ_AUTO)) == NULL) {
Packit a38265
    fprintf (stderr, "oggz-validate: unable to open file %s\n", filename);
Packit a38265
    return -1;
Packit a38265
  }
Packit a38265
Packit a38265
  ovdata_init (&ovdata);
Packit a38265
Packit a38265
  oggz_set_read_callback (reader, -1, read_packet, &ovdata);
Packit a38265
  oggz_set_read_page (reader, -1, read_page, &ovdata);
Packit a38265
Packit a38265
  while (active && (n = oggz_read (reader, 1024)) != 0) {
Packit a38265
#ifdef DEBUG
Packit a38265
      fprintf (stderr, "validate: read %ld bytes\n", n);
Packit a38265
#endif
Packit a38265
    
Packit a38265
    if (max_errors && nr_errors > max_errors) {
Packit a38265
      fprintf (stderr,
Packit a38265
	       "oggz-validate --max-errors %d: maximum error count reached, bailing out ...\n",
Packit a38265
               max_errors);
Packit a38265
      active = 0;
Packit a38265
    } else while ((nout = oggz_write_output (ovdata.writer, buf, n)) > 0) {
Packit a38265
#ifdef DEBUG
Packit a38265
      fprintf (stderr, "validate: wrote %ld bytes\n", nout);
Packit a38265
#endif
Packit a38265
      bytes_written += nout;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_close (reader);
Packit a38265
Packit a38265
  if (bytes_written == 0) {
Packit a38265
    log_error ();
Packit a38265
    fprintf (stderr, "File contains no Ogg packets\n");
Packit a38265
  }
Packit a38265
Packit a38265
  ovdata_clear (&ovdata);
Packit a38265
Packit a38265
  return active ? 0 : -1;
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
main (int argc, char ** argv)
Packit a38265
{
Packit a38265
  int show_version = 0;
Packit a38265
  int show_help = 0;
Packit a38265
Packit a38265
  /* Cache the --prefix, --suffix options and reset before validating
Packit a38265
   * each input file */
Packit a38265
  int opt_prefix = 0;
Packit a38265
  int opt_suffix = 0;
Packit a38265
Packit a38265
  char * filename;
Packit a38265
  int i = 1;
Packit a38265
Packit a38265
  char * optstring = "M:psPhvE";
Packit a38265
Packit a38265
#ifdef HAVE_GETOPT_LONG
Packit a38265
  static struct option long_options[] = {
Packit a38265
    {"max-errors", required_argument, 0, 'M'},
Packit a38265
    {"prefix", no_argument, 0, 'p'},
Packit a38265
    {"suffix", no_argument, 0, 's'},
Packit a38265
    {"partial", no_argument, 0, 'P'},
Packit a38265
    {"help", no_argument, 0, 'h'},
Packit a38265
    {"help-errors", no_argument, 0, 'E'},
Packit a38265
    {"version", no_argument, 0, 'v'},
Packit a38265
    {0,0,0,0}
Packit a38265
  };
Packit a38265
#endif
Packit a38265
Packit a38265
  ot_init();
Packit a38265
Packit a38265
  progname = argv[0];
Packit a38265
Packit a38265
  if (argc < 2) {
Packit a38265
    usage (progname);
Packit a38265
    return (1);
Packit a38265
  }
Packit a38265
Packit a38265
  if (!strncmp (argv[1], "-?", 2)) {
Packit a38265
#ifdef HAVE_GETOPT_LONG
Packit a38265
    ot_print_options (long_options, optstring);
Packit a38265
#else
Packit a38265
    ot_print_short_options (optstring);
Packit a38265
#endif
Packit a38265
    exit (0);
Packit a38265
  }
Packit a38265
Packit a38265
  while (1) {
Packit a38265
#ifdef HAVE_GETOPT_LONG
Packit a38265
    i = getopt_long(argc, argv, optstring, long_options, NULL);
Packit a38265
#else
Packit a38265
    i = getopt (argc, argv, optstring);
Packit a38265
#endif
Packit a38265
    if (i == -1) {
Packit a38265
      break;
Packit a38265
    }
Packit a38265
    if (i == ':') {
Packit a38265
      usage (progname);
Packit a38265
      goto exit_err;
Packit a38265
    }
Packit a38265
Packit a38265
    switch (i) {
Packit a38265
    case 'M': /* max-errors */
Packit a38265
      max_errors = atoi (optarg);
Packit a38265
      break;
Packit a38265
    case 'p': /* prefix */
Packit a38265
      opt_prefix = 1;
Packit a38265
      break;
Packit a38265
    case 's': /* suffix */
Packit a38265
      opt_suffix = 1;
Packit a38265
      break;
Packit a38265
    case 'P': /* partial */
Packit a38265
      opt_prefix = 1;
Packit a38265
      opt_suffix = 1;
Packit a38265
      break;
Packit a38265
    case 'h': /* help */
Packit a38265
      show_help = 1;
Packit a38265
      break;
Packit a38265
    case 'v': /* version */
Packit a38265
      show_version = 1;
Packit a38265
      break;
Packit a38265
    case 'E': /* list errors */
Packit a38265
      show_help = 2;
Packit a38265
      break;
Packit a38265
    default:
Packit a38265
      break;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  if (show_version) {
Packit a38265
    printf ("%s version " VERSION "\n", progname);
Packit a38265
  }
Packit a38265
Packit a38265
  if (show_help == 1) {
Packit a38265
    usage (progname);
Packit a38265
  } else if (show_help == 2) {
Packit a38265
    list_errors ();
Packit a38265
  }
Packit a38265
Packit a38265
  if (show_version || show_help) {
Packit a38265
    goto exit_out;
Packit a38265
  }
Packit a38265
Packit a38265
  if (max_errors < 0) {
Packit a38265
    printf ("%s: Error: [-M num, --max-errors num] option must be non-negative\n", progname);
Packit a38265
    goto exit_err;
Packit a38265
  }
Packit a38265
Packit a38265
  if (optind >= argc) {
Packit a38265
    usage (progname);
Packit a38265
    goto exit_err;
Packit a38265
  }
Packit a38265
Packit a38265
  if (argc-i > 2) multifile = 1;
Packit a38265
Packit a38265
  for (i = optind; i < argc; i++) {
Packit a38265
    filename = argv[i];
Packit a38265
    prefix = opt_prefix;
Packit a38265
    suffix = opt_suffix;
Packit a38265
    if (validate (filename) == -1)
Packit a38265
      exit_status = 1;
Packit a38265
  }
Packit a38265
Packit a38265
 exit_out:
Packit a38265
  exit (exit_status);
Packit a38265
Packit a38265
 exit_err:
Packit a38265
  exit (1);
Packit a38265
}