/* vobcopy 1.0.x
 *
 * Copyright (c) 2001 - 2007 robos@muon.de
 * Lots of contribution and cleanup from rosenauer@users.sourceforge.net
 * Critical bug-fix from Bas van den Heuvel
 * Takeshi HIYAMA made lots of changes to get it to run on FreeBSD
 * Erik Hovland made changes for solaris
 *  This file is part of vobcopy.
 *
 *  vobcopy is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  vobcopy is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with vobcopy; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 */

/* CONTRIBUTORS (direct or through source "borrowing")
 * rosenauer@users.sf.net - helped me a lot! 
 * Billy Biggs <vektor@dumbterm.net> - took some of his play_title.c code 
 * and implemeted it here 
 * Håkan Hjort <d95hjort@dtek.chalmers.se> and Billy Biggs - libdvdread
 */



/*TODO:
 * mnttab reading is "wrong" on solaris
 * - error handling with the errno and strerror function
 * with -t still numbers get added to the name, -T with no numbers or so.
 */

/*THOUGHT-ABOUT FEATURES
 * - being able to specify
     - which title
     - which chapter
     - which angle 
     - which language(s)
     - which subtitle(s)
     to copy
 * - print the total playing time of that title
 */

/*If you find a bug, contact me at robos@muon.de*/

#include "vobcopy.h"

extern int errno;
char              name[300];
bool              overwrite_flag = FALSE;
/* --------------------------------------------------------------------------*/
/* MAIN */
/* --------------------------------------------------------------------------*/

int main ( int argc, char *argv[] )
{
  int               streamout, block_count, blocks, file_block_count;
//  char              name[300], op;
  char              op;
  char              dvd_path[255], logfile_name[20],logfile_path[280]; /* TODO: fill logfile_path with all zeros so that, when " if strncpy() finds the source too long IT
  DOES NOT TERMINATE the sink, so the following strcat() is undefined and potentially fatal."  - Thanks Leigh!*/
  char              dvd_name[35], vobcopy_call[255], provided_dvd_name[35];
  char              *size_suffix;
  char              pwd[255],provided_output_dir[255],provided_input_dir[255];
  char              alternate_output_dir[4][255], onefile[255];
  unsigned char     bufferin[ DVD_VIDEO_LB_LEN * BLOCK_COUNT ];
  int               i = 0,j = 0, l = 0, argc_i = 0, alternate_dir_count = 0;
  int               partcount = 0, get_dvd_name_return, options_char = 0;
  int               dvd_count = 0, verbosity_level = 0, paths_taken = 0, fast_factor = 1;
  long long unsigned int          seek_start = 0, stop_before_end = 0, temp_var;
  off_t             pwd_free, vob_size = 0, disk_vob_size = 0;
  off_t             offset = 0, free_space = 0;
  off_t             max_filesize_in_blocks = 1048571;  /* for 2^31 / 2048 */
  off_t             max_filesize_in_blocks_summed = 0, angle_blocks_skipped = 0;
  ssize_t           file_size_in_blocks = 0;
  bool              mounted = FALSE, provided_output_dir_flag = FALSE;
  bool              verbose_flag = FALSE, provided_input_dir_flag = FALSE;
  bool              force_flag = FALSE, info_flag = FALSE, cut_flag = FALSE;
  bool              large_file_flag = FALSE, titleid_flag = FALSE;
  bool              mirror_flag = FALSE, provided_dvd_name_flag = FALSE;
  bool              stdout_flag = FALSE, space_greater_2gb_flag = TRUE;
  bool              fast_switch = FALSE, onefile_flag = FALSE;
  bool              quiet_flag = FALSE;
  struct stat       buf;

  dvd_reader_t      *dvd = NULL;
  dvd_file_t        *dvd_file = NULL;
  extern char       *optarg;
  extern int        optind, optopt;

  /**
   *this is taken from play_title.c
   */
  int               titleid = 2, chapid = 0, pgc_id, start_cell;
  int               angle = 0, ttn, pgn, sum_chapters = 0;
  int               sum_angles = 0, most_chapters = 0;
  ifo_handle_t *vmg_file;
  tt_srpt_t *tt_srpt;
  ifo_handle_t *vts_file;
  vts_ptt_srpt_t *vts_ptt_srpt;
  pgc_t *cur_pgc;

  /*
    this is for the mirror feature (readdir)
  */
  struct dirent * directory;
  DIR *dir;

  /**
   *getopt-long
   */
#ifdef HAVE_GETOPT_LONG
  int option_index = 0;
  static struct option long_options[] =
      {
        {"1st_alt_output_dir", 1, 0, '1'
        },
        {"2st_alt_output_dir", 1, 0, '2'},
        {"3st_alt_output_dir", 1, 0, '3'},
        {"4st_alt_output_dir", 1, 0, '4'},
        {"angle", 1, 0, 'a'},
        {"begin", 1, 0, 'b'},
        {"chapter", 1, 0, 'c'},
        {"end", 1, 0, 'e'},
        {"force", 0, 0, 'f'},
        {"fast", 1, 0, 'F'},
        {"help", 0, 0, 'h'},
        {"input-dir", 1, 0, 'i'},
        {"info", 0, 0, 'I'},
        {"large-file", 0, 0, 'l'},
        {"mirror", 0, 0, 'm'},
        {"title-number", 1, 0, 'n'},
        {"output-dir", 1, 0, 'o'},
        {"quiet", 0, 0, 'q'},
        {"onefile", 1, 0, 'O'},
        {"name", 1, 0, 't'},
        {"verbose", 0, 0, 'v'},
        {"version", 0, 0, 'V'},
        /*                    {"test", 1, 0, 'test'}, */
        {0, 0, 0, 0}
      };
#endif


   /* initialize string */
   dvd_path[0] = '\0';

  /*
   * the getopt part (getting the options from command line)
   */
  while (1)
    {
#ifdef HAVE_GETOPT_LONG
      options_char = getopt_long( argc, argv,
                                  "1:2:3:4:a:b:c:e:i:n:o:qO:t:vfF:lmhL:VI",
                                  long_options ,&option_index);
#else
      options_char = getopt( argc, argv, "1:2:3:4:a:b:c:e:i:n:o:qO:t:vfF:lmhL:VI-" );
#endif

      if ( options_char == -1 ) break;

      switch( options_char )
        {
          /* 	  debug */
          /* 	  case'test': */
          /* 	    fprintf( stderr, "%s aufruf %s\n", long_options[option_index].name ,optarg ); */
          /* 	  exit(0); */
          /* 	  break; */

        case'a': /*angle*/
          if ( !isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Error] The thing behind -a has to be a number! \n" );
              exit(1);
            }
          sscanf( optarg, "%i", &angle );
          angle--;/*in the ifo they start at zero */
          if (angle < 0)
            {
              fprintf( stderr, "[Hint] Um, you set angle to 0, try 1 instead ;-)\n" );
              exit(1);
            }
          break;

        case'b': /*size to skip from the beginning (beginning-offset) */
          if ( !isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Error] The thing behind -b has to be a number! \n" );
              exit(1);
            }
          temp_var = atol( optarg );
          size_suffix = strpbrk( optarg, "bkmgBKMG" );
          if( !size_suffix)
            {
              fprintf( stderr, "[Error] Wrong suffix behind -b, only b,k,m or g \n" );
              exit(1);
            }

          switch( *size_suffix )
            {
            case'b':
            case'B':
              temp_var *= 512;/*blocks (normal, not dvd)*/
              break;
            case'k':
            case'K':
              temp_var *= 1024; /*kilo*/
              break;
            case'm':
            case'M':
              temp_var *= ( 1024 * 1024 );/*mega*/
              break;
            case'g':
            case'G':
              temp_var *= ( 1024 * 1024 * 1024 );/*wow, giga *g */
              break;
            case'?':
              fprintf( stderr, "[Error] Wrong suffix behind -b, only b,k,m or g \n" );
              exit(1);
              break;
            }
          /* 	  sscanf( optarg, "%lli", &temp_var ); */
          seek_start = abs( temp_var / 2048 );
          if( seek_start < 0 )
            seek_start = 0;
          cut_flag = TRUE;
          break;

        case'c': /*chapter*/ /*NOT WORKING!!*/
          if ( !isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Error] The thing behind -c has to be a number! \n" );
              exit(1);
            }
          sscanf( optarg, "%i", &chapid );
          chapid--;/*in the ifo they start at zero */
          break;

        case'e': /*size to stop from the end (end-offset) */
          if ( !isdigit( (int) optarg[0] ) )
            {
              fprintf( stderr, "[Error] The thing behind -e has to be a number! \n" );
              exit(1);
            }
          temp_var = atol( optarg );
          size_suffix = strpbrk( optarg, "bkmgBKMG" );
          if( !size_suffix)
            {
              fprintf( stderr, "[Error] Wrong suffix behind -b, only b,k,m or g \n" );
              exit(1);
            }
          switch( *size_suffix )
            {
            case'b':
            case'B':
              temp_var *= 512;  /*blocks (normal, not dvd)*/
              break;
            case'k':
            case'K':
              temp_var *= 1024; /*kilo*/
              break;
            case'm':
            case'M':
              temp_var *= ( 1024 * 1024 );  /*mega*/
              break;
            case'g':
            case'G':
              temp_var *= ( 1024 * 1024 * 1024 );/*wow, giga *g */
              break;
            case'?':
              fprintf( stderr, "[Error] Wrong suffix behind -b, only b,k,m or g \n" );
              exit(1);
              break;
            }

          stop_before_end = abs( temp_var / 2048 );
          if( stop_before_end < 0 )
            stop_before_end = 0;
          cut_flag = TRUE;
          break;

        case'f': /*force flag, some options like -o, -1..-4 set this
          		     themselves */
          force_flag = TRUE;
          break;

        case'h': /* good 'ol help */
          usage( argv[0] );
          break;

        case'i': /*input dir, if the automatic needs to be overridden */
          if ( isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Error] Erm, the number comes behind -n ... \n" );
              exit(1);
            }
          fprintf( stderr, "[Hint] You use -i. Normally this is not necessary, vobcopy finds the input dir by itself. This option is only there if vobcopy makes trouble.\n" );
          fprintf( stderr, "[Hint] If vobcopy makes trouble, please mail me so that I can fix this (robos@muon.de). Thanks\n" );
          strncpy( provided_input_dir, optarg, 255 );
          if( strstr( provided_input_dir, "/dev" ) )
            {
              fprintf( stderr, "[Error] Please don't use -i /dev/something in this version, only the next version will support this again.\n" );
              fprintf( stderr, "[Hint] Please use the mount point instead (/cdrom, /dvd, /mnt/dvd or something)\n" );
            }
          provided_input_dir_flag = TRUE;
          break;

#if defined( HAS_LARGEFILE )
        case'l': /*large file output*/
          //max_filesize_in_blocks = 4500000000000000;
//	   max_filesize_in_blocks = 17179869184; //16 GB
          max_filesize_in_blocks = 8388608; //16 GB /2048 (block)
          /* 2^63 / 2048 (not exactly) */
          large_file_flag = TRUE;
          break;
#endif

        case'm':/*mirrors the dvd to harddrive completly*/
          mirror_flag = TRUE;
          info_flag = TRUE;
          break;

        case'n': /*title number*/
          if ( !isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Error] The thing behind -n has to be a number! \n" );
              exit(1);
            }
          sscanf( optarg, "%i", &titleid );
          titleid_flag = TRUE;
          break;

        case'o': /*output destination */
          if ( isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Hint] Erm, the number comes behind -n ... \n" );
            }
          strncpy( provided_output_dir, optarg, 255 );
          if ( !strcasecmp( provided_output_dir, "stdout" ) || !strcasecmp( provided_output_dir, "-" ) )
            {
              stdout_flag = TRUE;
              force_flag = TRUE;
            }
          else
            {
              provided_output_dir_flag = TRUE;
              alternate_dir_count++;
            }
          /* 	  force_flag = TRUE; */
          break;

        case'q':/*quiet flag* - meaning no progress and such output*/
          quiet_flag = TRUE;
          break;

        case'1': /*alternate output destination*/
        case'2':
        case'3':
        case'4':
          if( alternate_dir_count < options_char - 48 )
            {
              fprintf( stderr, "[Error] Please specify output dirs in this order: -o -1 -2 -3 -4 \n" );
              exit( 1 );
            }

          if ( isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Hint] Erm, the number comes behind -n ... \n" );
            }
          strncpy( alternate_output_dir[ options_char-49 ], optarg, 255 );
          provided_output_dir_flag = TRUE;
          alternate_dir_count++;
          force_flag = TRUE;
          break;

        case't': /*provided title instead of the one from dvd,
          		     maybe even stdout output */
          if ( strlen( optarg ) > 33 )
            printf( "[Hint] The max title-name length is 33, the remainder got discarded" );
          strncpy( provided_dvd_name, optarg, 33 );
          provided_dvd_name_flag = TRUE;
          if ( !strcasecmp( provided_dvd_name,"stdout" ) || !strcasecmp( provided_dvd_name,"-" ) )
            {
              stdout_flag = TRUE;
              force_flag = TRUE;
            }
          break;

        case'v': /*verbosity level, can be called multiple times*/
          strcpy( logfile_path, "/tmp/" );
          verbose_flag = TRUE;
          verbosity_level++;
          break;
        case'F': /*Fast-switch*/
          if ( !isdigit( (int) *optarg ) )
            {
              fprintf( stderr, "[Error] The thing behind -F has to be a number! \n" );
              exit(1);
            }
          sscanf( optarg, "%i", &fast_factor );
          if( fast_factor > BLOCK_COUNT ) //atm is BLOCK_COUNT == 64
            {
              fprintf( stderr, "[Hint] The largest value for -F is %d at the moment - used that one...\n", BLOCK_COUNT );
              fast_factor = BLOCK_COUNT;
            }

          fast_switch = TRUE;
          break;

        case'I': /*info, doesn't do anything, but simply prints some infos
          		     ( about the dvd )*/
          info_flag = TRUE;
          break;

        case'L': /*logfile-path (in case your system crashes every time you
          		     call vobcopy (and since the normal logs get written to 
          		     /tmp and that gets cleared at every reboot... )*/
          strncpy( logfile_path, optarg, 255 );
          strcat( logfile_path, "/" );
          verbose_flag = TRUE;
          verbosity_level = 2;
          break;

        case'O': /*only one file will get copied*/
          onefile_flag = TRUE;
          /*couldn't this be done like this: strncpy( onefile, optarg, sizeof( onefile ) - 1 ); and __shouldn't strncpy be made this way always! */
          strncpy( onefile, optarg, 254 );
          i = 0; /*this i here could be bad... */
          while( onefile[ i ] )
            {
              onefile[ i ] = toupper ( onefile[ i ] );
              i++;
            }
          if( onefile[ i - 1 ] == ',' )
            onefile[ i ] = 0;
          else
            {
              onefile[ i ] = ',';
              onefile[ i + 1 ] = 0;
            }
          mirror_flag = TRUE;
          i = 0;
          break;
        case'V': /*version number output */
          printf( "Vobcopy "VERSION" - GPL Copyright (c) 2001 - 2007 robos@muon.de\n" );
          exit( 0 );
          break;

        case'?': /*probably never gets here, the others should catch it */
          fprintf( stderr, "[Error] Wrong option.\n" );
          usage( argv[0] );
          exit( 1 );
          break;

#ifndef HAVE_GETOPT_LONG
        case'-': /* no getopt, complain */
          fprintf( stderr, "[Error] %s was compiled without support for long options.\n",  argv[0] );
          usage( argv[0] );
          exit( 1 );
          break;
#endif

        default:  /*probably never gets here, the others should catch it */
          fprintf( stderr, "[Error] Wrong option.\n" );
          usage( argv[0] );
          exit( 1 );
        }
    }

  fprintf( stderr, "Vobcopy "VERSION" - GPL Copyright (c) 2001 - 2007 robos@muon.de\n" );
  fprintf( stderr, "[Hint] All lines starting with \"libdvdread:\" are not from vobcopy but from the libdvdread-library\n" );
  if( quiet_flag )
    {
      fprintf( stderr, "[Hint] Quiet mode - All messages will now end up in /tmp/vobcopy.bla\n" );
      if ( freopen( "/tmp/vobcopy.bla" , "a" , stderr ) == NULL )
        {
          printf( "[Error] Aaah! Re-direct of stderr to /tmp/vobcopy.bla didn't work! If -f is not used I stop here... \n" );
          printf( "[Hint] Use -f to continue (at your risk of stupid ascii text ending up in your vobs\n" );
          if ( !force_flag )
            exit( 1 );
        }
    }


  if( verbosity_level > 1 ) /* this here starts writing the logfile */
    {
      fprintf( stderr, "[Info] Uhu, super-verbose\n" );

      strcpy( logfile_name, "vobcopy_" );
      strcat( logfile_name, VERSION );
      strcat( logfile_name, ".log" );
      strcat( logfile_path, logfile_name );
      fprintf( stderr, "[Info] The log-file is written to %s\n", logfile_path );
      fprintf( stderr, "[Hint] Make sure that vobcopy doesn't have to ask questions (like overwriting of old files), these end up in the log file...\n" );
      fprintf( stderr, "[Hint] If you don't like that position, use -L /path/to/logfile/ instead of -v -v\n" );
      if ( freopen( logfile_path, "a" , stderr ) == NULL )
        {
          printf( "[Error] Aaah! Re-direct of stderr to %s didn't work! \n", logfile_path );
          /* oh no! redirecting of stderr failed, do best to quit gracefully */
          exit( 1 );
        }

      strcpy( vobcopy_call, argv[0] );
      for( argc_i = 1; argc_i != argc; argc_i++ )
        {
          strcat( vobcopy_call, " " );
          strcat( vobcopy_call, argv[argc_i] );
        }
      fprintf( stderr, "--------------------------------------------------------------------------------\n" );
      fprintf( stderr, "[Info] Called: %s\n", vobcopy_call );
    }

  /*sanity check: -m and -n are mutually exclusive... */
  if( titleid_flag && mirror_flag )
    {
      fprintf( stderr, "\n[Error] There can be only one: either -m or -n...'\n" );
      exit( 1 );
    }


  /*
   * Check if the provided path is too long
   */
  if ( optind < argc ) /* there is still the path as in vobcopy-0.2.0 */
    {
      provided_input_dir_flag = TRUE;
      if ( strlen( argv[optind] ) >= 255 )
        {
          fprintf( stderr, "\n[Error] Bloody path to long '%s'\n", argv[optind] );
          exit( 1 );
        }
      strncpy( provided_input_dir, argv[optind],255 );
    }

  if ( provided_input_dir_flag ) /*the path has been given to us */
    {
      int result;
      /* 'mounted' is an enum, it should not get assigned an int -- lb */
      if ( ( result = get_device( provided_input_dir, dvd_path ) ) < 0 )
        {
          fprintf( stderr, "[Error] Could not get the device which belongs to the given path!\n" );
          exit( 1 );
        }
      if (result == 0)
        mounted = FALSE;

      if (result == 1)
        mounted = TRUE;
    }
  else /*need to get the path and device ourselves ( oyo = on your own ) */
    {
      if ( ( dvd_count = get_device_on_your_own( provided_input_dir, dvd_path ) ) <= 0 )
        {
          fprintf( stderr, "[Error] Could not get the device and path! Maybe not mounted the dvd?\n" );
          exit( 1 );
        }
      if( dvd_count > 0 )
        mounted = TRUE;
    }

  /*
   * Is the path correct
   */
  fprintf( stderr, "\n[Info] Path to dvd: %s\n", dvd_path );

  if( !( dvd = DVDOpen( dvd_path ) ) )
    {
      fprintf( stderr, "\n[Error] Path thingy didn't work '%s'\n", dvd_path);
      fprintf( stderr, "[Error] Try something like -i /cdrom, /dvd  or /mnt/dvd \n" );
      if( dvd_count > 1 )
        fprintf( stderr, "[Hint] By the way, you have %i cdroms|dvds mounted, that probably caused the problem\n", dvd_count );
      DVDClose( dvd );
      exit( 1 );
    }

  /*
   * this here gets the dvd name
   */
  if( !provided_dvd_name_flag )
    {
      get_dvd_name_return = get_dvd_name( dvd_path, dvd_name );
      fprintf( stderr, "[Info] Name of the dvd: %s\n", dvd_name );
    }
  else
    {
      strncpy( dvd_name, provided_dvd_name, 33 );
    }

  /* The new part taken from play-title.c*/

  /**
   * Load the video manager to find out the information about the titles on
   * this disc.
   */
  vmg_file = ifoOpen( dvd, 0 );
  if( !vmg_file )
    {
      fprintf( stderr, "[Error] Can't open VMG info.\n" );
      DVDClose( dvd );
      return -1;
    }
  tt_srpt = vmg_file->tt_srpt;

  /**
   * Get the title with the most chapters since this is probably the main part
   */
  for( i = 0;i < tt_srpt->nr_of_srpts;i++ )
    {
      sum_chapters += tt_srpt->title[ i ].nr_of_ptts;
      if( i > 0 )
        {
          if( tt_srpt->title[ i ].nr_of_ptts > tt_srpt->title[ most_chapters ].nr_of_ptts )
            {
              most_chapters = i;
            }
        }

    }


  if( !titleid_flag ) /*no title specified (-n ) */
    {
      titleid = most_chapters + 1; /*they start counting with 0, I with 1...*/
    }


  /**
   * Make sure our title number is valid.
   */
  fprintf( stderr, "[Info] There are %d titles on this DVD.\n",
           tt_srpt->nr_of_srpts );
  if( titleid <= 0 || ( titleid-1 ) >= tt_srpt->nr_of_srpts )
    {
      fprintf( stderr, "[Error] Invalid title %d.\n", titleid );
      ifoClose( vmg_file );
      DVDClose( dvd );
      return -1;
    }


  /**
   * Make sure the chapter number is valid for this title.
   */

  fprintf( stderr, "[Info] There are %i chapters on the dvd.\n", sum_chapters );
  fprintf( stderr, "[Info] Most chapters has title %i with %d chapters.\n",
           ( most_chapters + 1 ), tt_srpt->title[ most_chapters ].nr_of_ptts );

  if( info_flag )
    {
      fprintf( stderr, "[Info] All titles:\n" );
      for( i = 0;i < tt_srpt->nr_of_srpts;i++ )
        {
          int chapters = tt_srpt->title[ i ].nr_of_ptts;
          if( chapters > 1 )
            fprintf( stderr, "[Info] Title %i has %d chapters.\n",
                     ( i+1 ), chapters );
          else
            fprintf( stderr, "[Info] Title %i has %d chapter.\n",
                     ( i+1 ), chapters );

        }
    }



  if( chapid < 0 || chapid >= tt_srpt->title[ titleid-1 ].nr_of_ptts )
    {
      fprintf( stderr, "[Error] Invalid chapter %d\n", chapid + 1 );
      ifoClose( vmg_file );
      DVDClose( dvd );
      return -1;
    }

  /**
   * Make sure the angle number is valid for this title.
   */
  for( i = 0;i < tt_srpt->nr_of_srpts;i++ )
    {
      sum_angles += tt_srpt->title[ i ].nr_of_angles;
    }

  fprintf( stderr, "\n[Info] There are %d angles on this dvd.\n", sum_angles );
  if( angle < 0 || angle >= tt_srpt->title[ titleid-1 ].nr_of_angles )
    {
      fprintf( stderr, "[Error] Invalid angle %d\n", angle + 1 );
      ifoClose( vmg_file );
      DVDClose( dvd );
      return -1;
    }

  if( info_flag )
    {
      fprintf( stderr, "[Info] All titles:\n" );
      for(i = 0;i < tt_srpt->nr_of_srpts;i++ )
        {
          int angles = tt_srpt->title[ i ].nr_of_angles;
          if( angles > 1 )
            fprintf( stderr, "[Info] Title %i has %d angles.\n",
                     ( i+1 ), angles );
          else
            fprintf( stderr, "[Info] Title %i has %d angle.\n",
                     ( i+1 ), angles );

        }

      disk_vob_size = get_used_space( provided_input_dir, verbosity_level );
    }


  /*
   * get the whole vob size via stat( ) manually
   */
//  if( mounted )
  if( mounted && !mirror_flag )
    {
      vob_size = ( get_vob_size( titleid, provided_input_dir ) ) -
                 ( seek_start * 2048 ) - ( stop_before_end * 2048 );
      if( vob_size == 0 || vob_size > 9663676416LL) /*9663676416 equals 9GB */
        {
          fprintf( stderr, "\n[Error] Something went wrong during the size detection of the" );
          fprintf( stderr, "\n[Error] vobs, size check at the end won't work (probably), but I continue anyway\n\n" );
          vob_size = 0;
        }
    }

  if( mirror_flag ) /* this gets the size of the whole dvd */
    {
      add_end_slash( provided_input_dir );
      disk_vob_size = get_used_space( provided_input_dir, verbosity_level );
      /*       vob_size = get_free_space( provided_input_dir,verbosity_level ); */
    }


  /*
   * check if on the target directory is enough space free
   * see man statfs for more info
   */

  /*get the current working directory*/
  if ( provided_output_dir_flag )
    {
      strcpy( pwd, provided_output_dir );
    }
  else
    {
      if ( getcwd( pwd, 255 ) == NULL )
        {
          fprintf( stderr, "\n[Error] Hmm, the path length of your current directory is really large (>255)\n" );
          fprintf( stderr, "[Hint] Change to a path with shorter path length pleeeease ;-)\n" );
          exit( 1 );
        }
    }

  add_end_slash( pwd );

  pwd_free = get_free_space( pwd,verbosity_level );

  if( fast_switch )
    block_count = fast_factor;
  else
    block_count = 1;

  /***
    this is the mirror section
  ***/

  if( mirror_flag )
    {/*mirror beginning*/
      fprintf( stderr,"\n[Info] DVD-name: %s\n", dvd_name );
      fprintf( stderr, "[Info]  Disk free: %.0f MB\n", (float) pwd_free / ( 1024*1024 ) );
      fprintf( stderr, "[Info]  Vobs size: %.0f MB\n", (float) disk_vob_size / ( 1024*1024 ) );
      if( ( force_flag || pwd_free > disk_vob_size ) && alternate_dir_count < 2 )
        /* no dirs behind -1, -2 ... since its all in one dir */
        {
          char video_ts_dir[263];
          char number[8], nr[4];
          char input_file[280];
          char output_file[255];
          int  i, start, title_nr = 0;
          off_t file_size;
          double percent = 0, tmp_i = 0, tmp_file_size = 0;
          int k = 0;
          char d_name[256];

          strncpy( name, pwd, 255 );
          strncat( name, dvd_name, 33 );

          if( !stdout_flag )
            {
              makedir ( name );

              strcat( name, "/VIDEO_TS/" );

              makedir ( name );

              fprintf( stderr,"[Info] Writing files to this dir: %s\n", name );
            }
//TODO: substitute with open_dir function
          strcpy( video_ts_dir, provided_input_dir );
          strcat( video_ts_dir, "video_ts"); /*it's either video_ts */
          dir = opendir( video_ts_dir );     /*or VIDEO_TS*/
          if ( dir == NULL )
            {
              strcpy( video_ts_dir, provided_input_dir );
              strcat( video_ts_dir, "VIDEO_TS");
              dir = opendir( video_ts_dir );
              if ( dir == NULL )
                {
                  fprintf( stderr, "[Error] Hmm, weird, the dir video_ts|VIDEO_TS on the dvd couldn't be opened\n");
                  fprintf( stderr, "[Error] The dir to be opened was: %s\n", video_ts_dir );
                  fprintf( stderr, "[Hint] Please mail me what your vobcopy call plus -v -v spits out\n");
                  exit( 1 );
                }
            }

          directory = readdir( dir ); /* thats the . entry */
          directory = readdir( dir ); /* thats the .. entry */
          /* according to the file type (vob, ifo, bup) the file gets copied */
          while( ( directory = readdir( dir ) ) != NULL )
            {/*main mirror loop*/

              k = 0;
              strncpy( output_file, name, 255 );
              /*in dvd specs it says it must be uppercase VIDEO_TS/VTS...
              but iso9660 mounted dvd's sometimes have it lowercase */
              while( directory->d_name[k] )
                {
                  d_name[k] = toupper (directory->d_name[k] );
                  k++;
                }
              d_name[k] = 0;

              /*in order to copy only _one_ file */
              /*                if( onefile_flag )
                              {
                                  if( !strstr( onefile, d_name ) ) maybe other way around*/
              /*                    continue;
                              }*/
              /*in order to copy only _one_ file and do "globbing" a la: -O 02 will copy vts_02_1, vts_02_2 ... all that have 02 in it*/
              if( onefile_flag )
                {
                  char *tokenpos, *tokenpos1;
                  char tmp[12];
                  tokenpos = onefile;
                  if( strstr( tokenpos, "," ) )
                    {
                      while( ( tokenpos1 = strstr( tokenpos, "," ) ) ) /*tokens separated by , */
                        { /*this parses every token without modifying the onefile var */
                          int len_begin;
                          len_begin = tokenpos1 - tokenpos;
                          /*                                      printf(" debug: len = %d tokenpos = %d tokenpos1 = %d\n", len_begin, tokenpos, tokenpos1 ); */
                          strncpy( tmp, tokenpos, len_begin );
                          tokenpos = tokenpos1 + 1;
                          /*                                      printf(" debug: tokenpos = %d tokenpos1 = %d\n", tokenpos, tokenpos1 ); */
                          tmp[ len_begin ] = '\0';
                          /*                                      printf(" debug: token = %s\n", tmp ); */
                          if( strstr( d_name, tmp ) )
                            goto next; /*if the token is found in the d_name copy */
                          else
                            continue; /* next token is tested */
                        }
                      continue; /*no token matched, next d_name is tested */
                    }
                  else
                    {
                      /*                              printf(" debug: onefile = %s\n", onefile ); */
                      if( !strstr( d_name, onefile ) ) /*maybe other way around */
                        {
                          continue;
                        }
                      else
                        goto next; /*if the token is found in the d_name copy */
                      continue; /*no token matched, next d_name is tested */
                    }
                }
next: /*for the goto - ugly, I know... */

              if( stdout_flag ) /*this writes to stdout*/
                {
                  streamout = STDOUT_FILENO; /*in other words: 1, see "man stdout" */
                }
              else
                {
                  if( strstr( d_name, ";?" ) )
                    {
                      fprintf( stderr, "\n[Hint] File on dvd ends in \";?\" (%s)\n", d_name );
                      strncat( output_file, d_name, strlen( d_name ) - 2 );
                    }
                  else
                    {
                      strcat( output_file, d_name );
                    }

                  fprintf(stderr, "[Info] \nWriting to %s \n", output_file);

                  if( open( output_file, O_RDONLY ) >= 0 )
                    {
                      bool bSkip = FALSE;
                      fprintf( stderr,"\n[Error] File '%s' already exists, [o]verwrite, [s]kip or [q]uit? \n", output_file );
                      /*TODO: add [a]ppend  and seek thought stream till point of append is there */
                      while ( 1 )
                        {
//		      op=fgetc( stdin );
                          while ((op = fgetc (stdin)) == EOF)
                            usleep (1);
                          fgetc ( stdin ); /* probably need to do this for second
                          					  time it comes around this loop */
                          if( op == 'o' )
                            {
                              if( ( streamout = open( output_file, O_WRONLY | O_TRUNC ) ) < 0 )
                                {
                                  fprintf( stderr, "\n[Error] Error opening file %s\n", output_file );
                                  fprintf( stderr, "[Error] Error: %s\n", strerror( errno ) );
                                  exit ( 1 );
                                }
                              else
                                close (streamout);
                              overwrite_flag = TRUE;
                              break;
                            }
                          else if( op == 'q' )
                            {
                              DVDCloseFile( dvd_file );
                              DVDClose( dvd );
                              exit( 1 );
                            }
                          else if( op == 's' )
                            {
                              bSkip = TRUE;
                              break;
                            }
                          else
                            {
                              fprintf( stderr, "\n[Hint] Please choose [o]verwrite or [q]uit the next time ;-)\n" );
                            }
                        }
                      if( bSkip )
                        continue; /* next file, please! */
                    }

                  strcat( output_file, ".partial" );

                  if( open( output_file, O_RDONLY ) >= 0 )
                    {
                      fprintf( stderr,"\n[Error] File '%s' already exists, [o]verwrite or [q]uit? \n", output_file );
                      /*TODO: add [a]ppend  and seek thought stream till point of append is there */
                      while ( 1 )
                        {
//		      op=fgetc( stdin );
                          while ((op = fgetc (stdin)) == EOF)
                            usleep (1);
                          fgetc ( stdin ); /* probably need to do this for second
                          					  time it comes around this loop */
                          if( op == 'o' )
                            {
                              if( ( streamout = open( output_file, O_WRONLY | O_TRUNC ) ) < 0 )
                                {
                                  fprintf( stderr, "\n[Error] Error opening file %s\n", output_file );
                                  fprintf( stderr, "[Error] Error: %s\n", strerror( errno ) );
                                  exit ( 1 );
                                }
                              else
                                close( streamout );
                              overwrite_flag = TRUE;
                              break;
                            }
                          else if( op == 'q' )
                            {
                              DVDCloseFile( dvd_file );
                              DVDClose( dvd );
                              exit( 1 );
                            }
                          else
                            {
                              fprintf( stderr, "\n[Hint] Please choose [o]verwrite or [q]uit the next time ;-)\n" );
                            }
                        }
                    }
                  else
                    {
                      /*assign the stream */
                      if( ( streamout = open( output_file, O_WRONLY | O_CREAT, 0644 ) ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] Error opening file %s\n", output_file );
                          fprintf( stderr, "[Error] Error: %s\n", strerror( errno ) );
                          exit ( 1 );
                        }
                    }
                }
              /* get the size of that file*/
              strcpy( input_file, video_ts_dir );
              strcat( input_file, "/" );
              strcat( input_file, directory->d_name );
              stat( input_file, &buf );
              file_size = buf.st_size;
              tmp_file_size = file_size;

              memset( bufferin, 0, DVD_VIDEO_LB_LEN * sizeof( unsigned char ) );

              /*this here gets the title number*/
              for( i = 1; i <= 99; i++ ) /*there are 100 titles, but 0 is
                					   named video_ts, the others are 
                					   vts_number_0.bup */
                {
                  sprintf(number, "_%.2i", i);

                  if ( strstr( directory->d_name, number ) )
                    {
                      title_nr = i;

                      break; /*number found, is in i now*/
                    }
                  /*no number -> video_ts is the name -> title_nr = 0*/
                }

              /*which file type is it*/
              if( strstr( directory->d_name, ".bup" )
                  || strstr( directory->d_name, ".BUP" ) )
                {
                  dvd_file = DVDOpenFile( dvd, title_nr, DVD_READ_INFO_BACKUP_FILE );
                  /*this copies the data to the new file*/
                  for( i = 0; i*DVD_VIDEO_LB_LEN < file_size; i++)
                    {
                      DVDReadBytes( dvd_file, bufferin, DVD_VIDEO_LB_LEN );
                      if( write( streamout, bufferin, DVD_VIDEO_LB_LEN ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] Error writing to %s \n", output_file );
                          fprintf( stderr, "[Error] Error: %s\n", strerror( errno ) );
                          exit( 1 );
                        }
                      /* progress indicator */
                      tmp_i = i;
                      fprintf( stderr, "%4.0fkB of %4.0fkB written\r",
                               ( tmp_i+1 )*( DVD_VIDEO_LB_LEN/1024 ), tmp_file_size/1024 );
                    }
                  fprintf( stderr, "\n");
                  if( !stdout_flag )
                    {
                      if( fsync( streamout ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] error writing to %s \n", output_file );
                          fprintf( stderr, "[Error] error: %s\n", strerror( errno ) );
                          exit( 1 );
                        }

                      close( streamout );
                      re_name( output_file );
                    }
                }

              if( strstr( directory->d_name, ".ifo" )
                  || strstr( directory->d_name, ".IFO" ) )
                {
                  dvd_file = DVDOpenFile( dvd, title_nr, DVD_READ_INFO_FILE );

                  /*this copies the data to the new file*/
                  for( i = 0; i*DVD_VIDEO_LB_LEN < file_size; i++)
                    {
                      DVDReadBytes( dvd_file, bufferin, DVD_VIDEO_LB_LEN );
                      if( write( streamout, bufferin, DVD_VIDEO_LB_LEN ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] Error writing to %s \n", output_file );
                          fprintf( stderr, "[Error] Error: %s\n", strerror( errno ) );
                          exit( 1 );
                        }
                      /* progress indicator */
                      tmp_i = i;
                      fprintf( stderr, "%4.0fkB of %4.0fkB written\r",
                               ( tmp_i+1 )*( DVD_VIDEO_LB_LEN/1024 ), tmp_file_size/1024 );
                    }
                  fprintf( stderr, "\n");
                  if( !stdout_flag )
                    {
                      if( fsync( streamout ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] error writing to %s \n", output_file );
                          fprintf( stderr, "[Error] error: %s\n", strerror( errno ) );
                          exit( 1 );
                        }

                      close( streamout );
                      re_name( output_file );
                    }
                }

              if( strstr( directory->d_name, ".vob" )
                  || strstr( directory->d_name, ".VOB"  ) )
                {
                  int l=0;
                  if( directory->d_name[7] == 48 || title_nr == 0  )
                    {
                      /*this is vts_xx_0.vob or video_ts.vob, a menu vob*/
                      dvd_file = DVDOpenFile( dvd, title_nr, DVD_READ_MENU_VOBS );
                      start = 0 ;
                    }
                  else
                    {
                      dvd_file = DVDOpenFile( dvd, title_nr, DVD_READ_TITLE_VOBS );
                    }
                  if( directory->d_name[7] == 49 || directory->d_name[7] == 48 ) /* 49 means in ascii 1 and 48 0 */
                    {
                      /* reset start when at beginning of Title */
                      start = 0 ;
                    }
                  if( directory->d_name[7] > 49 && directory->d_name[7] < 58 ) /* 49 means in ascii 1 and 58 :  (i.e. over 9)*/
                    {
                      off_t culm_single_vob_size = 0;
                      int a, subvob;
//			 printf( "debug: title = %d \n", title_nr );

                      subvob = ( directory->d_name[7] - 48 );
//			 printf( "debug: subvob = %d \n", subvob );

                      for( a = 1; a < subvob; a++ )
                        {
                          if( strstr( input_file, ";?" ) )
                            input_file[ strlen( input_file ) - 7 ] = ( a + 48 );
                          else
                            input_file[ strlen( input_file ) - 5 ] = ( a + 48 );

                          /*			      input_file[ strlen( input_file ) - 5 ] = ( a + 48 );*/
                          if( stat( input_file, &buf ) < 0 )
                            {
                              fprintf( stderr, "[Info] Can't stat() %s.\n", input_file );
                              exit( 1 );
                            }

                          culm_single_vob_size += buf.st_size;
                          if( verbosity_level > 1 )
                            fprintf( stderr, "[Info] Vob %d %d (%s) has a size of %lli\n", title_nr, subvob, input_file, buf.st_size );
                        }

                      start = ( culm_single_vob_size / DVD_VIDEO_LB_LEN ); /* this here seeks d_name[7]
                      //                          start = ( ( ( directory->d_name[7] - 49 ) * 512 * 1024 ) - ( directory->d_name[7] - 49 ) ); /* this here seeks d_name[7] 
                                                    (which is the 3 in vts_01_3.vob) Gigabyte (which is equivalent to 512 * 1024 blocks 
                                                    (a block is 2kb) in the dvd stream in order to reach the 3 in the above example.
                      			 * NOT! the sizes of the "1GB" files aren't 1GB... */
                    }

                  /*this copies the data to the new file*/
                  if( verbosity_level > 1)
                    fprintf( stderr, "[Info] Start of %s at %d blocks \n", output_file, start );
                  file_block_count = block_count;
                  int tries = 0, skipped_blocks = 0; //TODO: nicht hier
                  for( i = start; ( i - start ) * DVD_VIDEO_LB_LEN < file_size; i += file_block_count)
                    {
                      /* Only read and write as many blocks as there are left in the file */
                      if ( ( i - start + file_block_count ) * DVD_VIDEO_LB_LEN > file_size )
                        {
                          file_block_count = ( file_size / DVD_VIDEO_LB_LEN ) - ( i - start );
                        }

                      /*		      DVDReadBlocks( dvd_file, i, 1, bufferin );this has to be wrong with the 1 there...*/
//                               blocks = DVDReadBlocks( dvd_file, i, file_block_count, bufferin );

                      while( ( blocks = DVDReadBlocks( dvd_file, i, file_block_count, bufferin ) ) <= 0 && tries < 10 )
                        {
                          if( tries == 9 )
                            {
                              i += file_block_count;
                              skipped_blocks +=1;
                              tries=0;
                            }
                          if( verbosity_level >= 1 )
                            fprintf( stderr, "[Warn] Had to skip %d blocks!  ", skipped_blocks );
                          //                              fprintf( stderr, "[Debug] tries=%d\n", tries );
                          //                              blocks = DVDReadBlocks( dvd_file, i, file_block_count, bufferin );
                          tries++;
                        }


                      if( write( streamout, bufferin, DVD_VIDEO_LB_LEN * blocks ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] Error writing to %s \n", output_file );
                          fprintf( stderr, "[Error] Error: %s, errno: %d \n", strerror( errno ), errno );
                          exit( 1 );
                        }

                      l += blocks;
                      /*progression bar*/
                      /*this here doesn't work with -F 10 */
                      /*		      if( !( ( ( ( i-start )+1 )*DVD_VIDEO_LB_LEN )%( 1024*1024 ) ) ) */
                      if( l > 100 )
                        {
                          tmp_i = ( i-start );

                          percent = ( ( ( ( tmp_i+1 )*DVD_VIDEO_LB_LEN )*100 )/tmp_file_size );
                          fprintf( stderr, "%4.0fMB of %4.0fMB written ",
                                   ( ( tmp_i+1 )*DVD_VIDEO_LB_LEN )/( 1024*1024 ),
                                   ( tmp_file_size+2048 )/( 1024*1024 ) );
                          fprintf( stderr,"( %3.1f %% ) \r", percent );
                          l = 0;
                        }

                    }
//this is just so that at the end it actually says 100.0% all the time...
//TODO: if it is correct to always assume it's 100% is a good question....
                  fprintf( stderr, "%4.0fMB of %4.0fMB written ",
                           ( ( tmp_i+1 )*DVD_VIDEO_LB_LEN )/( 1024*1024 ),
                           ( tmp_file_size+2048 )/( 1024*1024 ) );
                  fprintf( stderr,"( 100.0% ) \r" );

                  start=i;
                  fprintf( stderr, "\n" );
                  if( !stdout_flag )
                    {
                      if( fsync( streamout ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] error writing to %s \n", output_file );
                          fprintf( stderr, "[Error] error: %s\n", strerror( errno ) );
                          exit( 1 );
                        }

                      close( streamout );
                      re_name( output_file );
                    }
                }
            }

          ifoClose( vmg_file );
          DVDCloseFile( dvd_file );
          DVDClose( dvd );
          exit( 0 );
        }
      else
        {
          fprintf( stderr, "[Error] Not enough free space on the destination dir. Please choose another one or -f\n" );
          fprintf( stderr, "[Error] or dirs behind -1, -2 ... are NOT allowed with -m!\n" );
          exit( 1 );
        }
    }
  /*end of mirror block*/





  /*
   * Open now up the actual files for reading
   * they come from libdvdread merged together under the given title number
   * (thx again for the great library)
   */
  fprintf( stderr,"[Info] Using Title: %i\n", titleid );
  fprintf( stderr,"[Info] Title has %d chapters and %d angles\n",tt_srpt->title[ titleid - 1 ].nr_of_ptts,tt_srpt->title[ titleid - 1 ].nr_of_angles );
  fprintf( stderr,"[Info] Using Chapter: %i\n", chapid + 1 );
  fprintf( stderr,"[Info] Using Angle: %i\n", angle + 1 );


  if( info_flag && vob_size != 0 )
    {
      fprintf( stderr,"\n[Info] DVD-name: %s\n", dvd_name );
      fprintf( stderr, "[Info]  Disk free: %f MB\n", (double)  (pwd_free / ( 1024.0*1024.0 )) );
      fprintf( stderr, "[Info]  Vobs size: %f MB\n", (double)  vob_size / ( 1024.0*1024.0 ) );
      ifoClose( vmg_file );
      DVDCloseFile( dvd_file );
      DVDClose( dvd );
      /*hope all are closed now...*/
      exit( 0 );
    }


  /**
   * Load the VTS information for the title set our title is in.
   */
  vts_file = ifoOpen( dvd, tt_srpt->title[ titleid-1 ].title_set_nr );
  if( !vts_file )
    {
      fprintf( stderr, "[Error] Can't open the title %d info file.\n",
               tt_srpt->title[ titleid-1 ].title_set_nr );
      ifoClose( vmg_file );
      DVDClose( dvd );
      return -1;
    }


  /**
   * Determine which program chain we want to watch.  This is based on the
   * chapter number.
   */
  ttn = tt_srpt->title[ titleid-1 ].vts_ttn;
  vts_ptt_srpt = vts_file->vts_ptt_srpt;
  pgc_id = vts_ptt_srpt->title[ ttn - 1 ].ptt[ chapid ].pgcn;
  pgn = vts_ptt_srpt->title[ ttn - 1 ].ptt[ chapid ].pgn;
  cur_pgc = vts_file->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc;
  start_cell = cur_pgc->program_map[ pgn - 1 ] - 1;


  /**
   * We've got enough info, time to open the title set data.
   */
  dvd_file = DVDOpenFile( dvd, tt_srpt->title[ titleid-1 ].title_set_nr,
                          DVD_READ_TITLE_VOBS );
  if( !dvd_file )
    {
      fprintf( stderr, "[Error] Can't open title VOBS (VTS_%02d_1.VOB).\n",
               tt_srpt->title[ titleid-1 ].title_set_nr );
      ifoClose( vts_file );
      ifoClose( vmg_file );
      DVDClose( dvd );
      return -1;
    }



  file_size_in_blocks = DVDFileSize( dvd_file );

  if ( vob_size == ( - ( seek_start * 2048 ) - ( stop_before_end * 2048 ) ) )
    {
      vob_size = ( ( off_t ) ( file_size_in_blocks ) * ( off_t ) DVD_VIDEO_LB_LEN ) -
                 ( seek_start * 2048 ) - ( stop_before_end * 2048 );
      if( verbosity_level >= 1 )
        fprintf( stderr, "[Info] Vob_size was 0\n" );
    }


  /*debug-output: difference between vobsize read from cd and size returned from libdvdread */
  if ( mounted && verbose_flag )
    {
      fprintf( stderr,"\n[Info] Difference between vobsize read from cd and size returned from libdvdread:\n" );
      /*        fprintf( stderr,"vob_size (stat) = %lu\nlibdvdsize      = %lu\ndiff            = %lu\n",  //TODO:the diff returns only crap...
                      vob_size, 
                      ( off_t ) ( file_size_in_blocks ) * ( off_t ) DVD_VIDEO_LB_LEN, 
                      ( off_t ) vob_size - ( off_t ) ( ( off_t )( file_size_in_blocks ) * ( off_t ) ( DVD_VIDEO_LB_LEN ) ) ); */
      fprintf( stderr,"[Info] Vob_size (stat) = %lu\n[Info] libdvdsize      = %lu\n",
               vob_size,
               ( off_t ) ( file_size_in_blocks ) * ( off_t ) DVD_VIDEO_LB_LEN );
    }

  if( info_flag )
    {
      fprintf( stderr,"\n[Info] DVD-name: %s\n", dvd_name );
      fprintf( stderr, "[Info]  Disk free: %.0f MB\n", ( float ) (pwd_free / (1024 * 1024)) );
      /* Should be the *disk* size here, right? -- lb */
      fprintf( stderr, "[Info]  Vobs size: %.0f MB\n", ( float ) (disk_vob_size / (1024 * 1024 )) );

      ifoClose( vts_file );
      ifoClose( vmg_file );
      DVDCloseFile( dvd_file );
      DVDClose( dvd );
      /*hope all are closed now...*/
      exit( 0 );
    }


  /* now the actual check if enough space is free*/
  if ( pwd_free < vob_size )
    {
      fprintf( stderr, "\n[Info]  Disk free: %.0f MB", (float) pwd_free / ( 1024*1024 ) );
      fprintf( stderr, "\n[Info]  Vobs size: %.0f MB", (float) vob_size / ( 1024*1024 ) );
      if( !force_flag )
        fprintf( stderr, "\n[Error] Hmm, better change to a dir with enough space left or call with -f (force) \n" );
      if( pwd_free == 0 && !force_flag )
        {
          if( verbosity_level > 1 )
            fprintf( stderr,"[Error] Hmm, statfs (statvfs) seems not to work on that directory. \n" );
          fprintf( stderr, "[Error] Hmm, statfs (statvfs) seems not to work on that directory. \n" );
          fprintf( stderr, "[Hint] Nevertheless, do you want vobcopy to continue [y] or do you want to check for \n" );
          fprintf( stderr, "[Hint] enough space first [q]?\n" );

          while ( 1 )
            {
//	      op=fgetc( stdin );
              while ((op = fgetc (stdin)) == EOF)
                usleep (1);
              if( op == 'y' )
                {
                  force_flag = TRUE;
                  if( verbosity_level >= 1 )
                    fprintf( stderr, "[Info] y pressed - force write\n" );
                  break;
                }
              else if( op == 'n' || op =='q' )
                {
                  if( verbosity_level >= 1 )
                    fprintf( stderr, "[Info] n/q pressed\n" );
                  exit( 1 );
                  break;
                }
              else
                {
                  fprintf( stderr, "[Error] Please choose [y] to continue or [n] to quit\n" );
                }
            }
        }
      if ( !force_flag )
        exit( 1 );
    }

  /*
from bash.org

#464444 +(878)- [X]

<Phryss> Sometimes, I sit back and think about what my father used to tell 
me about the birds and the bees: "Stop fucking the dog.  The neighbors are 
watching, and it's their dog."
  */

  /*********************
   * this is the main read and copy loop
   *********************/
  fprintf( stderr,"\n[Info] DVD-name: %s\n", dvd_name );
  if( provided_dvd_name_flag && !stdout_flag )
    /*if the user has given a name for the file */
    {
      fprintf( stderr, "\n[Info] Your-name for the dvd: %s\n", provided_dvd_name );
      strcpy( dvd_name, provided_dvd_name );
    }

  while( offset < ( file_size_in_blocks - seek_start - stop_before_end ) )
    {
      partcount++;

      if( !stdout_flag ) /*if the stream doesn't get written to stdout*/
        {
          /*part to distribute the files over different directories*/
          if( paths_taken == 0 )
            {
              add_end_slash( pwd );
              free_space = get_free_space( pwd, verbosity_level );
              if( verbosity_level > 1 )
                fprintf( stderr,"[Info] Free space for -o dir: %.0f\n", ( float ) free_space );
              if( large_file_flag )
                make_output_path( pwd,name,get_dvd_name_return,dvd_name,titleid, -1 );
              else
                make_output_path( pwd,name,get_dvd_name_return,dvd_name,titleid, partcount );
            }
          else
            {
              /* 	      if( alternate_dir_count == 1 && !space_greater_2gb_flag )  */
              /*shouldn't come to this only if some data is still there but
                no free space left... */
              /* 		{ */
              /* 		  fprintf( stderr,  */
              /* 			   "\nHmm, looks like there wasn't enough space in the provided dirs ...\n" */
              /* 			   "Did you use -1, -2, -3 or even up to -4 to specify alternate dirs?\n" ); */
              /* 		  exit( 1 ); */
              /* 		} */
              /* 	      for( i = 1; i < 5; i++ ) */
              /* 		{ */
              /* 		  if( paths_taken == i && alternate_dir_count > 1 ) */

              for( i = 1; i < alternate_dir_count; i++ )
                {
                  if( paths_taken == i )
                    {
                      add_end_slash( alternate_output_dir[ i-1 ] );
                      free_space = get_free_space( alternate_output_dir[ i-1 ],verbosity_level );

                      if( verbosity_level > 1 )
                        fprintf( stderr,"[Info] Free space for -%i dir: %.0f\n", i, ( float ) free_space );
                      if ( large_file_flag )
                        make_output_path( alternate_output_dir[ i-1 ], name, get_dvd_name_return, dvd_name, titleid, -1 );
                      else
                        make_output_path( alternate_output_dir[ i-1 ], name, get_dvd_name_return, dvd_name, titleid,partcount );
                      /* 			alternate_dir_count--; */
                    }
                }
            }
          /*here the output size gets adjusted to the given free space*/

          if( !large_file_flag && force_flag && free_space < 2147473408 ) /* 2GB */
            {
              space_greater_2gb_flag = FALSE;
              max_filesize_in_blocks = ( ( free_space - 2097152 ) / 2048 ); /* - 2 MB */
              if( verbosity_level > 1 )
                fprintf( stderr, "[Info] Taken max_filesize_in_blocks(2GB version): %.0f\n", ( float ) max_filesize_in_blocks );
              paths_taken++;
            }
          else if( large_file_flag && force_flag) /*lfs version */
            {
              space_greater_2gb_flag = FALSE;
              max_filesize_in_blocks = ( ( free_space - 2097152) / 2048);/* - 2 MB */
              if( verbosity_level > 1)
                fprintf( stderr,"[Info] Taken max_filesize_in_blocks(lfs version): %.0f\n", ( float ) max_filesize_in_blocks );
              paths_taken++;
            }
          else if( !large_file_flag )
            {
              max_filesize_in_blocks = 1048571; /*if free_space is more than  2 GB fall back to max_filesize_in_blocks=2GB*/
              space_greater_2gb_flag = TRUE;
            }


          if( open( name, O_RDONLY ) >= 0 )
            {
              fprintf( stderr,"\n[Error] File '%s' already exists, [o]verwrite or [q]uit? \n", name );
              /*TODO: add [a]ppend  and seek thought stream till point of append is there */
              while ( 1 )
                {
//		      op=fgetc( stdin );
                  while ((op = fgetc (stdin)) == EOF)
                    usleep (1);
                  fgetc ( stdin ); /* probably need to do this for second time it comes around this loop */
                  if( op == 'o' )
                    {
                      if( ( streamout = open( name, O_WRONLY | O_TRUNC ) ) < 0 )
                        {
                          fprintf( stderr, "\n[Error] Error opening file %s\n", name );
                          fprintf( stderr, "[Error] Error: %s\n", strerror( errno ) );
                          exit ( 1 );
                        }
                      else
                        close( streamout );
                      overwrite_flag = TRUE;
                      break;
                    }
                  else if( op == 'q' )
                    {
                      DVDCloseFile( dvd_file );
                      DVDClose( dvd );
                      exit( 1 );
                    }
                  else
                    {
                      fprintf( stderr, "\n[Hint] please choose [o]verwrite or [q]uit the next time ;-)\n" );
                    }
                }
            }

          strcat( name, ".partial" );

#if defined( HAS_LARGEFILE )
          if( open( name, O_RDONLY|0 ) >= 0 )
#else
          if( open( name, O_RDONLY ) >= 0 )
#endif
            {
              if ( get_free_space( name, verbosity_level ) < 2097152 )
                /* it might come here when the platter is full after a -f */
                {
                  fprintf( stderr, "[Error] Seems your platter is full...\n");
                  exit ( 1 );
                }
              fprintf( stderr, "\n[Error] File '%s' already exists, [o]verwrite, [a]ppend, [q]uit? \n", name );
              while ( 1 )
                {
//		    op=fgetc( stdin );
                  while ((op = fgetc (stdin)) == EOF)
                    usleep (1);
                  fgetc ( stdin ); /* probably need to do this for second time it
                  				       comes around this loop */
                  if( op == 'o' )
                    {
#if defined( HAS_LARGEFILE )
                      if( ( streamout = open( name, O_WRONLY | O_TRUNC | 0 ) ) < 0 )
#else
                      if( ( streamout = open( name, O_WRONLY | O_TRUNC ) ) < 0 )
#endif
                        {
                          fprintf( stderr, "\n[Error] Error opening file %s\n", name );
                          exit ( 1 );
                        }
                      else
                        close (streamout);
                      overwrite_flag = TRUE;
                      break;
                    }
                  else if( op == 'a' )
                    {
#if defined( HAS_LARGEFILE )
                      if( ( streamout = open( name, O_WRONLY | O_APPEND | 0 ) ) < 0 )
#else
                      if( ( streamout = open( name, O_WRONLY | O_APPEND ) ) < 0 )
#endif
                        {
                          fprintf( stderr, "\n[Error] Error opening file %s\n", name );
                          exit ( 1 );
                        }
                      else
                        close( streamout );
                      if( verbosity_level >= 1 )
                        fprintf( stderr, "[Info] User chose append\n" );
                      break;
                    }
                  else if( op == 'q' )
                    {
                      DVDCloseFile( dvd_file );
                      DVDClose( dvd );
                      exit( 1 );
                    }
                  else
                    {
                      fprintf( stderr, "\n[Hint] Please choose [o]verwrite, [a]ppend, [q]uit the next time ;-)\n" );
                    }
                }
            }
          else
            {
              /*assign the stream */
#if defined( HAS_LARGEFILE )
              if( ( streamout = open( name, O_WRONLY | O_CREAT | 0, 0644 ) ) < 0 )
#else
              if( ( streamout = open( name, O_WRONLY | O_CREAT, 0644 ) ) < 0 )
#endif
                {
                  fprintf( stderr, "\n[Error] Error opening file %s\n", name );
                  exit ( 1 );
                }
            }
        }

      if( stdout_flag ) /*this writes to stdout*/
        {
          streamout = STDOUT_FILENO; /*in other words: 1, see "man stdout" */
        }

      /* this here is the main copy part */

      fprintf( stderr, "\n" );
      memset( bufferin, 0, BLOCK_COUNT * DVD_VIDEO_LB_LEN * sizeof( unsigned char ) );

      /*       for ( ; ( offset + ( off_t ) seek_start ) < ( ( off_t ) file_size_in_blocks - ( off_t ) stop_before_end )   */
      /* 	     && offset - ( off_t )max_filesize_in_blocks_summed < max_filesize_in_blocks;  */
      /* 	    offset++ ) */
      /* 	{ */

      file_block_count = block_count;
      for ( ; ( offset + ( off_t ) seek_start ) < ( ( off_t ) file_size_in_blocks - ( off_t ) stop_before_end )
            && offset - ( off_t )max_filesize_in_blocks_summed - (off_t)angle_blocks_skipped < max_filesize_in_blocks;
            offset += file_block_count )
        {
          /* Only read and write as many blocks as there are left in the file */
          if ( ( offset + file_block_count + ( off_t ) seek_start ) > ( ( off_t ) file_size_in_blocks - ( off_t ) stop_before_end ) )
            {
              file_block_count = ( off_t ) file_size_in_blocks - ( off_t ) stop_before_end - offset - ( off_t ) seek_start;
            }
          if ( offset + file_block_count - ( off_t )max_filesize_in_blocks_summed - (off_t)angle_blocks_skipped > max_filesize_in_blocks )
            {
              file_block_count = max_filesize_in_blocks - ( offset + file_block_count - ( off_t )max_filesize_in_blocks_summed - (off_t)angle_blocks_skipped );
            }

          blocks = DVDReadBlocks( dvd_file,( offset + seek_start ), file_block_count, bufferin );
          if( write( streamout, bufferin, DVD_VIDEO_LB_LEN * blocks ) < 0 )
            {
              fprintf( stderr, "\n[Error] Write() error\n"
                       "[Error] It's possible that you try to write files\n"
                       "[Error] greater than 2GB to filesystem which\n"
                       "[Error] doesn't support it? (try without -l)\n" );
              fprintf( stderr, "[Error] Error: %s\n", strerror( errno ) );
              exit( 1 );
            }
          l += blocks;

          /*this is for people who report that it takes vobcopy ages to copy something */
          /* TODO */


          if( l > 100 )
            {
              /*this is the progress indicator*/
              fprintf( stderr, "%4.0fMB of %4.0fMB written (%.0f %%)\r",
                       ( float ) offset/512,
                       ( float ) ( file_size_in_blocks - seek_start - stop_before_end )/512 ,
                       ( float ) ( offset*100 )/( file_size_in_blocks - seek_start - stop_before_end ) );
              l=0;
            }
        }
      if( !stdout_flag )
        {
          if( fsync( streamout ) < 0 )
            {
              fprintf( stderr, "\n[Error] error writing to %s \n", name );
              fprintf( stderr, "[Error] error: %s\n", strerror( errno ) );
              exit( 1 );
            }
//this is just so that it says 100% at the end. Dirty....
          fprintf( stderr, "%4.0fMB of %4.0fMB written ( 100.0 %)\r",
                   ( float ) offset/512,
                   ( float ) ( file_size_in_blocks - seek_start - stop_before_end )/512 );

          close( streamout );

          if( verbosity_level >= 1 )
            {
              fprintf( stderr,"[Info] max_filesize_in_blocks %8.0f \n", ( float ) max_filesize_in_blocks );
              fprintf( stderr,"[Info] offset at the end %8.0f \n", ( float ) offset );
              fprintf( stderr,"[Info] file_size_in_blocks %8.0f \n",( float ) file_size_in_blocks );
            }
          /* now lets see whats the size of this file in bytes */
          stat( name, &buf );
          disk_vob_size += ( off_t ) buf.st_size;


          if( large_file_flag && !cut_flag )
            {
              if( ( vob_size - disk_vob_size ) < MAX_DIFFER )
                {
                  re_name( name );
                }
              else
                {
                  fprintf( stderr, "\n[Error] File size (%.0f) of %s differs largely from that on dvd, therefore keeps it's .partial\n", ( float ) buf.st_size, name );
                }
            }
          else if( !cut_flag )
            {
              re_name( name );
              /* TODO: this check should go in again
              if( ( ( 1048571 * 2048 ) - disk_vob_size ) < MAX_DIFFER )
              {
              re_name( name );
              }
              else
              {
              fprintf( stderr, "\n[Error]File size (%.0f) of %s differs largely from 2GB, therefore keeps it's .partial\n", ( float ) buf.st_size, name );
              } */
            }

          else if( cut_flag )
            {
              re_name( name );
            }

          if( verbosity_level >= 1 )
            {
              fprintf( stderr, "[Info] Single file size (of copied file %s ) %.0f\n", name, ( float ) buf.st_size );
              fprintf( stderr, "[Info] Cumulated size %.0f\n", ( float ) disk_vob_size );
            }
        }
      max_filesize_in_blocks_summed += max_filesize_in_blocks;
      fprintf( stderr, "[Info] Successfully copied file %s\n", name );
      j++; 	/* # of seperate files we have written */
    }
  /*end of main copy loop*/


  if( verbosity_level >= 1 )
    fprintf( stderr, "[Info] # of separate files: %i\n", j );

  /*
   * clean up and close everything 
   */

  ifoClose( vts_file );
  ifoClose( vmg_file );
  DVDCloseFile( dvd_file );
  DVDClose( dvd );
  fprintf( stderr,"\n[Info] Copying finished! Let's see if the sizes match (roughly)\n" );
  fprintf( stderr,"[Info] Combined size of title-vobs: %.0f (%.0f MB)\n", ( float ) vob_size, ( float ) vob_size / ( 1024*1024 ) );
  fprintf( stderr,"[Info] Copied size (size on disk):  %.0f (%.0f MB)\n", ( float ) disk_vob_size, ( float ) disk_vob_size / ( 1024*1024 ) );

  if ( ( vob_size - disk_vob_size ) > MAX_DIFFER )
    {
      fprintf( stderr, "[Error] Hmm, the sizes differ by more than %d\n", MAX_DIFFER );
      fprintf( stderr, "[Hint] Take a look with MPlayer if the output is ok\n" );
    }
  else
    {
      fprintf( stderr, "[Info] Everything seems to be fine, the sizes match pretty good ;-)\n" );
      fprintf( stderr, "[Hint] Have a lot of fun!\n" );
    }

  return 0;
}
/*
 *this is the end of the main program, following are some useful functions
 * functions directly concerning the dvd have been moved to dvd.c
 */



/*
 * if you symlinked a dir to some other place the path name might not get
 * ended by a slash after the first tab press, therefore here is a / added
 * if necessary
 */

int add_end_slash( char *path )
{  /* add a trailing '/' to path */
  char *pointer;
  if ( path[strlen( path )-1] != '/' )
    {
      pointer = path + strlen( path );
      *pointer = '/';
      pointer++;
      *pointer = '\0';
    }
  return 0;
}

/*
 * get available space on target filesystem
 */

off_t get_free_space( char *path, int verbosity_level )
{

#ifdef USE_STATFS
  struct statfs     buf1;
#else
  struct statvfs    buf1;
#endif
  /*   ssize_t temp1, temp2; */
  long temp1, temp2;
  off_t sum;
#ifdef USE_STATFS
  statfs( path, &buf1 );
  if( verbosity_level >= 1 )
    fprintf( stderr, "[Info] Used the linux statfs\n" );
#else
  statvfs( path, &buf1 );
  if( verbosity_level >= 1 )
    fprintf( stderr, "[Info] Used statvfs\n" );
#endif
  temp1 = buf1.f_bavail;
  /* On Solaris at least, f_bsize is not the actual block size -- lb */
  /* There wasn't this ifdef below, strange! How could the linux statfs
   * handle this since there seems to be no frsize??! */
#ifdef USE_STATFS
  temp2 = buf1.f_bsize;
#else
  temp2 = buf1.f_frsize;
#endif
  sum = ( ( off_t )temp1 * ( off_t )temp2 );
  if( verbosity_level >= 1 )
    {
      fprintf( stderr, "[Info] In freespace_getter:for %s : %.0f free\n", path, ( float ) sum );
      fprintf( stderr, "[Info] In freespace_getter:bavail %ld * bsize %ld = above\n", temp1, temp2 );
    }
  /*   return ( buf1.f_bavail * buf1.f_bsize ); */
  return sum;
}


/*
 * get used space on dvd (for mirroring)
 */

off_t get_used_space( char *path, int verbosity_level )
{

#ifdef USE_STATFS
  struct statfs     buf2;
#else
  struct statvfs    buf2;
#endif
  /*   ssize_t temp1, temp2; */
  long temp1, temp2;
  off_t sum;
#ifdef USE_STATFS
  statfs( path, &buf2 );
  if( verbosity_level >= 1 )
    fprintf( stderr, "[Info] Used the linux statfs\n" );
#else
  statvfs( path, &buf2 );
  if( verbosity_level >= 1 )
    fprintf( stderr, "[Info] Used statvfs\n" );
#endif
  temp1 = buf2.f_blocks;
  /* On Solaris at least, f_bsize is not the actual block size -- lb */
  /* There wasn't this ifdef below, strange! How could the linux statfs
   * handle this since there seems to be no frsize??! */
#ifdef USE_STATFS
  temp2 = buf2.f_bsize;
#else
  temp2 = buf2.f_frsize;
#endif
  sum = ( ( off_t )temp1 * ( off_t )temp2 );
  if( verbosity_level >= 1 )
    {
      fprintf( stderr, "[Info] In usedspace_getter:for %s : %.0f used\n", path, ( float ) sum );
      fprintf( stderr, "[Info] In usedspace_getter:part1 %ld, part2 %ld\n", temp1, temp2 );
    }
  /*   return ( buf1.f_blocks * buf1.f_bsize ); */
  return sum;
}

/*
 * this function concatenates the given information into a path name
 */

int make_output_path( char *pwd,char *name,int get_dvd_name_return, char *dvd_name,int titleid, int partcount )
{
  char temp[5];
  int i;
  strcpy( name, pwd );
  if( !get_dvd_name_return )
    {
      strcat( name, dvd_name );
    }
  else
    strcat( name, "the_video_you_wanted" );

  sprintf( temp, "%d", titleid );
  strcat( name, temp );
  if( partcount >= 0 )
    {
      strcat( name, "-" );
      sprintf( temp, "%d", partcount );
      strcat( name, temp );
    }
  strcat( name, ".vob" );

  for( i=0; i< strlen(name);i++ )
    {
      if( name[i] == ' ')
        name[i] = '_';
    }


  fprintf( stderr, "\n[Info] Outputting to %s", name );
  return 0;
}

/*
 *The usage function
 */

void usage( char *program_name )
{
  fprintf( stderr, "Vobcopy "VERSION" - GPL Copyright (c) 2001 - 2007 robos@muon.de\n"
           "\nUsage: %s \n"
           "[-m (mirror)] \n"
           "[-i /path/to/the/mounted/dvd/]\n"
           "[-n title-number] \n"
           "[-t <your name for the dvd>] \n"
           "[-o /path/to/output-dir/ (can be \"stdout\" or \"-\")] \n"
           "[-f (force output)]\n"
           "[-V (version)]\n"
           "[-v (verbose)]\n"
           "[-v -v (create log-file)]\n"
           "[-h (this here ;-)] \n"
           "[-I (infos about title, chapters and angles on the dvd)]\n"
           "[-1/path/to/second/output/dir/] [-2/.../third/..] [-3/../] [-4 /../]\n"
           "[-b <skip-size-at-beginning[bkmg]>] \n"
           "[-e <skip-size-at-end[bkmg]>]\n"
           "[-O <single_file_name1,single_file_name2, ...>] \n"
           "[-q (quiet)]\n"
           "[-F <fast-factor:1..64>]\n", program_name );

#if defined( HAS_LARGEFILE )
  fprintf( stderr, "[-l (large-file support for files > 2GB)] \n" );
#endif
  exit( 1 );
}


/* from play_title */
/**
 * Returns true if the pack is a NAV pack.  This check is clearly insufficient,
 * and sometimes we incorrectly think that valid other packs are NAV packs.  I
 * need to make this stronger.
 */
int is_nav_pack( unsigned char *buffer )
{
  return ( buffer[ 41 ] == 0xbf && buffer[ 1027 ] == 0xbf );
}

/*
*Rename: move file name from bla.partial to bla or mark as dublicate 
*/

void re_name( char *output_file )
{
  char new_output_file[ 255 ];
  strcpy( new_output_file, output_file );
  new_output_file[ strlen( new_output_file ) - 8 ] = 0;

  if( ! link( output_file, new_output_file ) )
    {
      if( unlink( output_file ) )
        {
          fprintf( stderr, "[Error] Could not remove old filename: %s \n", output_file );
          fprintf( stderr, "[Hint] This: %s is a hardlink to %s. Dunno what to do... \n", new_output_file, output_file );
        }
      //            else
      //                fprintf( stderr, "[Info] Removed \".partial\" from %s since it got copied in full \n", output_file );
    }
  else
    {
      if( errno == EEXIST && ! overwrite_flag )
        {
          fprintf( stderr, "[Error] File %s already exists! Gonna name the new one %s.dupe \n", new_output_file, new_output_file );
          strcat( new_output_file, ".dupe" );
          rename( output_file,  new_output_file );
        }
      if( errno == EEXIST && overwrite_flag )
        {
          rename( output_file,  new_output_file );
        }

      if( errno == EPERM ) /*EPERM means that the filesystem doesn't allow hardlinks, e.g. smb */
        {
          /*this here is a stdio function which simply overwrites an existing file. Bad but I don't want to include another test...*/
          rename( output_file, new_output_file );
          //                fprintf( stderr, "[Info] Removed \".partial\" from %s since it got copied in full \n", output_file );
        }
    }
  /* test, test... */
  if( strstr( name, ".partial" ) )
    name[ strlen( name ) - 8 ] = 0;

}


/*
* Creates a directory with the given name, checking permissions and reacts accordingly (bails out or asks user)
*/

int makedir( char *name )
{
  if( mkdir( name, 0777 ) )
    {
      if( errno == EEXIST )
        {
          fprintf( stderr, "[Error] The directory %s\n already exists! \n", name );
          fprintf( stderr, "[Hint] You can either [c]ontinue writing to it or you can [q]uit: " );
          while ( 1 )
            {
              char op;
//                            op=fgetc( stdin );
              while ((op = fgetc (stdin)) == EOF)
                usleep (1);
              fgetc ( stdin ); /* probably need to do this for second time it
                                          comes around this loop */
              if( op == 'c' )
                {
                  return 0;
                }
              else if( op == 'q' )
                {
                  exit( 1 );
                }
              else
                {
                  fprintf( stderr, "\n[Hint] please choose [c]ontinue or [q]uit the next time ;-)\n" );
                }
            }

        }
      else /*most probably the user has no right to create dir or space if full or something */
        {
          fprintf( stderr, "[Error] Creating of directory %s\n failed! \n", name );
          fprintf( stderr, "[Error] error: %s\n",strerror( errno ) );
          exit( 1 );
        }
    }
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1