Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

wodim.c

/*
 * This file has been modified for the cdrkit suite.
 *
 * The behaviour and appearence of the program code below can differ to a major
 * extent from the version distributed by the original author(s).
 *
 * For details, see Changelog file distributed with the cdrkit package. If you
 * received this file from another source then ask the distributing person for
 * a log of modifications.
 *
 */

/*
 *
 * Modified by Eduard Bloch in 08/2006 and later
 */

/* @(#)cdrecord.c 1.310 06/02/09 Copyright 1995-2006 J. Schilling */
/*
 *    Record data on a CD/CVD-Recorder
 *
 *    Copyright (c) 1995-2006 J. Schilling
 */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program 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
 * this program; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <mconfig.h>
#include <stdio.h>
#include <standard.h>
#include <stdxlib.h>
#include <fctldefs.h>
#include <errno.h>
#include <timedefs.h>
#ifdef      HAVE_SYS_RESOURCE_H
#include <sys/resource.h>     /* for rlimit */
#endif
#include <statdefs.h>
#include <unixstd.h>
#ifdef      HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <strdefs.h>
#include <utypes.h>
#include <intcvt.h>
#include <signal.h>
#include <schily.h>
#include <string.h>
#include <getargs.h>
#ifdef      HAVE_PRIV_H
#include <priv.h>
#endif

#include "xio.h"

#include <usal/scsireg.h>     /* XXX wegen SC_NOT_READY */
#include <usal/scsitransp.h>
#include <usal/usalcmd.h>           /* XXX fuer read_buffer */
#include "scsi_scan.h"

#include "auheader.h"
#include "wodim.h"
#include "defaults.h"
#include "movesect.h"

#ifdef __linux__
#include <sys/capability.h>   /* for rawio capability */
#endif

#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0
#ifdef  HAVE_SYS_PRIOCNTL_H   /* The preferred SYSvR4 schduler */
#else
#define     USE_POSIX_PRIORITY_SCHEDULING
#endif
#endif

/*
 * Map toc/track types into names.
 */
char  *toc2name[] = {
            "CD-DA",
            "CD-ROM",
            "CD-ROM XA mode 1",
            "CD-ROM XA mode 2",
            "CD-I",
            "Illegal toc type 5",
            "Illegal toc type 6",
            "Illegal toc type 7",
};

/*
 * Map sector types into names.
 */
char  *st2name[] = {
            "Illegal sector type 0",
            "CD-ROM mode 1",
            "CD-ROM mode 2",
            "Illegal sector type 3",
            "CD-DA without preemphasis",
            "CD-DA with preemphasis",
            "Illegal sector type 6",
            "Illegal sector type 7",
};

/*
 * Map data block types into names.
 */
char  *db2name[] = {
            "Raw (audio)",
            "Raw (audio) with P/Q sub channel",
            "Raw (audio) with P/W packed sub channel",
            "Raw (audio) with P/W raw sub channel",
            "Reserved mode 4",
            "Reserved mode 5",
            "Reserved mode 6",
            "Vendor unique mode 7",
            "CD-ROM mode 1",
            "CD-ROM mode 2",
            "CD-ROM XA mode 2 form 1",
            "CD-ROM XA mode 2 form 1 (with subheader)",
            "CD-ROM XA mode 2 form 2",
            "CD-ROM XA mode 2 form 1/2/mix",
            "Reserved mode 14",
            "Vendor unique mode 15",
};

/*
 * Map write modes into names.
 */
static      char  wm_none[] = "unknown";
static      char  wm_ill[]  = "illegal";

char  *wm2name[] = {
            wm_none,
            "BLANK",
            "FORMAT",
            wm_ill,
            "PACKET",
            wm_ill,
            wm_ill,
            wm_ill,
            "TAO",
            wm_ill,
            wm_ill,
            wm_ill,
            "SAO",
            "SAO/RAW16",      /* Most liklely not needed */
            "SAO/RAW96P",
            "SAO/RAW96R",
            "RAW",
            "RAW/RAW16",
            "RAW/RAW96P",
            "RAW/RAW96R",
};

int         debug;            /* print debug messages */
static      int   kdebug;           /* print kernel debug messages */
static      int   scsi_verbose;     /* SCSI verbose flag */
static      int   silent;           /* SCSI silent flag */
int         lverbose;   /* static verbose flag */
int         xdebug;           /* extended debug flag */

char  *buf;             /* The transfer buffer */
long  bufsize = -1;           /* The size of the transfer buffer */

static      int   gracetime = GRACE_TIME;
static      int   raw_speed = -1;
static      int   dma_speed = -1;
static      int   dminbuf = -1;     /* XXX Hack for now drive min buf fill */
BOOL  isgui;
static      int   didintr;
char  *driveropts;
static      char  *cuefilename = NULL;
static      uid_t oeuid = (uid_t)-1;

struct timeval    starttime;
struct timeval    wstarttime;
struct timeval    stoptime;
struct timeval    fixtime;

static      long  fs = -1;    /* fifo (ring buffer) size */
static Llong warn_minisize = -1L;

static      int   gracewait(cdr_t *dp, BOOL *didgracep);
static      void  cdrstats(cdr_t *dp);
static      void  susage(int);
static      void  usage(int);
static      void  blusage(int);
static      void  formattypeusage(int);
static      void  intr(int sig);
static      void  catchsig(int sig);
static      int   scsi_cb(void *arg);
static      void  intfifo(int sig);
static      void  exscsi(int excode, void *arg);
static      void  excdr(int excode, void *arg);
int   read_buf(int f, char *bp, int size);
int   fill_buf(int f, track_t *trackp, long secno, char *bp, int size);
int   get_buf(int f, track_t *trackp, long secno, char **bpp, int size);
int   write_secs(SCSI *usalp, cdr_t *dp, char *bp, long startsec, int bytespt,
                                int secspt, BOOL islast);
static      int   write_track_data(SCSI *usalp, cdr_t *, track_t *);
int   pad_track(SCSI *usalp, cdr_t *dp, track_t *trackp, long startsec, 
                               Llong amt, BOOL dolast, Llong *bytesp);
int   write_buf(SCSI *usalp, cdr_t *dp, track_t *trackp, char *bp, 
                               long startsec, Llong amt, int secsize, BOOL dolast, 
                               Llong *bytesp);
static      void  printdata(int, track_t *);
static      void  printaudio(int, track_t *);
static      void  checkfile(int, track_t *);
static      int   checkfiles(int, track_t *);
static      void  setleadinout(int, track_t *);
static      void  setpregaps(int, track_t *);
static      long  checktsize(int, track_t *);
static      void  opentracks(track_t *);
static      void  checksize(track_t *);
static      BOOL  checkdsize(SCSI *usalp, cdr_t *dp, long tsize, int flags);
static      void  raise_fdlim(void);
static      void  raise_memlock(void);
static      int   gargs(int, char **, int *, track_t *, char **, int *, cdr_t **,
                                          int *, long *, int *, int *);
static      void  set_trsizes(cdr_t *, int, track_t *);
void        load_media(SCSI *usalp, cdr_t *, BOOL);
void        unload_media(SCSI *usalp, cdr_t *, int);
void        reload_media(SCSI *usalp, cdr_t *);
void        set_secsize(SCSI *usalp, int secsize);
static      int   get_dmaspeed(SCSI *usalp, cdr_t *);
static      BOOL  do_opc(SCSI *usalp, cdr_t *, int);
static      void  check_recovery(SCSI *usalp, cdr_t *, int);
void        audioread(SCSI *usalp, cdr_t *, int);
static      void  print_msinfo(SCSI *usalp, cdr_t *);
static      void  print_toc(SCSI *usalp, cdr_t *);
static      void  print_track(int, long, struct msf *, int, int, int);
#if !defined(HAVE_SYS_PRIOCNTL_H)
static      int   rt_raisepri(int);
#endif
void        raisepri(int);
static      void  wait_input(void);
static      void  checkgui(void);
static      int   getbltype(char *optstr, long *typep);
static      int   getformattype(char *optstr, long *typep);
static      void  print_drflags(cdr_t *dp);
static      void  print_wrmodes(cdr_t *dp);
static      BOOL  check_wrmode(cdr_t *dp, int wmode, int tflags);
static      void  set_wrmode(cdr_t *dp, int wmode, int tflags);
static      void  linuxcheck(void);

#ifdef __linux__
static int get_cap(cap_value_t cap_array);
#endif

struct exargs {
      SCSI  *usalp;
      cdr_t *dp;
      int   old_secsize;
      int   flags;
      int   exflags;
} exargs;

void fifo_cleanup(void) {
   kill_faio();
}

/* shared variables */
int   scandevs = 0;
char  *msifile = NULL;

int main(int argc, char *argv[])
{
      char  *dev = NULL;
      int   timeout = 40;     /* Set default timeout to 40s CW-7502 is slow*/
      int   speed = -1;
      long  flags = 0L;
      int   blanktype = 0;
      int   formattype = 0;
      int   i;
      int   tracks = 0;
      int   trackno;
      long  tsize;
      track_t     track[MAX_TRACK+2];     /* Max tracks + track 0 + track AA */
      cdr_t *dp = (cdr_t *)0;
      long  startsec = 0L;
      int   errs = 0;
      SCSI  *usalp = NULL;
      char  errstr[80];
      BOOL  gracedone = FALSE;
      int     ispacket;
      BOOL  is_cdwr = FALSE;
      BOOL  is_dvdwr = FALSE;

      buf=strstr(argv[0], "cdrecord");
      if(buf && '\0' == buf[8]) /* lame cheater detected */
            argv[0]="wodim";

#ifdef __EMX__
      /* This gives wildcard expansion with Non-Posix shells with EMX */
      _wildcard(&argc, &argv);
#endif
      save_args(argc, argv);
      oeuid = geteuid();            /* Remember saved set uid     */

      fillbytes(track, sizeof (track), '\0');
      for (i = 0; i < MAX_TRACK+2; i++)
            track[i].track = track[i].trackno = i;
      track[0].tracktype = TOC_MASK;
      raise_fdlim();
      ispacket = gargs(argc, argv, &tracks, track, &dev, &timeout, &dp, &speed, &flags,
                                          &blanktype, &formattype);
      if ((track[0].tracktype & TOC_MASK) == TOC_MASK)
            comerrno(EX_BAD, "Internal error: Bad TOC type.\n");

      if (flags & F_VERSION) {
         fprintf(stderr,
                   "Cdrecord-yelling-line-to-tell-frontends-to-use-it-like-version 2.01.01a03-dvd \n"
             "Wodim " CDRKIT_VERSION "\n"
             "Copyright (C) 2006 Cdrkit suite contributors\n"
             "Based on works from Joerg Schilling, Copyright (C) 1995-2006, J. Schilling\n"
             );
         exit(0);
      }

      checkgui();

      if (debug || lverbose) {
            printf("TOC Type: %d = %s\n",
                  track[0].tracktype & TOC_MASK,
                  toc2name[track[0].tracktype & TOC_MASK]);
      }

      if ((flags & (F_MSINFO|F_TOC|F_PRATIP|F_FIX|F_VERSION|F_CHECKDRIVE|F_INQUIRY|F_SCANBUS|F_RESET)) == 0) {
            /*
             * Try to lock us im memory (will only work for root)
             * but you need access to root anyway to send SCSI commands.
             * We need to be root to open /dev/usal? or similar devices
             * on other OS variants and we need to be root to be able
             * to send SCSI commands at least on AIX and
             * Solaris (USCSI only) regardless of the permissions for
             * opening files
             *
             * XXX The following test used to be
             * XXX #if defined(HAVE_MLOCKALL) || defined(_POSIX_MEMLOCK)
             * XXX but the definition for _POSIX_MEMLOCK did change during
             * XXX the last 8 years and the autoconf test is better for
             * XXX the static case. sysconf() only makes sense if we like
             * XXX to check dynamically.
             */
            raise_memlock();
#if defined(HAVE_MLOCKALL)
            /*
             * XXX mlockall() needs root privilleges.
             */
            if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
                  if(lverbose>2)
                        fprintf(stderr,
                                    "W: Cannot do mlockall(2). Possibly increased risk for buffer underruns.\n");
            }
#endif

            /*
             * XXX raisepri() needs root privilleges.
             */
            raisepri(0); /* max priority */
            /*
             * XXX shmctl(id, SHM_LOCK, 0) needs root privilleges.
             * XXX So if we use SysV shared memory, wee need to be root.
             *
             * Note that not being able to set up a FIFO bombs us
             * back to the DOS ages. Trying to run cdrecord without
             * root privillegs is extremely silly, it breaks most
             * of the advanced features. We need to be at least installed
             * suid root or called by RBACs pfexec.
             */
            init_fifo(fs);    /* Attach shared memory (still one process) */
      }

      if ((flags & F_WAITI) != 0) {
            if (lverbose)
                  printf("Waiting for data on stdin...\n");
            wait_input();
      }

      /*
       * Call usal_remote() to force loading the remote SCSI transport library
       * code that is located in librusal instead of the dummy remote routines
       * that are located inside libusal.
       */
      usal_remote();
      if (dev != NULL &&
          ((strncmp(dev, "HELP", 4) == 0) ||
          (strncmp(dev, "help", 4) == 0))) {
            usal_help(stderr);
            exit(0);
      }

      if( (!dev || *dev=='\0'|| 0==strcmp(dev, "-1")) && (flags & F_SCANBUS)==0 ) {
            int64_t need_size=0L;
            struct stat statbuf;
            int t;

            fprintf(stderr, "Device was not specified. Trying to find an appropriate drive...\n");

            /* estimate how much data user wants to write */
            for(t=1;t<=tracks;t++) { 
                  if(track[t].tracksize>=0)
                        need_size+=track[t].tracksize;
                  else if(0==stat(track[t].filename, &statbuf))
                        need_size+=statbuf.st_size;
            }
            usalp=open_auto(need_size, debug, lverbose);
      }

      if(!usalp)
            usalp = usal_open(dev, errstr, sizeof(errstr), debug, lverbose);

      if(!usalp)
      {
            errmsg("\nCannot open SCSI driver!\n"
                        "For possible targets try 'wodim --devices' or 'wodim -scanbus'.\n"
                        "For possible transport specifiers try 'wodim dev=help'.\n"
                        "For IDE/ATAPI devices configuration, see the file README.ATAPI.setup from\n"
                        "the wodim documentation.\n");
            exit(EX_BAD);
      }

#ifdef      HAVE_PRIV_SET
#ifdef      PRIV_DEBUG
      fprintf(stderr, "file_dac_read: %d\n", priv_ineffect(PRIV_FILE_DAC_READ));
#endif
      /*
       * Give up privs we do not need anymore.
       * We no longer need:
       *    file_dac_read,proc_lock_memory,proc_priocntl,net_privaddr
       * We still need:
       *    sys_devices
       */
      priv_set(PRIV_OFF, PRIV_EFFECTIVE,
            PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
            PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL);
      priv_set(PRIV_OFF, PRIV_PERMITTED,
            PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
            PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL);
      priv_set(PRIV_OFF, PRIV_INHERITABLE,
            PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
            PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL);
#endif
      /*
       * This is only for OS that do not support fine grained privs.
       *
       * XXX Below this point we do not need root privilleges anymore.
       */
      if (geteuid() != getuid()) {  /* AIX does not like to do this */
                              /* If we are not root         */
#ifdef      HAVE_SETREUID
            if (setreuid(-1, getuid()) < 0)
#else
#ifdef      HAVE_SETEUID
            if (seteuid(getuid()) < 0)
#else
            if (setuid(getuid()) < 0)
#endif
#endif
                  comerr("Panic cannot set back effective uid.\n");
      }

#ifdef __linux__
      /* get the rawio capability */
      if (get_cap(CAP_SYS_RAWIO) && (debug || lverbose>2)) 
      {
            perror("Warning: Cannot gain SYS_RAWIO capability");
            fprintf(stderr, "Possible reason: wodim not installed SUID root.\n");
      }
#endif

      /*
       * WARNING: We now are no more able to do any privilleged operation
       * unless we have been called by root.
       *
       * XXX It may be that we later get problems in init_faio() because
       * XXX this function calls raisepri() to lower the priority slightly.
       */
      usal_settimeout(usalp, timeout);
      usalp->verbose = scsi_verbose;
      usalp->silent = silent;
      usalp->debug = debug;
      usalp->kdebug = kdebug;
      usalp->cap->c_bsize = DATA_SEC_SIZE;

      if ((flags & F_MSINFO) == 0 || lverbose) {
            char  *vers;
            char  *auth;


            if(lverbose)
                  fprintf(stderr, "Wodim version: " CDRKIT_VERSION "\n");

            vers = usal_version(0, SCG_VERSION);
            auth = usal_version(0, SCG_AUTHOR);
            if(lverbose >1 && auth && vers)
                  fprintf(stderr, "Using libusal version '%s-%s'.\n", auth, vers);


            vers = usal_version(usalp, SCG_RVERSION);
            auth = usal_version(usalp, SCG_RAUTHOR);
            if (lverbose > 1 && vers && auth)
                  fprintf(stderr, "Using remote transport code version '%s-%s'\n", auth, vers);
      }

      if (lverbose && driveropts)
            printf("Driveropts: '%s'\n", driveropts);

/*    bufsize = usal_bufsize(usalp, CDR_BUF_SIZE);*/
      bufsize = usal_bufsize(usalp, bufsize);
      if (lverbose || debug)
            fprintf(stderr, "SCSI buffer size: %ld\n", bufsize);
      if ((buf = usal_getbuf(usalp, bufsize)) == NULL)
            comerr("Cannot get SCSI I/O buffer.\n");

      if (scandevs)
            return (list_devices(usalp, stdout, 0));

      if ((flags & F_SCANBUS) != 0) {
            select_target(usalp, stdout);
            exit(0);
      }
      if ((flags & F_RESET) != 0) {
            if (usal_reset(usalp, SCG_RESET_NOP) < 0)
                  comerr("Cannot reset (OS does not implement reset).\n");
            if (usal_reset(usalp, SCG_RESET_TGT) >= 0)
                  exit(0);
            if (usal_reset(usalp, SCG_RESET_BUS) < 0)
                  comerr("Cannot reset target.\n");
            exit(0);
      }

      /*
       * First try to check which type of SCSI device we
       * have.
      if (debug || lverbose)
            printf("atapi: %d\n", usal_isatapi(usalp));
       */
      usalp->silent++;
      test_unit_ready(usalp); /* eat up unit attention */
      usalp->silent--;
      if (!do_inquiry(usalp, (flags & F_MSINFO) == 0 || lverbose)) {
            errmsgno(EX_BAD, "Cannot do inquiry for CD/DVD-Recorder.\n");
            if (unit_ready(usalp))
                  errmsgno(EX_BAD, "The unit seems to be hung and needs power cycling.\n");
            exit(EX_BAD);
      }
#ifdef      GCONF
      /*
       * Debug only
       */
      {
            extern      void  gconf(SCSI *);

            if (lverbose > 2)
                  gconf(usalp);
      }
#endif

      if ((flags & F_PRCAP) != 0) {
            print_capabilities(usalp);
            print_capabilities_mmc4(usalp);
            exit(0);
      }
      if ((flags & F_INQUIRY) != 0)
            exit(0);

      if (dp == (cdr_t *)NULL) {    /* No driver= option specified      */
            dp = get_cdrcmds(usalp);      /* Calls dp->cdr_identify()   */
      }
      else if (!is_unknown_dev(usalp) && dp != get_cdrcmds(usalp)) {
            errmsgno(EX_BAD, "WARNING: Trying to use other driver on known device.\n");
      }
        is_mmc(usalp, &is_cdwr, &is_dvdwr);
      if (ispacket) {
            if (is_dvdwr) {
                  track[0].flags |= TI_PACKET; 
                  /*XXX put here to only affect DVD writing, should be in gargs.
                   * however if set in args for all mode, packet writing is then
                   * broken for all disc as cdrecord assume that PACKET imply TAO which  
                   * is not true at all???? */ 
                  track[0].flags &= ~TI_TAO;
            }
      }

      if (dp == (cdr_t *)0)
            comerrno(EX_BAD, "Sorry, no supported CD/DVD-Recorder found on this target.\n");

      /* DVD does not support TAO */
      if (dp->is_dvd) {
        if(lverbose>1)
            fprintf(stderr, "Using Session At Once (SAO) for DVD mode.\n");
        dp->cdr_flags |= F_SAO;
        for (i = 0; i <= MAX_TRACK; i++) {
            track[i].flags &= ~TI_TAO;
            track[i].flags |= TI_SAO;
        }
      }

      if (!is_cddrive(usalp))
            comerrno(EX_BAD, "Sorry, no CD/DVD-Drive found on this target.\n");
      /*
       * The driver is known, set up data structures...
       */
      {
            cdr_t *ndp;
            dstat_t     *dsp;

            ndp = malloc(sizeof (cdr_t));
            dsp = malloc(sizeof (dstat_t));
            if (ndp == NULL || dsp == NULL)
                  comerr("Cannot allocate memory for driver structure.\n");
            movebytes(dp, ndp, sizeof (cdr_t));
            dp = ndp;
            dp->cdr_flags |= CDR_ALLOC;
            dp->cdr_cmdflags = flags;

            fillbytes(dsp, sizeof (*dsp), '\0');
            dsp->ds_minbuf = 0xFFFF;
            dp->cdr_dstat = dsp;
      }

      if ((flags & (F_MSINFO|F_TOC|F_LOAD|F_DLCK|F_EJECT)) == 0 ||
                  tracks > 0 ||
                  cuefilename != NULL)
      {
            if ((dp->cdr_flags & CDR_ISREADER) != 0) {
                  errmsgno(EX_BAD,
                              "Sorry, no CD/DVD-Recorder or unsupported CD/DVD-Recorder found on this target.\n");
            }

            if (!is_mmc(usalp, &is_cdwr, &is_dvdwr))
                  is_cdwr = TRUE;               /* If it is not MMC, it must be a CD writer */

            if (is_dvdwr && !set_cdrcmds("mmc_mdvd", (cdr_t **)NULL)) {
                  errmsgno(EX_BAD,
                              "Internal error, DVD driver failure. Please report to debburn-devel@lists.alioth.debian.org.\n");
            }
            /*
             * Only exit if this is not the ProDVD test binary.
             */
            if (!is_cdwr)
                  exit(EX_BAD);
      }

      /*
       * Set up data structures for current drive state.
       */
      if ((*dp->cdr_attach)(usalp, dp) != 0)
            comerrno(EX_BAD, "Cannot attach driver for CD/DVD-Recorder.\n");

      if (lverbose > 1) {
            printf("Drive current speed: %d\n", dp->cdr_dstat->ds_dr_cur_wspeed);
            printf("Drive default speed: %d\n", dp->cdr_speeddef);
            printf("Drive max speed    : %d\n", dp->cdr_speedmax);
      }
      if (speed > (int)dp->cdr_speedmax && (flags & F_FORCE) == 0)
            speed = dp->cdr_speedmax;
      if (speed < 0)
            speed = dp->cdr_speeddef;

      if (lverbose > 1) {
            printf("Selected speed     : %d\n", speed);
      }
      dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */

      exargs.usalp         = usalp;
      exargs.dp      = dp;
      exargs.old_secsize = -1;
      exargs.flags         = flags;

      if ((flags & F_MSINFO) == 0 || lverbose) {
            printf("Using %s (%s).\n", dp->cdr_drtext, dp->cdr_drname);
            print_drflags(dp);
            print_wrmodes(dp);
      }
      usalp->silent++;
      if ((debug || lverbose)) {
            tsize = -1;
            if ((*dp->cdr_buffer_cap)(usalp, &tsize, (long *)0) < 0 || tsize < 0) {
                  if (read_buffer(usalp, buf, 4, 0) >= 0)
                        tsize = a_to_u_4_byte(buf);
            }
            if (tsize > 0) {
                  printf("Drive buf size : %lu = %lu KB\n",
                                    tsize, tsize >> 10);
            }
      }
      usalp->silent--;

      dma_speed = get_dmaspeed(usalp, dp);

      if ((debug || lverbose) && dma_speed > 0) {
            /*
             * We do not yet know what medium type is in...
             */
            printf("Drive DMA Speed: %d kB/s %dx CD %dx DVD\n",
                  dma_speed, dma_speed/176, dma_speed/1385);
      }
      if ((tracks > 0 || cuefilename != NULL) && (debug || lverbose))
            printf("FIFO size      : %lu = %lu KB\n", fs, fs >> 10);

#ifdef      HAVE_LIB_EDC_ECC
      if ((flags & F_RAW) != 0 && (dp->cdr_dstat->ds_flags & DSF_DVD) == 0)
            raw_speed = encspeed(debug || lverbose);
#endif

      if ((flags & F_CHECKDRIVE) != 0)
            exit(0);

      if ((flags & F_ABORT) != 0) {
            /*
             * flush cache is not supported by CD-ROMs avoid prob with -toc
             */
            usalp->silent++;
            scsi_flush_cache(usalp, FALSE);
            (*dp->cdr_abort_session)(usalp, dp);
            usalp->silent--;
            exit(0);
      }

      if (tracks == 0 && cuefilename == NULL &&
          (flags & (F_FIX|F_BLANK)) == 0 && (flags & F_EJECT) != 0) {
            /*
             * Do not check if the unit is ready here to allow to open
             * an empty unit too.
             */
            unload_media(usalp, dp, flags);
            exit(0);
      }
      flush();

      if (cuefilename) {
            parsecue(cuefilename, track);
            tracks = track[0].tracks;
      } else {
            opentracks(track);
      }

      if (tracks > 1)
            sleep(2);   /* Let the user watch the inquiry messages */

      if (tracks > 0 && !check_wrmode(dp, flags, track[1].flags))
            comerrno(EX_BAD, "Illegal write mode for this drive.\n");

      if ((track[0].flags & TI_TEXT) == 0 &&    /* CD-Text not yet processed */
          (track[MAX_TRACK+1].flags & TI_TEXT) != 0) {
            /*
             * CD-Text from textfile= or from CUE CDTEXTFILE will win
             * over CD-Text from *.inf files and over CD-Text from
             * CUE SONGWRITER, ...
             */
            packtext(tracks, track);
            track[0].flags |= TI_TEXT;
      }
#ifdef      CLONE_WRITE
      if (flags & F_CLONE) {
            clone_toc(track);
            clone_tracktype(track);
      }
#endif
      setleadinout(tracks, track);
      set_trsizes(dp, tracks, track);
      setpregaps(tracks, track);
      checkfiles(tracks, track);
      tsize = checktsize(tracks, track);

      /*
       * Make wm2name[wrmode] work.
       * This must be done after the track flags have been set up
       * by the functions above.
       */
      if (tracks == 0 && (flags & F_BLANK) != 0)
            dp->cdr_dstat->ds_wrmode = WM_BLANK;
      else if (tracks == 0 && (flags & F_FORMAT) != 0)
            dp->cdr_dstat->ds_wrmode = WM_FORMAT;
      else
            set_wrmode(dp, flags, track[1].flags);

      /*
       * Debug only
       */
      {
            void  *cp = NULL;

            (*dp->cdr_gen_cue)(track, &cp, FALSE);
            if (cp)
                  free(cp);
      }

      /*
       * Create Lead-in data. Only needed in RAW mode.
       */
      do_leadin(track);


      /*
       * Install exit handler before we change the drive status.
       */
      on_comerr(exscsi, &exargs);

      if ((flags & F_FORCE) == 0)
            load_media(usalp, dp, TRUE);

      if ((flags & (F_LOAD|F_DLCK)) != 0) {
            if ((flags & F_DLCK) == 0) {
                  usalp->silent++;        /* silently       */
                  scsi_prevent_removal(
                        usalp, 0);  /* allow manual open    */
                  usalp->silent--;        /* if load failed...    */
            }
            exit(0);                /* we did not change status */
      }
      exargs.old_secsize = sense_secsize(usalp, 1);
      if (exargs.old_secsize < 0)
            exargs.old_secsize = sense_secsize(usalp, 0);
      if (debug)
            printf("Current Secsize: %d\n", exargs.old_secsize);
      usalp->silent++;
      if (read_capacity(usalp) < 0) {
            if (exargs.old_secsize > 0)
                  usalp->cap->c_bsize = exargs.old_secsize;
      }
      usalp->silent--;
      if (exargs.old_secsize < 0)
            exargs.old_secsize = usalp->cap->c_bsize;
      if (exargs.old_secsize != usalp->cap->c_bsize)
            errmsgno(EX_BAD, "Warning: blockdesc secsize %d differs from cap secsize %d\n",
                        exargs.old_secsize, usalp->cap->c_bsize);

      if (lverbose)
            printf("Current Secsize: %d\n", exargs.old_secsize);

      if (exargs.old_secsize > 0 && exargs.old_secsize != DATA_SEC_SIZE) {
            /*
             * Some drives (e.g. Plextor) don't like to write correctly
             * in SAO mode if the sector size is set to 512 bytes.
             * In addition, wodim -msinfo will not work properly
             * if the sector size is not 2048 bytes.
             */
            set_secsize(usalp, DATA_SEC_SIZE);
      }

      /*
       * Is this the right place to do this ?
       */
      check_recovery(usalp, dp, flags);

/*audioread(dp, flags);*/
/*unload_media(usalp, dp, flags);*/
/*return 0;*/
      if (flags & F_WRITE)
            dp->cdr_dstat->ds_cdrflags |= RF_WRITE;
      if (flags & F_BLANK)
            dp->cdr_dstat->ds_cdrflags |= RF_BLANK;
      if (flags & F_PRATIP || lverbose > 0) {
            dp->cdr_dstat->ds_cdrflags |= RF_PRATIP;
      }
      if (flags & F_IMMED || dminbuf > 0) {
            if (dminbuf <= 0)
                  dminbuf = 50;
            if (lverbose <= 0)      /* XXX Hack needed for now */
                  lverbose++;
            dp->cdr_dstat->ds_cdrflags |= RF_WR_WAIT;
      }
      if ((*dp->cdr_getdisktype)(usalp, dp) < 0) {
            errmsgno(EX_BAD, "Cannot get disk type.\n");
            if ((flags & F_FORCE) == 0)
                  comexit(EX_BAD);
      }
      if (flags & F_PRATIP) {
            comexit(0);
      }
      /*
       * The next actions should depend on the disk type.
       */
      if (dma_speed > 0) {
            if ((dp->cdr_dstat->ds_flags & DSF_DVD) == 0)
                  dma_speed /= 176;
            else
                  dma_speed /= 1385;
      }

      /*
       * Init drive to default modes:
       *
       * We set TAO unconditionally to make checkdsize() work
       * currectly in SAO mode too.
       *
       * At least MMC drives will not return the next writable
       * address we expect when the drive's write mode is set
       * to SAO. We need this address for mkisofs and thus
       * it must be the first user accessible sector and not the
       * first sector of the pregap.
       *
       * XXX The ACER drive:
       * XXX Vendor_info    : 'ATAPI   '
       * XXX Identifikation : 'CD-R/RW 8X4X32  '
       * XXX Revision       : '5.EW'
       * XXX Will not return from -dummy to non-dummy without
       * XXX opening the tray.
       */
      usalp->silent++;
      if ((*dp->cdr_init)(usalp, dp) < 0)
            comerrno(EX_BAD, "Cannot init drive.\n");
      usalp->silent--;

      if (flags & F_SETDROPTS) {
            /*
             * Note that the set speed function also contains
             * drive option processing for speed related drive options.
             */
            if ((*dp->cdr_opt1)(usalp, dp) < 0) {
                  errmsgno(EX_BAD, "Cannot set up 1st set of driver options.\n");
            }
            if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0) {
                  errmsgno(EX_BAD, "Cannot set speed/dummy.\n");
            }
            dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */
            if ((*dp->cdr_opt2)(usalp, dp) < 0) {
                  errmsgno(EX_BAD, "Cannot set up 2nd set of driver options.\n");
            }
            comexit(0);
      }
      /*
       * XXX If dp->cdr_opt1() ever affects the result for
       * XXX the multi session info we would need to move it here.
       */
      if (flags & F_MSINFO) {
            print_msinfo(usalp, dp);
            comexit(0);
      }
      if (flags & F_TOC) {
            print_toc(usalp, dp);
            comexit(0);
      }
#ifdef      XXX
      if ((*dp->cdr_check_session)() < 0) {
            comexit(EX_BAD);
      }
#endif
      {
            Int32_t omb = dp->cdr_dstat->ds_maxblocks;

            if ((*dp->cdr_opt1)(usalp, dp) < 0) {
                  errmsgno(EX_BAD, "Cannot set up 1st set of driver options.\n");
            }
            if (tsize > 0 && omb != dp->cdr_dstat->ds_maxblocks) {
                  printf("Disk size changed by user options.\n");
                  printf("Checking disk capacity according to new values.\n");
            }
      }
      if (tsize == 0) {
            if (tracks > 0) {
                  errmsgno(EX_BAD,
                  "WARNING: Total disk size unknown. Data may not fit on disk.\n");
            }
      } else if (tracks > 0) {
            /*
             * XXX How do we let the user check the remaining
             * XXX disk size witout starting the write process?
             */
            if (!checkdsize(usalp, dp, tsize, flags))
                  comexit(EX_BAD);
      }
      if (tracks > 0 && fs > 0l) {
#if defined(USE_POSIX_PRIORITY_SCHEDULING) && defined(HAVE_SETREUID)
            /*
             * Hack to work around the POSIX design bug in real time
             * priority handling: we need to be root even to lower
             * our priority.
             * Note that we need to find a more general way that works
             * even on OS that do not support getreuid() which is *BSD
             * and SUSv3 only.
             */
            if (oeuid != getuid()) {
                  if (setreuid(-1, oeuid) < 0)
                        errmsg("Could set back effective uid.\n");
            }

#endif
            /*
             * fork() here to start the extra process needed for
             * improved buffering.
             */
            if (!init_faio(track, bufsize))
                  fs = 0L;
            else
                  on_comerr(excdr, &exargs);

    atexit(fifo_cleanup);

#if defined(USE_POSIX_PRIORITY_SCHEDULING) && defined(HAVE_SETREUID)
            /*
             * XXX Below this point we never need root privilleges anymore.
             */
            if (geteuid() != getuid()) {  /* AIX does not like to do this */
                                    /* If we are not root         */
                  if (setreuid(-1, getuid()) < 0)
                        comerr("Panic cannot set back effective uid.\n");
            }
#ifdef __linux__
            if (get_cap(CAP_SYS_RAWIO) && (debug || lverbose>2))
                  perror("Error: Cannot gain SYS_RAWIO capability, is wodim installed SUID root? Reason");
#endif

#endif
      }

      if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0) {
            errmsgno(EX_BAD, "Cannot set speed/dummy.\n");
            if ((flags & F_FORCE) == 0)
                  comexit(EX_BAD);
      }
      dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */
      if ((flags & F_WRITE) != 0 && raw_speed >= 0) {
            int   max_raw = (flags & F_FORCE) != 0 ? raw_speed:raw_speed/2;

            if (getenv("CDR_FORCERAWSPEED"))
                  max_raw = raw_speed;

            for (i = 1; i <= MAX_TRACK; i++) {
                  /*
                   * Check for Clone tracks
                   */
                  if ((track[i].sectype & ST_MODE_RAW) != 0)
                        continue;
                  /*
                   * Check for non-data tracks
                   */
                  if ((track[i].sectype & ST_MODE_MASK) == ST_MODE_AUDIO)
                        continue;

                  if (speed > max_raw) {
                        errmsgno(EX_BAD,
                        "Processor too slow. Cannot write RAW data at speed %d.\n",
                        speed);
                        comerrno(EX_BAD, "Max RAW data speed on this processor is %d.\n",
                        max_raw);
                  }
                  break;
            }
      }
      if (tracks > 0 && (flags & F_WRITE) != 0 && dma_speed > 0) {
            int max_dma = (dma_speed+1)*4/5; /* use an empirical formula to estimate available bandwith */

        if((flags & F_FORCE) != 0 || getenv("CDR_FORCESPEED"))
                  max_dma = dma_speed;

            if (speed > max_dma) {
                  errmsgno(EX_BAD,
                  "DMA speed too slow (OK for %dx). Cannot write at speed %dx.\n",
                              max_dma, speed);
                  if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) {
                        errmsgno(EX_BAD, "Max DMA data speed is %d.\n", max_dma);
                        comerrno(EX_BAD, "Try to use 'driveropts=burnfree'.\n");
                  }
            }
      }
      if ((flags & (F_WRITE|F_BLANK)) != 0 &&
                        (dp->cdr_dstat->ds_flags & DSF_ERA) != 0) {
            if (xdebug) {
                  printf("Current speed %d, medium low speed: %d medium high speed: %d\n",
                        speed,
                        dp->cdr_dstat->ds_at_min_speed,
                        dp->cdr_dstat->ds_at_max_speed);
            }
            if (dp->cdr_dstat->ds_at_max_speed > 0 &&
                        speed <= 8 &&
                        speed > (int)dp->cdr_dstat->ds_at_max_speed) {
                  /*
                   * Be careful here: 10x media may be written faster.
                   * The current code will work as long as there is no
                   * writer that can only write faster than 8x
                   */
                  if ((flags & F_FORCE) == 0) {
                        errmsgno(EX_BAD,
                        "Write speed %d of medium not sufficient for this writer.\n",
                              dp->cdr_dstat->ds_at_max_speed);
                        comerrno(EX_BAD,
                        "You may have used an ultra low speed medium on a high speed writer.\n");
                  }
            }

            if ((dp->cdr_dstat->ds_flags & DSF_ULTRASPP_ERA) != 0 &&
                (speed < 16 || (dp->cdr_cdrw_support & CDR_CDRW_ULTRAP) == 0)) {
                  if ((dp->cdr_cdrw_support & CDR_CDRW_ULTRAP) == 0) {
                        comerrno(EX_BAD,
                        "Trying to use ultra high speed+ medium on a writer which is not\ncompatible with ultra high speed+ media.\n");
                  } else if ((flags & F_FORCE) == 0) {
                        comerrno(EX_BAD,
                        "Probably trying to use ultra high speed+ medium on improper writer.\n");
                  }
            } else if ((dp->cdr_dstat->ds_flags & DSF_ULTRASP_ERA) != 0 &&
                (speed < 16 || (dp->cdr_cdrw_support & CDR_CDRW_ULTRA) == 0)) {
                  if ((dp->cdr_cdrw_support & CDR_CDRW_ULTRA) == 0) {
                        comerrno(EX_BAD,
                        "Trying to use ultra high speed medium on a writer which is not\ncompatible with ultra high speed media.\n");
                  } else if ((flags & F_FORCE) == 0) {
                        comerrno(EX_BAD,
                        "Probably trying to use ultra high speed medium on improper writer.\n");
                  }
            }
            if (dp->cdr_dstat->ds_at_min_speed >= 4 &&
                        dp->cdr_dstat->ds_at_max_speed > 4 &&
                        dp->cdr_dstat->ds_dr_max_wspeed <= 4) {
                  if ((flags & F_FORCE) == 0) {
                        comerrno(EX_BAD,
                        "Trying to use high speed medium on low speed writer.\n");
                  }
            }
            if ((int)dp->cdr_dstat->ds_at_min_speed > speed) {
                  if ((flags & F_FORCE) == 0) {
                        errmsgno(EX_BAD,
                        "Write speed %d of writer not sufficient for this medium.\n",
                              speed);
                        errmsgno(EX_BAD,
                        "You did use a %s speed medium on an improper writer or\n",
                        dp->cdr_dstat->ds_flags & DSF_ULTRASP_ERA ?
                        "ultra high": "high");
                        comerrno(EX_BAD,
                        "you used a speed=# option with a speed too low for this medium.\n");
                  }
            }
      }
      if ((flags & (F_BLANK|F_FORCE)) == (F_BLANK|F_FORCE)) {
            printf("Waiting for drive to calm down.\n");
            wait_unit_ready(usalp, 120);
            if (gracewait(dp, &gracedone) < 0) {
                  /*
                   * In case kill() did not work ;-)
                   */
                  errs++;
                  goto restore_it;
            }
            scsi_blank(usalp, 0L, blanktype, FALSE);
      }

      /*
       * Last chance to quit!
       */
      if (gracewait(dp, &gracedone) < 0) {
            /*
             * In case kill() did not work ;-)
             */
            errs++;
            goto restore_it;
      }
      
      if (dp->profile == 0x2B && flags & F_SAO && tsize > 0) {
          printf("Preparing middle zone location for this DVD+R dual layer disc\n");
          if (!dp->cdr_layer_split(usalp, dp, tsize)) {
            errmsgno(EX_BAD, "Cannot send structure for middle zone location.\n");
            comexit(EX_BAD);
          }
      }

      if (tracks > 0 && fs > 0l) {
            /*
             * Wait for the read-buffer to become full.
             * This should be take no extra time if the input is a file.
             * If the input is a pipe (e.g. mkisofs) this can take a
             * while. If mkisofs dumps core before it starts writing,
             * we abort before the writing process started.
             */
            if (!await_faio()) {
                  comerrno(EX_BAD, "Input buffer error, aborting.\n");
            }
      }
      wait_unit_ready(usalp, 120);

      starttime.tv_sec = 0;
      wstarttime.tv_sec = 0;
      stoptime.tv_sec = 0;
      fixtime.tv_sec = 0;
      if (gettimeofday(&starttime, (struct timezone *)0) < 0)
            errmsg("Cannot get start time\n");

      /*
       * Blank the media if we were requested to do so
       */
      if (flags & F_BLANK) {
            /*
             * Do not abort if OPC failes. Just give it a chance
             * for better laser power calibration than without OPC.
             *
             * Ricoh drives return with a vendor unique sense code.
             * This is most likely because they refuse to do OPC
             * on a non blank media.
             */
            usalp->silent++;
            do_opc(usalp, dp, flags);
            usalp->silent--;
            wait_unit_ready(usalp, 120);
            if (gettimeofday(&starttime, (struct timezone *)0) < 0)
                  errmsg("Cannot get start time\n");

            if ((*dp->cdr_blank)(usalp, dp, 0L, blanktype) < 0) {
                  errmsgno(EX_BAD, "Cannot blank disk, aborting.\n");
                  if (blanktype != BLANK_DISC) {
                        errmsgno(EX_BAD, "Some drives do not support all blank types.\n");
                        errmsgno(EX_BAD, "Try again with wodim blank=all.\n");
                  }
                  comexit(EX_BAD);
            }
            if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
                  errmsg("Cannot get blank time\n");
            if (lverbose)
                  prtimediff("Blanking time: ", &starttime, &fixtime);

            /*
             * XXX Erst blank und dann format?
             * XXX Wenn ja, dann hier (flags & F_FORMAT) testen
             *
             * EB: nee, besser nicht
             */
            if (!wait_unit_ready(usalp, 240) || tracks == 0) {
                  comexit(0);
            }
      }
      if (flags & F_FORMAT) {
            printf("wodim: media format asked\n");
            /*
            * Do not abort if OPC failes. Just give it a chance
            * for better laser power calibration than without OPC.
            *
            * Ricoh drives return with a vendor unique sense code.
            * This is most likely because they refuse to do OPC
            * on a non blank media.
            */
            usalp->silent++;
            do_opc(usalp, dp, flags);
            usalp->silent--;
            wait_unit_ready(usalp, 120);
            if (gettimeofday(&starttime, (struct timezone *)0) < 0)
                  errmsg("Cannot get start time\n");

            if ((*dp->cdr_format)(usalp, dp, formattype) < 0) {
                  errmsgno(EX_BAD, "Cannot format disk, aborting.\n");
                  comexit(EX_BAD);
            }
            if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
                  errmsg("Cannot get format time\n");
            if (lverbose)
                  prtimediff("Formatting time: ", &starttime, &fixtime);

            if (!wait_unit_ready(usalp, 240) || tracks == 0) {
                  comexit(0);
            }
            if (gettimeofday(&starttime, (struct timezone *)0) < 0)
                  errmsg("Cannot get start time\n");
      }
      /*
      * Reset start time so we will not see blanking time and
      * writing time counted together.
      */
      if (gettimeofday(&starttime, (struct timezone *)0) < 0)
            errmsg("Cannot get start time\n");
      if (tracks == 0 && (flags & F_FIX) == 0)
      comerrno(EX_BAD, "No tracks found.\n");
      /*
       * Get the number of the next recordable track by reading the TOC and
       * use the number the last current track number.
       */
      usalp->silent++;
      if (read_tochdr(usalp, dp, NULL, &trackno) < 0) {
            trackno = 0;
      }
      usalp->silent--;
      
      /* If it is DVD, the information in TOC is fabricated :)
         The real information is from read disk info command*/
      if((dp->cdr_dstat->ds_disktype&DT_DVD) && (dp->cdr_dstat->ds_trlast>0)){
            trackno=dp->cdr_dstat->ds_trlast-1;
            if (lverbose > 2)
                  printf("trackno=%d\n",trackno);
      }

      if ((tracks + trackno) > MAX_TRACK) {
            /*
             * XXX How many tracks are allowed on a DVD?
             */
            comerrno(EX_BAD, "Too many tracks for this disk, last track number is %d.\n",
                        tracks + trackno);
      }

      for (i = 0; i <= tracks+1; i++) {   /* Lead-in ... Lead-out */
            track[i].trackno = i + trackno;     /* Set up real track #  */
      }

      if ((*dp->cdr_opt2)(usalp, dp) < 0) {
            errmsgno(EX_BAD, "Cannot set up 2nd set of driver options.\n");
      }

      /*
       * Now we actually start writing to the CD/DVD.
       * XXX Check total size of the tracks and remaining size of disk.
       */
      if ((*dp->cdr_open_session)(usalp, dp, track) < 0) {
            comerrno(EX_BAD, "Cannot open new session.\n");
      }
      if (!do_opc(usalp, dp, flags))
            comexit(EX_BAD);

      /*
       * As long as open_session() will do nothing but
       * set up parameters, we may leave fix_it here.
       * I case we have to add an open_session() for a drive
       * that wants to do something that modifies the disk
       * We have to think about a new solution.
       */
      if (flags & F_FIX)
            goto fix_it;

      /*
       * This call may modify trackp[i].trackstart for all tracks.
       */
      if ((*dp->cdr_write_leadin)(usalp, dp, track) < 0)
            comerrno(EX_BAD, "Could not write Lead-in.\n");

      if (lverbose && (dp->cdr_dstat->ds_cdrflags & RF_LEADIN) != 0) {

            if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
                  errmsg("Cannot get lead-in write time\n");
            prtimediff("Lead-in write time: ", &starttime, &fixtime);
      }

      if (gettimeofday(&wstarttime, (struct timezone *)0) < 0)
            errmsg("Cannot get start time\n");
      for (i = 1; i <= tracks; i++) {
            startsec = 0L;

            if ((*dp->cdr_open_track)(usalp, dp, &track[i]) < 0) {
                  errmsgno(EX_BAD, "Cannot open next track.\n");
                  errs++;
                  break;
            }

            if ((flags & (F_SAO|F_RAW)) == 0) {
                  if ((*dp->cdr_next_wr_address)(usalp, &track[i], &startsec) < 0) {
                        errmsgno(EX_BAD, "Cannot get next writable address.\n");
                        errs++;
                        break;
                  }
                  track[i].trackstart = startsec;
            }
            if (debug || lverbose) {
                  printf("Starting new track at sector: %ld\n",
                                    track[i].trackstart);
                  flush();
            }
            if (write_track_data(usalp, dp, &track[i]) < 0) {
                  if (cdr_underrun(usalp)) {
                        errmsgno(EX_BAD,
                        "The current problem looks like a buffer underrun.\n");
                        if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0)
                              errmsgno(EX_BAD, "Try to use 'driveropts=burnfree'.\n");
                        else {
                              errmsgno(EX_BAD, "It looks like 'driveropts=burnfree' does not work for this drive.\n");
                              errmsgno(EX_BAD, "Please report.\n");
                        }

                        errmsgno(EX_BAD,
                        "Make sure that you are root, enable DMA and check your HW/OS set up.\n");
                  } else {
                        errmsgno(EX_BAD, "A write error occured.\n");
                        errmsgno(EX_BAD, "Please properly read the error message above.\n");
                  }
                  errs++;
                  sleep(5);
                  unit_ready(usalp);
                  (*dp->cdr_close_track)(usalp, dp, &track[i]);
                  break;
            }
            if ((*dp->cdr_close_track)(usalp, dp, &track[i]) < 0) {
                  /*
                   * Check for "Dummy blocks added" message first.
                   */
                  if (usal_sense_key(usalp) != SC_ILLEGAL_REQUEST ||
                              usal_sense_code(usalp) != 0xB5) {
                        errmsgno(EX_BAD, "Cannot close track.\n");
                        errs++;
                        break;
                  }
            }
      }
fix_it:
      if (gettimeofday(&stoptime, (struct timezone *)0) < 0)
            errmsg("Cannot get stop time\n");
      cdrstats(dp);

      if (flags & F_RAW) {
            if (lverbose) {
                  printf("Writing Leadout...\n");
                  flush();
            }
            write_leadout(usalp, dp, track);
      }
      if ((flags & F_NOFIX) == 0) {
            if (lverbose) {
                  printf("Fixating...\n");
                  flush();
            }
            if ((*dp->cdr_fixate)(usalp, dp, track) < 0) {
                  /*
                   * Ignore fixating errors in dummy mode.
                   */
                  if ((flags & F_DUMMY) == 0) {
                        errmsgno(EX_BAD, "Cannot fixate disk.\n");
                        errs++;
                  }
            }
            if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
                  errmsg("Cannot get fix time\n");
            if (lverbose)
                  prtimediff("Fixating time: ", &stoptime, &fixtime);
      }
      if ((dp->cdr_dstat->ds_cdrflags & RF_DID_CDRSTAT) == 0) {
            dp->cdr_dstat->ds_cdrflags |= RF_DID_CDRSTAT;
            (*dp->cdr_stats)(usalp, dp);
      }
      if ((flags & (F_RAW|F_EJECT)) == F_RAW) {
            /*
             * Most drives seem to forget to reread the TOC from disk
             * if they are in RAW mode.
             */
            usalp->silent++;
            if (read_tochdr(usalp, dp, NULL, NULL) < 0) {
                  usalp->silent--;
                  if ((flags & F_DUMMY) == 0)
                        reload_media(usalp, dp);
            } else {
                  usalp->silent--;
            }
      }

restore_it:
      /*
       * Try to restore the old sector size and stop FIFO.
       */
      kill_faio();
      comexit(errs?-2:0);
      return (0);
}

static int 
gracewait(cdr_t *dp, BOOL *didgracep)
{
      int   i;
      BOOL  didgrace = FALSE;

      if (didgracep)
            didgrace = *didgracep;

      if(warn_minisize>=0) {
            fprintf(stderr,  "\nWARNING: found a microscopic small track size (%lld bytes).\n"
                        "         Do you really want to write this image? Press Ctrl-C to abort...\n\n",
                        warn_minisize);
            gracetime+=20;
      }
      if (gracetime < MIN_GRACE_TIME)
            gracetime = MIN_GRACE_TIME;
      if (gracetime > 999)
            gracetime = 999;

      printf("Starting to write CD/DVD at speed %5.1f in %s%s %s mode for %s session.\n",
         (float)dp->cdr_dstat->ds_wspeed,
            (dp->cdr_cmdflags & F_DUMMY) ? "dummy" : "real",
            (dp->cdr_cmdflags & F_FORCE) ? " force" : "",
            wm2name[dp->cdr_dstat->ds_wrmode],
            (dp->cdr_cmdflags & F_MULTI) ? "multi" : "single");
      if (didgrace) {
            printf("No chance to quit anymore.");
            goto grace_done;
      }
      printf("Last chance to quit, starting %s write in %4d seconds.",
            (dp->cdr_cmdflags & F_DUMMY)?"dummy":"real", gracetime);
      flush();
      signal(SIGINT, intr);
      signal(SIGHUP, intr);
      signal(SIGTERM, intr);

      for (i = gracetime; --i >= 0; ) {
            sleep(1);
            if (didintr) {
                  printf("\n");
                  excdr(SIGINT, &exargs);
                  signal(SIGINT, SIG_DFL);
                  kill(getpid(), SIGINT);
                  /*
                   * In case kill() did not work ;-)
                   */
                  if (didgracep)
                        *didgracep = FALSE;
                  return (-1);
            }
            printf("\b\b\b\b\b\b\b\b\b\b\b\b\b%4d seconds.", i);
            flush();
      }
grace_done:
      printf(" Operation starts.");
      flush();
      signal(SIGINT, SIG_DFL);
      signal(SIGHUP, SIG_DFL);
      signal(SIGTERM, SIG_DFL);
      signal(SIGINT, intfifo);
      signal(SIGHUP, intfifo);
      signal(SIGTERM, intfifo);
      printf("\n");

      if (didgracep)
            *didgracep = TRUE;
      return (0);
}

static void 
cdrstats(cdr_t *dp)
{
      float secsps = 75.0;
      int   nsecs;
      float fspeed;
      struct timeval    tcur;
      struct timeval    tlast;
      BOOL  nostop = FALSE;

      if (starttime.tv_sec == 0)
            return;

      if (stoptime.tv_sec == 0) {
            gettimeofday(&stoptime, (struct timezone *)0);
            nostop = TRUE;
      }

      if ((dp->cdr_dstat->ds_cdrflags & RF_DID_STAT) != 0)
            return;
      dp->cdr_dstat->ds_cdrflags |= RF_DID_STAT;

      if (lverbose == 0)
            return;

      if (dp->cdr_cmdflags & F_FIX)
            return;

      if ((dp->cdr_cmdflags & (F_WRITE|F_BLANK)) == F_BLANK)
            return;

      tlast = wstarttime;
      tcur = stoptime;

      prtimediff("Writing  time: ", &starttime, &stoptime);

      nsecs = dp->cdr_dstat->ds_endsec - dp->cdr_dstat->ds_startsec;

      if (dp->cdr_dstat->ds_flags & DSF_DVD)
            secsps = 676.27;

      tlast.tv_sec = tcur.tv_sec - tlast.tv_sec;
      tlast.tv_usec = tcur.tv_usec - tlast.tv_usec;
      while (tlast.tv_usec < 0) {
            tlast.tv_usec += 1000000;
            tlast.tv_sec -= 1;
      }
      if (!nostop && nsecs != 0 && dp->cdr_dstat->ds_endsec > 0) {
            /*
             * May not be known (e.g. cdrecord -)
             *
             * XXX if we later allow this code to know how much has
             * XXX actually been written, then we may remove the
             * XXX dependance from nostop & nsecs != 0
             */
            fspeed = (nsecs / secsps) /
                  (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001);
            if (fspeed > 999.0)
                  fspeed = 999.0;
      if (dp->is_dvd) fspeed /= 9;
            printf("Average write speed %5.1fx.\n", fspeed);
      }

      if (dp->cdr_dstat->ds_minbuf <= 100) {
            printf("Min drive buffer fill was %u%%\n",
                  (unsigned int)dp->cdr_dstat->ds_minbuf);
      }
      if (dp->cdr_dstat->ds_buflow > 0) {
            printf("Total of %ld possible drive buffer underruns predicted.\n",
                  (long)dp->cdr_dstat->ds_buflow);
      }
}

/*
 * Short usage
 */
static void
susage(int ret)
{
      fprintf(stderr, "Usage: %s [options] track1...trackn\n", get_progname());
      fprintf(stderr, "\nUse\t%s -help\n", get_progname());
      fprintf(stderr, "to get a list of valid options.\n");
      fprintf(stderr, "\nUse\t%s blank=help\n", get_progname());
      fprintf(stderr, "to get a list of valid blanking options.\n");
      fprintf(stderr, "\nUse\t%s dev=b,t,l driveropts=help -checkdrive\n", get_progname());
      fprintf(stderr, "to get a list of drive specific options.\n");
      fprintf(stderr, "\nUse\t%s dev=help\n", get_progname());
      fprintf(stderr, "to get a list of possible SCSI transport specifiers.\n");
      exit(ret);
      /* NOTREACHED */
}

static void 
usage(int excode)
{
      fprintf(stderr, "Usage: %s [options] track1...trackn\n", get_progname());
      fprintf(stderr, "Options:\n");
      fprintf(stderr, "\t-version   print version information and exit\n");
      fprintf(stderr, "\tdev=target SCSI target to use as CD/DVD-Recorder\n");
      fprintf(stderr, "\tgracetime=#      set the grace time before starting to write to #.\n");
      fprintf(stderr, "\ttimeout=#  set the default SCSI command timeout to #.\n");
      fprintf(stderr, "\tdebug=#,-d Set to # or increment misc debug level\n");
      fprintf(stderr, "\tkdebug=#,kd=#    do Kernel debugging\n");
      fprintf(stderr, "\t-verbose,-v      increment general verbose level by one\n");
      fprintf(stderr, "\t-Verbose,-V      increment SCSI command transport verbose level by one\n");
      fprintf(stderr, "\t-silent,-s do not print status of failed SCSI commands\n");
      fprintf(stderr, "\tdriver=name      user supplied driver name, use with extreme care\n");
      fprintf(stderr, "\tdriveropts=opt   a comma separated list of driver specific options\n");
      fprintf(stderr, "\t-setdropts set driver specific options and exit\n");
      fprintf(stderr, "\t-checkdrive      check if a driver for the drive is present\n");
      fprintf(stderr, "\t-prcap           print drive capabilities for MMC compliant drives\n");
      fprintf(stderr, "\t-inq       do an inquiry for the drive and exit\n");
      fprintf(stderr, "\t-scanbus   scan the SCSI and IDE buses and exit\n");
      fprintf(stderr, "\t-reset           reset the SCSI bus with the cdrecorder (if possible)\n");
      fprintf(stderr, "\t-abort           send an abort sequence to the drive (may help if hung)\n");
      fprintf(stderr, "\t-overburn  allow to write more than the official size of a medium\n");
      fprintf(stderr, "\t-ignsize   ignore the known size of a medium (may cause problems)\n");
      fprintf(stderr, "\t-useinfo   use *.inf files to overwrite audio options.\n");
      fprintf(stderr, "\tspeed=#          set speed of drive\n");
      fprintf(stderr, "\tblank=type blank a CD-RW disc (see blank=help)\n");
      fprintf(stderr, "\t-format          format a CD-RW/DVD-RW/DVD+RW disc\n");
   fprintf(stderr, "\tformattype=#  select the format method for DVD+RW disc\n");
#ifdef      FIFO
      fprintf(stderr, "\tfs=#       Set fifo size to # (0 to disable, default is %ld MB)\n",
                                          DEFAULT_FIFOSIZE/(1024L*1024L));
#endif
      fprintf(stderr, "\tts=#       set maximum transfer size for a single SCSI command\n");
      fprintf(stderr, "\t-load            load the disk and exit (works only with tray loader)\n");
      fprintf(stderr, "\t-lock            load and lock the disk and exit (works only with tray loader)\n");
      fprintf(stderr, "\t-eject           eject the disk after doing the work\n");
      fprintf(stderr, "\t-dummy           do everything with laser turned off\n");
      fprintf(stderr, "\t-msinfo          retrieve multi-session info for genisoimage\n");
      fprintf(stderr, "\t-msifile=path    run -msinfo and copy output to file\n");
      fprintf(stderr, "\t-toc       retrieve and print TOC/PMA data\n");
      fprintf(stderr, "\t-atip            retrieve and print ATIP data\n");
      fprintf(stderr, "\t-multi           generate a TOC that allows multi session\n");
      fprintf(stderr, "\t           In this case default track type is CD-ROM XA mode 2 form 1 - 2048 bytes\n");
      fprintf(stderr, "\t-fix       fixate a corrupt or unfixated disk (generate a TOC)\n");
      fprintf(stderr, "\t-nofix           do not fixate disk after writing tracks\n");
      fprintf(stderr, "\t-waiti           wait until input is available before opening SCSI\n");
      fprintf(stderr, "\t-immed           Try to use the SCSI IMMED flag with certain long lasting commands\n");
      fprintf(stderr, "\t-force           force to continue on some errors to allow blanking bad disks\n");
      fprintf(stderr, "\t-tao       Write disk in TAO mode.\n");
      fprintf(stderr, "\t-dao       Write disk in SAO mode.\n");
      fprintf(stderr, "\t-sao       Write disk in SAO mode.\n");
      fprintf(stderr, "\t-raw       Write disk in RAW mode.\n");
      fprintf(stderr, "\t-raw96r          Write disk in RAW/RAW96R mode.\n");
      fprintf(stderr, "\t-raw96p          Write disk in RAW/RAW96P mode.\n");
      fprintf(stderr, "\t-raw16           Write disk in RAW/RAW16 mode.\n");
#ifdef      CLONE_WRITE
      fprintf(stderr, "\t-clone           Write disk in clone write mode.\n");
#endif
      fprintf(stderr, "\ttsize=#          Length of valid data in next track\n");
      fprintf(stderr, "\tpadsize=#  Amount of padding for next track\n");
      fprintf(stderr, "\tpregap=#   Amount of pre-gap sectors before next track\n");
      fprintf(stderr, "\tdefpregap=#      Amount of pre-gap sectors for all but track #1\n");
      fprintf(stderr, "\tmcn=text   Set the media catalog number for this CD to 'text'\n");
      fprintf(stderr, "\tisrc=text  Set the ISRC number for the next track to 'text'\n");
      fprintf(stderr, "\tindex=list Set the index list for the next track to 'list'\n");
      fprintf(stderr, "\t-text            Write CD-Text from information from *.inf or *.cue files\n");
      fprintf(stderr, "\ttextfile=name    Set the file with CD-Text data to 'name'\n");
      fprintf(stderr, "\tcuefile=name     Set the file with CDRWIN CUE data to 'name'\n");

      fprintf(stderr, "\t-audio           Subsequent tracks are CD-DA audio tracks\n");
      fprintf(stderr, "\t-data            Subsequent tracks are CD-ROM data mode 1 - 2048 bytes (default)\n");
      fprintf(stderr, "\t-mode2           Subsequent tracks are CD-ROM data mode 2 - 2336 bytes\n");
      fprintf(stderr, "\t-xa        Subsequent tracks are CD-ROM XA mode 2 form 1 - 2048 bytes\n");
      fprintf(stderr, "\t-xa1       Subsequent tracks are CD-ROM XA mode 2 form 1 - 2056 bytes\n");
      fprintf(stderr, "\t-xa2       Subsequent tracks are CD-ROM XA mode 2 form 2 - 2324 bytes\n");
      fprintf(stderr, "\t-xamix           Subsequent tracks are CD-ROM XA mode 2 form 1/2 - 2332 bytes\n");
      fprintf(stderr, "\t-cdi       Subsequent tracks are CDI tracks\n");
      fprintf(stderr, "\t-isosize   Use iso9660 file system size for next data track\n");
      fprintf(stderr, "\t-preemp          Audio tracks are mastered with 50/15 microseconds preemphasis\n");
      fprintf(stderr, "\t-nopreemp  Audio tracks are mastered with no preemphasis (default)\n");
      fprintf(stderr, "\t-copy            Audio tracks have unlimited copy permission\n");
      fprintf(stderr, "\t-nocopy          Audio tracks may only be copied once for personal use (default)\n");
      fprintf(stderr, "\t-scms            Audio tracks will not have any copy permission at all\n");
      fprintf(stderr, "\t-pad       Pad data tracks with %d zeroed sectors\n", PAD_SECS);
      fprintf(stderr, "\t           Pad audio tracks to a multiple of %d bytes\n", AUDIO_SEC_SIZE);
      fprintf(stderr, "\t-nopad           Do not pad data tracks (default)\n");
      fprintf(stderr, "\t-shorttrack      Subsequent tracks may be non Red Book < 4 seconds if in SAO or RAW mode\n");
      fprintf(stderr, "\t-noshorttrack    Subsequent tracks must be >= 4 seconds\n");
      fprintf(stderr, "\t-swab            Audio data source is byte-swapped (little-endian/Intel)\n");
      fprintf(stderr, "The type of the first track is used for the toc type.\n");
      fprintf(stderr, "Currently only form 1 tracks are supported.\n");
      exit(excode);
}

static void 
blusage(int ret)
{
      fprintf(stderr, "Blanking options:\n");
      fprintf(stderr, "\tall\t\tblank the entire disk\n");
      fprintf(stderr, "\tdisc\t\tblank the entire disk\n");
      fprintf(stderr, "\tdisk\t\tblank the entire disk\n");
      fprintf(stderr, "\tfast\t\tminimally blank the entire disk (PMA, TOC, pregap)\n");
      fprintf(stderr, "\tminimal\t\tminimally blank the entire disk (PMA, TOC, pregap)\n");
      fprintf(stderr, "\ttrack\t\tblank a track\n");
      fprintf(stderr, "\tunreserve\tunreserve a track\n");
      fprintf(stderr, "\ttrtail\t\tblank a track tail\n");
      fprintf(stderr, "\tunclose\t\tunclose last session\n");
      fprintf(stderr, "\tsession\t\tblank last session\n");

      exit(ret);
      /* NOTREACHED */
}

static void 
formattypeusage(int ret)
{
      fprintf(stderr, "Formating options:\n");
      fprintf(stderr, "\tfull\t\tstandard formating\n");
      fprintf(stderr, "\tbackground\t\tbackground formating\n");
      fprintf(stderr, "\tforce\t\tforce reformat\n");

      exit(ret);
      /* NOTREACHED */
}

/* ARGSUSED */
static void
intr(int sig)
{
      sig = 0;    /* Fake usage for gcc */

      signal(SIGINT, intr);

      didintr++;
}

static void 
catchsig(int sig)
{
      signal(sig, catchsig);
}

static int 
scsi_cb(void *arg)
{
      comexit(EX_BAD);
      /* NOTREACHED */
      return (0); /* Keep lint happy */
}

static void 
intfifo(int sig)
{
      errmsgno(EX_BAD, "Caught interrupt.\n");
      if (exargs.usalp) {
            SCSI  *usalp = exargs.usalp;

            if (usalp->running) {
                  if (usalp->cb_fun != NULL) {
                        comerrno(EX_BAD, "Second interrupt. Doing hard abort.\n");
                        /* NOTREACHED */
                  }
                  usalp->cb_fun = scsi_cb;
                  usalp->cb_arg = &exargs;
                  return;
            }
      }
      comexit(sig);
}

/* ARGSUSED */
static void 
exscsi(int excode, void *arg)
{
      struct exargs     *exp = (struct exargs *)arg;

      /*
       * Try to restore the old sector size.
       */
      if (exp != NULL && exp->exflags == 0) {
            if (exp->usalp->running) {
                  return;
            }
            /*
             * flush cache is not supported by CD-ROMs avoid prob with -toc
             */
            exp->usalp->silent++;
            scsi_flush_cache(exp->usalp, FALSE);
            (*exp->dp->cdr_abort_session)(exp->usalp, exp->dp);
            exp->usalp->silent--;
            set_secsize(exp->usalp, exp->old_secsize);
            unload_media(exp->usalp, exp->dp, exp->flags);

            exp->exflags++;   /* Make sure that it only get called once */
      }
}

static void 
excdr(int excode, void *arg)
{
      struct exargs     *exp = (struct exargs *)arg;

      exscsi(excode, arg);

      cdrstats(exp->dp);
      if ((exp->dp->cdr_dstat->ds_cdrflags & RF_DID_CDRSTAT) == 0) {
            exp->dp->cdr_dstat->ds_cdrflags |= RF_DID_CDRSTAT;
            (*exp->dp->cdr_stats)(exp->usalp, exp->dp);
      }

#ifdef      FIFO
      kill_faio();
      wait_faio();
      if (debug || lverbose)
            fifo_stats();
#endif
}

int 
read_buf(int f, char *bp, int size)
{
      char  *p = bp;
      int   amount = 0;
      int   n;

      do {
            do {
                  n = read(f, p, size-amount);
            } while (n < 0 && (geterrno() == EAGAIN || geterrno() == EINTR));
            if (n < 0)
                  return (n);
            amount += n;
            p += n;

      } while (amount < size && n > 0);
      return (amount);
}

int 
fill_buf(int f, track_t *trackp, long secno, char *bp, int size)
{
      int   amount = 0;
      int   nsecs;
      int   rsize;
      int   rmod;
      int   readoffset = 0;

      nsecs = size / trackp->secsize;
      if (nsecs < trackp->secspt) {
            /*
             * Clear buffer to prepare for last transfer.
             * Make sure that a partial sector ends with NULs
             */
            fillbytes(bp, trackp->secspt * trackp->secsize, '\0');
      }

      if (!is_raw(trackp)) {
            amount = read_buf(f, bp, size);
            if (amount != size) {
                  if (amount < 0)
                        return (amount);
                  /*
                   * We got less than expected, clear rest of buf.
                   */
                  fillbytes(&bp[amount], size-amount, '\0');
            }
            if (is_swab(trackp))
                  swabbytes(bp, amount);
            return (amount);
      }

      rsize = nsecs * trackp->isecsize;
      rmod  = size % trackp->secsize;
      if (rmod > 0) {
            rsize += rmod;
            nsecs++;
      }

      readoffset = trackp->dataoff;
      amount = read_buf(f, bp + readoffset, rsize);
      if (is_swab(trackp))
            swabbytes(bp + readoffset, amount);

      if (trackp->isecsize == 2448 && trackp->secsize == 2368)
            subrecodesecs(trackp, (Uchar *)bp, secno, nsecs);

      scatter_secs(trackp, bp + readoffset, nsecs);

      if (amount != rsize) {
            if (amount < 0)
                  return (amount);
            /*
             * We got less than expected, clear rest of buf.
             */
            fillbytes(&bp[amount], rsize-amount, '\0');
            nsecs = amount / trackp->isecsize;
            rmod  = amount % trackp->isecsize;
            amount = nsecs * trackp->secsize;
            if (rmod > 0) {
                  nsecs++;
                  amount += rmod;
            }
      } else {
            amount = size;
      }
      if ((trackp->sectype & ST_MODE_RAW) == 0) {
            encsectors(trackp, (Uchar *)bp, secno, nsecs);
            fillsubch(trackp, (Uchar *)bp, secno, nsecs);
      } else {
            scrsectors(trackp, (Uchar *)bp, secno, nsecs);
      }
      return (amount);
}

int 
get_buf(int f, track_t *trackp, long secno, char **bpp, int size)
{
      if (fs > 0) {
/*          return (faio_read_buf(f, *bpp, size));*/
            return (faio_get_buf(f, bpp, size));
      } else {
            return (fill_buf(f, trackp, secno, *bpp, size));
      }
}

int 
write_secs(SCSI *usalp, cdr_t *dp, char *bp, long startsec, int bytespt, 
                  int secspt, BOOL islast)
{
      int   amount;

again:
      usalp->silent++;
      amount = (*dp->cdr_write_trackdata)(usalp, bp, startsec, bytespt, secspt, islast);
      usalp->silent--;
      if (amount < 0) {
            if (scsi_in_progress(usalp)) {
                  /*
                   * If we sleep too long, the drive buffer is empty
                   * before we start filling it again. The max. CD speed
                   * is ~ 10 MB/s (52x RAW writing). The max. DVD speed
                   * is ~ 25 MB/s (18x DVD 1385 kB/s).
                   * With 10 MB/s, a 1 MB buffer empties within 100ms.
                   * With 25 MB/s, a 1 MB buffer empties within 40ms.
                   */
                  if ((dp->cdr_dstat->ds_flags & DSF_DVD) == 0) {
                        usleep(60000);
                  } else {
#ifndef     _SC_CLK_TCK
                        usleep(20000);
#else
                        if (sysconf(_SC_CLK_TCK) < 100)
                              usleep(20000);
                        else
                              usleep(10000);

#endif
                  }
                  goto again;
            }
            return (-1);
      }
      return (amount);
}

static int 
write_track_data(SCSI *usalp, cdr_t *dp, track_t *trackp)
{
      int   track = trackp->trackno;
      int   f = -1;
      int   isaudio;
      long  startsec;
      Llong bytes_read = 0;
      Llong bytes = 0;
      Llong savbytes = 0;
      int   count;
      Llong tracksize;
      int   secsize;
      int   secspt;
      int   bytespt;
      int   bytes_to_read;
      long  amount;
      int   pad;
      BOOL  neednl      = FALSE;
      BOOL  islast      = FALSE;
      char  *bp   = buf;
      struct timeval tlast;
      struct timeval tcur;
      float secsps = 75.0;
long bsize;
long bfree;
#define     BCAP
#ifdef      BCAP
int per = 0;
#ifdef      XBCAP
int oper = -1;
#endif
#endif

      if (dp->cdr_dstat->ds_flags & DSF_DVD)
            secsps = 676.27;

      usalp->silent++;
      if ((*dp->cdr_buffer_cap)(usalp, &bsize, &bfree) < 0)
            bsize = -1L;
      if (bsize == 0)         /* If we have no (known) buffer, we cannot */
            bsize = -1L;      /* retrieve the buffer fill ratio      */
      usalp->silent--;


      if (is_packet(trackp))  /* XXX Ugly hack for now */
            return (write_packet_data(usalp, dp, trackp));

      if (trackp->xfp != NULL)
            f = xfileno(trackp->xfp);

      isaudio = is_audio(trackp);
      tracksize = trackp->tracksize;
      startsec = trackp->trackstart;

      secsize = trackp->secsize;
      secspt = trackp->secspt;
      bytespt = secsize * secspt;

      pad = !isaudio && is_pad(trackp);   /* Pad only data tracks */

      if (debug) {
            printf("secsize:%d secspt:%d bytespt:%d audio:%d pad:%d\n",
                  secsize, secspt, bytespt, isaudio, pad);
      }

      if (lverbose) {
            if (tracksize > 0)
                  printf("\rTrack %02d:    0 of %4lld MB written.",
                        track, tracksize >> 20);
            else
                  printf("\rTrack %02d:    0 MB written.", track);
            flush();
            neednl = TRUE;
      }

      gettimeofday(&tlast, (struct timezone *)0);
      do {
            bytes_to_read = bytespt;
            if (tracksize > 0) {
                  if ((tracksize - bytes_read) > bytespt)
                        bytes_to_read = bytespt;
                  else
                        bytes_to_read = tracksize - bytes_read;
            }
            count = get_buf(f, trackp, startsec, &bp, bytes_to_read);

            if (count < 0)
                  comerr("read error on input file\n");
            if (count == 0)
                  break;
            bytes_read += count;
            if (tracksize >= 0 && bytes_read >= tracksize) {
                  count -= bytes_read - tracksize;
                  /*
                   * Paranoia: tracksize is known (trackp->tracksize >= 0)
                   * At this point, trackp->padsize should alway be set
                   * if the tracksize is less than 300 sectors.
                   */
                  if (trackp->padsecs == 0 &&
                      (is_shorttrk(trackp) || (bytes_read/secsize) >= 300))
                        islast = TRUE;
            }

            if (count < bytespt) {
                  if (debug) {
                        printf("\nNOTICE: reducing block size for last record.\n");
                        neednl = FALSE;
                  }

                  if ((amount = count % secsize) != 0) {
                        amount = secsize - amount;
                        count += amount;
                        printf("\nWARNING: padding up to secsize.\n");
                        neednl = FALSE;
                  }
                  bytespt = count;
                  secspt = count / secsize;
                  /*
                   * If tracksize is not known (trackp->tracksize < 0)
                   * we may need to set trackp->padsize
                   * if the tracksize is less than 300 sectors.
                   */
                  if (trackp->padsecs == 0 &&
                      (is_shorttrk(trackp) || (bytes_read/secsize) >= 300))
                        islast = TRUE;
            }

            amount = write_secs(usalp, dp, bp, startsec, bytespt, secspt, islast);
            if (amount < 0) {
                  printf("%swrite track data: error after %lld bytes\n",
                                          neednl?"\n":"", bytes);
                  return (-1);
            }
            bytes += amount;
            startsec += amount / secsize;

            if (lverbose && (bytes >= (savbytes + 0x100000))) {
                  int   fper;
                  int   nsecs = (bytes - savbytes) / secsize;
                  float fspeed;

                  gettimeofday(&tcur, (struct timezone *)0);
                  printf("\rTrack %02d: %4lld", track, bytes >> 20);
                  if (tracksize > 0)
                        printf(" of %4lld MB", tracksize >> 20);
                  else
                        printf(" MB");
                  printf(" written");
                  fper = fifo_percent(TRUE);
                  if (fper >= 0)
                        printf(" (fifo %3d%%)", fper);
#ifdef      BCAP
                  if (bsize > 0) {              /* buffer size known */
                        usalp->silent++;
                        per = (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree);
                        usalp->silent--;
                        if (per >= 0) {
                              per = 100*(bsize - bfree) / bsize;
                              if ((bsize - bfree) <= amount || per <= 5)
                                    dp->cdr_dstat->ds_buflow++;
                              if (per < (int)dp->cdr_dstat->ds_minbuf &&
                                  (startsec*secsize) > bsize) {
                                    dp->cdr_dstat->ds_minbuf = per;
                              }
                              printf(" [buf %3d%%]", per);
#ifdef      BCAPDBG
                              printf(" %3ld %3ld", bsize >> 10, bfree >> 10);
#endif
                        }
                  }
#endif

                  tlast.tv_sec = tcur.tv_sec - tlast.tv_sec;
                  tlast.tv_usec = tcur.tv_usec - tlast.tv_usec;
                  while (tlast.tv_usec < 0) {
                        tlast.tv_usec += 1000000;
                        tlast.tv_sec -= 1;
                  }
                  fspeed = (nsecs / secsps) /
                        (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001);
                  if (fspeed > 999.0)
                        fspeed = 999.0;
#ifdef      BCAP
                  if (bsize > 0 && per > dminbuf &&
                      dp->cdr_dstat->ds_cdrflags & RF_WR_WAIT) {
                        int   wsecs = (per-dminbuf)*(bsize/secsize)/100;
                        int   msecs = 0x100000/secsize;
                        int   wt;
                        int   mt;
                        int   s = dp->cdr_dstat->ds_dr_cur_wspeed;


                        if (s <= 0) {
                              if (dp->cdr_dstat->ds_flags & DSF_DVD)
                                    s = 4;
                              else
                                    s = 50;
                        }
                        if (wsecs > msecs)      /* Less that 1 MB */
                              wsecs = msecs;
                        wt = wsecs * 1000 / secsps / fspeed;
                        mt = (per-dminbuf)*(bsize/secsize)/100 * 1000 / secsps/s;

                        if (wt > mt)
                              wt = mt;
                        if (wt > 1000)          /* Max 1 second */
                              wt = 1000;
                        if (wt < 20)            /* Min 20 ms */
                              wt = 0;

                        if (xdebug)
                              printf(" |%3d %4dms %5dms|", wsecs, wt, mt);
                        else
                              printf(" |%3d %4dms|", wsecs, wt);
                        if (wt > 0)
                              usleep(wt*1000);
                  }
#endif
         if (dp->is_dvd) fspeed /= 9;
                  printf(" %5.1fx", fspeed);
                  printf(".");
                  savbytes = (bytes >> 20) << 20;
                  flush();
                  neednl = TRUE;
                  tlast = tcur;
            }
#ifdef      XBCAP
            if (bsize > 0) {              /* buffer size known */
                  (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree);
                  per = 100*(bsize - bfree) / bsize;
                  if (per != oper)
                        printf("[buf %3d%%] %3ld %3ld\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
                              per, bsize >> 10, bfree >> 10);
                  oper = per;
                  flush();
            }


#endif
      } while (tracksize < 0 || bytes_read < tracksize);

      if (!is_shorttrk(trackp) && (bytes / secsize) < 300) {
            /*
             * If tracksize is not known (trackp->tracksize < 0) or
             * for some strange reason we did not set padsecs properly
             * we may need to modify trackp->padsecs if
             * tracksize+padsecs is less than 300 sectors.
             */
            if ((trackp->padsecs + (bytes / secsize)) < 300)
                  trackp->padsecs = 300 - (bytes / secsize);
      }
      if (trackp->padsecs > 0) {
            Llong padbytes;

            /*
             * pad_track() is based on secsize. Compute the amount of bytes
             * assumed by pad_track().
             */
            padbytes = (Llong)trackp->padsecs * secsize;

            if (neednl) {
                  printf("\n");
                  neednl = FALSE;
            }
            if ((padbytes >> 20) > 0) {
                  neednl = TRUE;
            } else if (lverbose) {
                  printf("Track %02d: writing %3lld KB of pad data.\n",
                              track, (Llong)(padbytes >> 10));
                  neednl = FALSE;
            }
            pad_track(usalp, dp, trackp, startsec, padbytes,
                              TRUE, &savbytes);
            bytes += savbytes;
            startsec += savbytes / secsize;
      }
      printf("%sTrack %02d: Total bytes read/written: %lld/%lld (%lld sectors).\n",
            neednl?"\n":"", track, bytes_read, bytes, bytes/secsize);
      flush();
      return (0);
}

int 
pad_track(SCSI *usalp, cdr_t  *dp, track_t *trackp, long startsec, Llong amt,
                        BOOL dolast, Llong *bytesp)
{
      int   track = trackp->trackno;
      Llong bytes = 0;
      Llong savbytes = 0;
      Llong padsize = amt;
      int   secsize;
      int   secspt;
      int   bytespt;
      int   amount;
      BOOL  neednl      = FALSE;
      BOOL  islast      = FALSE;
      struct timeval tlast;
      struct timeval tcur;
      float secsps = 75.0;
long bsize;
long bfree;
#define     BCAP
#ifdef      BCAP
int per;
#ifdef      XBCAP
int oper = -1;
#endif
#endif

      if (dp->cdr_dstat->ds_flags & DSF_DVD)
            secsps = 676.27;

      usalp->silent++;
      if ((*dp->cdr_buffer_cap)(usalp, &bsize, &bfree) < 0)
            bsize = -1L;
      if (bsize == 0)         /* If we have no (known) buffer, we cannot */
            bsize = -1L;      /* retrieve the buffer fill ratio      */
      usalp->silent--;

      secsize = trackp->secsize;
      secspt = trackp->secspt;
      bytespt = secsize * secspt;

      fillbytes(buf, bytespt, '\0');

      if ((amt >> 20) > 0) {
            printf("\rTrack %02d:    0 of %4lld MB pad written.",
                                    track, amt >> 20);
            flush();
      }
      gettimeofday(&tlast, (struct timezone *)0);
      do {
            if (amt < bytespt) {
                  bytespt = roundup(amt, secsize);
                  secspt = bytespt / secsize;
            }
            if (dolast && (amt - bytespt) <= 0)
                  islast = TRUE;

            if (is_raw(trackp)) {
                  encsectors(trackp, (Uchar *)buf, startsec, secspt);
                  fillsubch(trackp, (Uchar *)buf, startsec, secspt);
            }

            amount = write_secs(usalp, dp, buf, startsec, bytespt, secspt, islast);
            if (amount < 0) {
                  printf("%swrite track pad data: error after %lld bytes\n",
                                          neednl?"\n":"", bytes);
                  if (bytesp)
                        *bytesp = bytes;
(*dp->cdr_buffer_cap)(usalp, (long *)0, (long *)0);
                  return (-1);
            }
            amt -= amount;
            bytes += amount;
            startsec += amount / secsize;

            if (lverbose && (bytes >= (savbytes + 0x100000))) {
                  int   nsecs = (bytes - savbytes) / secsize;
                  float fspeed;

                  gettimeofday(&tcur, (struct timezone *)0);
                  printf("\rTrack %02d: %4lld", track, bytes >> 20);
                  if (padsize > 0)
                        printf(" of %4lld MB", padsize >> 20);
                  else
                        printf(" MB");
                  printf(" pad written");
                  savbytes = (bytes >> 20) << 20;

#ifdef      BCAP
                  if (bsize > 0) {              /* buffer size known */
                        usalp->silent++;
                        per = (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree);
                        usalp->silent--;
                        if (per >= 0) {
                              per = 100*(bsize - bfree) / bsize;
                              if ((bsize - bfree) <= amount || per <= 5)
                                    dp->cdr_dstat->ds_buflow++;
                              if (per < (int)dp->cdr_dstat->ds_minbuf &&
                                  (startsec*secsize) > bsize) {
                                    dp->cdr_dstat->ds_minbuf = per;
                              }
                              printf(" [buf %3d%%]", per);
#ifdef      BCAPDBG
                              printf(" %3ld %3ld", bsize >> 10, bfree >> 10);
#endif
                        }
                  }
#endif
                  tlast.tv_sec = tcur.tv_sec - tlast.tv_sec;
                  tlast.tv_usec = tcur.tv_usec - tlast.tv_usec;
                  while (tlast.tv_usec < 0) {
                        tlast.tv_usec += 1000000;
                        tlast.tv_sec -= 1;
                  }
                  fspeed = (nsecs / secsps) /
                        (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001);
                  if (fspeed > 999.0)
                        fspeed = 999.0;
                  printf(" %5.1fx", fspeed);
                  printf(".");
                  flush();
                  neednl = TRUE;
                  tlast = tcur;
            }
      } while (amt > 0);

      if (bytesp)
            *bytesp = bytes;
      if (bytes == 0)
            return (0);
      return (bytes > 0 ? 1:-1);
}

#ifdef      USE_WRITE_BUF
int 
write_buf(SCSI *usalp, cdr_t *dp, track_t *trackp, char *bp, long startsec, 
              Llong amt, int secsize, BOOL dolast, Llong *bytesp)
{
      int   track = trackp->trackno;
      Llong bytes = 0;
      Llong savbytes = 0;
/*    int   secsize;*/
      int   secspt;
      int   bytespt;
      int   amount;
      BOOL  neednl      = FALSE;
      BOOL  islast      = FALSE;

/*    secsize = trackp->secsize;*/
/*    secspt = trackp->secspt;*/

      secspt = bufsize/secsize;
      secspt = min(255, secspt);
      bytespt = secsize * secspt;

/*    fillbytes(buf, bytespt, '\0');*/

      if ((amt >> 20) > 0) {
            printf("\rTrack %02d:   0 of %4ld MB pad written.",
                                    track, amt >> 20);
            flush();
      }
      do {
            if (amt < bytespt) {
                  bytespt = roundup(amt, secsize);
                  secspt = bytespt / secsize;
            }
            if (dolast && (amt - bytespt) <= 0)
                  islast = TRUE;

            amount = write_secs(usalp, dp, bp, startsec, bytespt, secspt, islast);
            if (amount < 0) {
                  printf("%swrite track data: error after %ld bytes\n",
                                          neednl?"\n":"", bytes);
                  if (bytesp)
                        *bytesp = bytes;
(*dp->cdr_buffer_cap)(usalp, (long *)0, (long *)0);
                  return (-1);
            }
            amt -= amount;
            bytes += amount;
            startsec += amount / secsize;

            if (lverbose && (bytes >= (savbytes + 0x100000))) {
                  printf("\rTrack %02d: %3ld", track, bytes >> 20);
                  savbytes = (bytes >> 20) << 20;
                  flush();
                  neednl = TRUE;
            }
      } while (amt > 0);

      if (bytesp)
            *bytesp = bytes;
      return (bytes);
}
#endif      /* USE_WRITE_BUF */

static void 
printdata(int track, track_t *trackp)
{
      if (trackp->itracksize >= 0) {
            printf("Track %02d: data  %4lld MB        ",
                              track, (Llong)(trackp->itracksize >> 20));
      } else {
            printf("Track %02d: data  unknown length",
                              track);
      }
      if (trackp->padsecs > 0) {
            Llong padbytes = (Llong)trackp->padsecs * trackp->isecsize;

            if ((padbytes >> 20) > 0)
                  printf(" padsize: %4lld MB", (Llong)(padbytes >> 20));
            else
                  printf(" padsize: %4lld KB", (Llong)(padbytes >> 10));
      }
      if (trackp->pregapsize != (trackp->flags & TI_DVD)? 0 : 150) {
            printf(" pregapsize: %3ld", trackp->pregapsize);
      }
      if (xdebug)
            printf(" START: %ld SECTORS: %ld INDEX0 %ld",
                  trackp->trackstart, trackp->tracksecs, trackp->index0start);
      printf("\n");
}

static void 
printaudio(int track, track_t *trackp)
{
      if (trackp->itracksize >= 0) {
            printf("Track %02d: audio %4lld MB (%02d:%02d.%02d) %spreemp%s%s",
                  track, (Llong)(trackp->itracksize >> 20),
                  minutes(trackp->itracksize),
                  seconds(trackp->itracksize),
                  hseconds(trackp->itracksize),
                  is_preemp(trackp) ? "" : "no ",
                  is_swab(trackp) ? " swab":"",
                  ((trackp->itracksize < 300L*trackp->isecsize) ||
                  (trackp->itracksize % trackp->isecsize)) &&
                  is_pad(trackp) ? " pad" : "");
      } else {
            printf("Track %02d: audio unknown length    %spreemp%s%s",
                  track, is_preemp(trackp) ? "" : "no ",
                  is_swab(trackp) ? " swab":"",
                  (trackp->itracksize % trackp->isecsize) && is_pad(trackp) ? " pad" : "");
      }
      if (is_scms(trackp))
            printf(" scms");
      else if (is_copy(trackp))
            printf(" copy");
      else
            printf("     ");

      if (trackp->padsecs > 0) {
            Llong padbytes = (Llong)trackp->padsecs * trackp->isecsize;

            if ((padbytes >> 20) > 0)
                  printf(" padsize: %4lld MB", (Llong)(padbytes >> 20));
            else
                  printf(" padsize: %4lld KB", (Llong)(padbytes >> 10));
            printf(" (%02d:%02d.%02d)",
                  Sminutes(trackp->padsecs),
                  Sseconds(trackp->padsecs),
                  Shseconds(trackp->padsecs));
      }
      if (trackp->pregapsize != ((trackp->flags & TI_DVD)? 0 : 150) || xdebug > 0) {
            printf(" pregapsize: %3ld", trackp->pregapsize);
      }
      if (xdebug)
            printf(" START: %ld SECTORS: %ld INDEX0 %ld",
                  trackp->trackstart, trackp->tracksecs, trackp->index0start);
      printf("\n");
}

static void 
checkfile(int track, track_t *trackp)
{
      if (trackp->itracksize > 0 &&
                  is_audio(trackp) &&
                  ((!is_shorttrk(trackp) &&
                  (trackp->itracksize < 300L*trackp->isecsize)) ||
                  (trackp->itracksize % trackp->isecsize)) &&
                                    !is_pad(trackp)) {
            errmsgno(EX_BAD, "Bad audio track size %lld for track %02d.\n",
                        (Llong)trackp->itracksize, track);
            errmsgno(EX_BAD, "Audio tracks must be at least %ld bytes and a multiple of %d.\n",
                        300L*trackp->isecsize, trackp->isecsize);

            if (!is_shorttrk(trackp) && (trackp->itracksize < 300L*trackp->isecsize))
                  comerrno(EX_BAD, "See -shorttrack option.\n");
            if (!is_pad(trackp) && (trackp->itracksize % trackp->isecsize))
                  comerrno(EX_BAD, "See -pad option.\n");
      }

      if (lverbose == 0 && xdebug == 0)
            return;

      if (is_audio(trackp))
            printaudio(track, trackp);
      else
            printdata(track, trackp);
}

static int 
checkfiles(int tracks, track_t *trackp)
{
      int   i;
      int   isaudio = 1;
      int   starttrack = 1;
      int   endtrack = tracks;

      if (xdebug) {
            /*
             * Include Lead-in & Lead-out.
             */
            starttrack--;
            endtrack++;
      }
      for (i = starttrack; i <= endtrack; i++) {
            if (!is_audio(&trackp[i]))
                  isaudio = 0;
            if (xdebug)
                  printf("SECTYPE %X ", trackp[i].sectype);
            checkfile(i, &trackp[i]);
      }
      return (isaudio);
}

static void 
setleadinout(int tracks, track_t *trackp)
{
      /*
       * Set some values for track 0 (the lead-in)
       */
      if (!is_clone(&trackp[0])) {
            trackp[0].sectype = trackp[1].sectype;
            trackp[0].dbtype  = trackp[1].dbtype;
            trackp[0].dataoff = trackp[1].dataoff;

            /*
             * XXX Which other flags should be copied to Track 0 ?
             */
            if (is_audio(&trackp[1]))
                  trackp[0].flags |= TI_AUDIO;
      }

      /*
       * Set some values for track 0xAA (the lead-out)
       */
      trackp[tracks+1].pregapsize = 0;
      trackp[tracks+1].isecsize   = trackp[tracks].isecsize;
      trackp[tracks+1].secsize    = trackp[tracks].secsize;

      if (!is_clone(&trackp[0])) {
            trackp[tracks+1].tracktype = trackp[tracks].tracktype;
            trackp[tracks+1].sectype   = trackp[tracks].sectype;
            trackp[tracks+1].dbtype    = trackp[tracks].dbtype;
            trackp[tracks+1].dataoff   = trackp[tracks].dataoff;
      }

      trackp[tracks+1].flags = trackp[tracks].flags;
}

static void 
setpregaps(int tracks, track_t *trackp)
{
      int   i;
      int   sectype;
      long  pregapsize;
      track_t     *tp;

      sectype = trackp[1].sectype;
      sectype &= ST_MASK;

      for (i = 1; i <= tracks; i++) {
            tp = &trackp[i];
            if (tp->pregapsize == -1L) {
                  tp->pregapsize = 150;         /* Default CD Pre GAP*/
                  if (trackp->flags & TI_DVD) {
                        tp->pregapsize = 0;
                  } else if (sectype != (tp->sectype & ST_MASK)) {
                        tp->pregapsize = 255;   /* Pre GAP is 255 */
                        tp->flags &= ~TI_PREGAP;
                  }
            }
            sectype = tp->sectype & ST_MASK;    /* Save old sectype */
      }
      trackp[tracks+1].pregapsize = 0;
      trackp[tracks+1].index0start = 0;

      for (i = 1; i <= tracks; i++) {
            /*
             * index0start is set below tracksecks if this track contains
             * the pregap (index 0) of the next track.
             */
            trackp[i].index0start = trackp[i].tracksecs;

            pregapsize = trackp[i+1].pregapsize;
            if (is_pregap(&trackp[i+1]) && pregapsize > 0)
                  trackp[i].index0start -= pregapsize;
      }
}

/*
 * Check total size of the medium
 */
static long 
checktsize(int tracks, track_t *trackp)
{
      int   i;
      Llong curr;
      Llong total = -150;     /* CD track #1 pregap compensation */
      Ullong      btotal;
      track_t     *tp;

      if (trackp->flags & TI_DVD)
            total = 0;
      for (i = 1; i <= tracks; i++) {
            tp = &trackp[i];
            if (!is_pregap(tp))
                  total += tp->pregapsize;

            if (lverbose > 1) {
                  printf("track: %d start: %lld pregap: %ld\n",
                              i, total, tp->pregapsize);
            }
            tp->trackstart = total;
            if (tp->itracksize >= 0) {
                  curr = (tp->itracksize + (tp->isecsize-1)) / tp->isecsize;
                  curr += tp->padsecs;
                  /*
                   * Minimum track size is 4s
                   */
                  if (!is_shorttrk(tp) && curr < 300)
                        curr = 300;
                  if ((trackp->flags & TI_DVD) == 0) {
                        /*
                         * XXX Was passiert hier bei is_packet() ???
                         */
                        if (is_tao(tp) && !is_audio(tp)) {
                              curr += 2;
                        }
                  }
                  total += curr;
            } else if (is_sao(tp) || is_raw(tp)) {
                  errmsgno(EX_BAD, "Track %d has unknown length.\n", i);
                  comerrno(EX_BAD,
                  "Use tsize= option in %s mode to specify track size.\n",
                  is_sao(tp) ? "SAO" : "RAW");
            }
      }
      tp = &trackp[i];
      tp->trackstart = total;
      tp->tracksecs = 6750;         /* Size of first session Lead-Out */
      if (!lverbose)
            return (total);

      if (trackp->flags & TI_DVD)
            btotal = (Ullong)total * 2048;
      else
            btotal = (Ullong)total * 2352;
/* XXX CD Sector Size ??? */
      if (tracks > 0) {
            if (trackp->flags & TI_DVD) {
                  printf("Total size:     %4llu MB = %lld sectors\n",
                        btotal >> 20, total);
            } else {
                  printf("Total size:     %4llu MB (%02d:%02d.%02d) = %lld sectors\n",
                        btotal >> 20,
                        minutes(btotal),
                        seconds(btotal),
                        hseconds(btotal), total);
                  btotal += 150 * 2352;
                  printf("Lout start:     %4llu MB (%02d:%02d/%02d) = %lld sectors\n",
                        btotal >> 20,
                        minutes(btotal),
                        seconds(btotal),
                        frames(btotal), total);
            }
      }
      return (total);
}

static void 
opentracks(track_t *trackp)
{
      track_t     *tp;
      int   i;
      int   tracks = trackp[0].tracks;

      Llong tracksize;
      int   secsize;

      for (i = 1; i <= tracks; i++) {
            tp = &trackp[i];

            if (auinfosize(tp->filename, tp)) {
                  /*
                   * open stdin
                   */
                  tp->xfp = xopen(NULL, O_RDONLY|O_BINARY, 0);
            } else if (strcmp("-", tp->filename) == 0) {
                  /*
                   * open stdin
                   */
                  tp->xfp = xopen(NULL, O_RDONLY|O_BINARY, 0);
            } else {
                  if ((tp->xfp = xopen(tp->filename,
                              O_RDONLY|O_BINARY, 0)) == NULL) {
                        comerr("Cannot open '%s'.\n", tp->filename);
                  }
            }

            checksize(tp);
            tracksize = tp->itracksize;
            secsize = tp->isecsize;
            if (!is_shorttrk(tp) &&
                tracksize > 0 && (tracksize / secsize) < 300) {

                  tracksize = roundup(tracksize, secsize);
                  if ((tp->padsecs +
                      (tracksize / secsize)) < 300) {
                        tp->padsecs =
                              300 - tracksize / secsize;
                  }
                  if (xdebug) {
                        printf("TRACK %d SECTORS: %ld",
                              i, tp->tracksecs);
                        printf(" pasdize %lld (%ld sectors)\n",
                              (Llong)tp->padsecs * secsize,
                              tp->padsecs);
                  }
            }
#ifdef      AUINFO
            if (tp->flags & TI_USEINFO) {
                  auinfo(tp->filename, i, trackp);
                  if (lverbose > 0 && i == 1)
                        printf("pregap1: %ld\n", trackp[1].pregapsize);
            }
#endif
            /*
             * tracksecks is total numbers of sectors in track (starting from
             * index 0).
             */
            if (tp->padsecs > 0)
                  tp->tracksecs += tp->padsecs;

            if (debug) {
                  printf("File: '%s' itracksize: %lld isecsize: %d tracktype: %d = %s sectype: %X = %s dbtype: %s flags %X\n",
                        tp->filename, (Llong)tp->itracksize,
                        tp->isecsize,
                        tp->tracktype & TOC_MASK, toc2name[tp->tracktype & TOC_MASK],
                        tp->sectype, st2name[tp->sectype & ST_MASK], db2name[tp->dbtype], tp->flags);
            }
      }
}

static void 
checksize(track_t *trackp)
{
      struct stat st;
      Llong       lsize;
      int         f = -1;

      if (trackp->xfp != NULL)
            f = xfileno(trackp->xfp);

      /*
       * If the current input file is a regular file and
       * 'padsize=' has not been specified,
       * use fstat() or file parser to get the size of the file.
       */
      if (trackp->itracksize < 0 && (trackp->flags & TI_ISOSIZE) != 0) {
            lsize = isosize(f);
            trackp->itracksize = lsize;
            if (trackp->itracksize != lsize)
                  comerrno(EX_BAD, "This OS cannot handle large ISO-9660 images.\n");
      }
      if (trackp->itracksize < 0 && (trackp->flags & TI_NOAUHDR) == 0) {
            lsize = ausize(f);
            trackp->itracksize = lsize;
            if (trackp->itracksize != lsize)
                  comerrno(EX_BAD, "This OS cannot handle large audio images.\n");
      }
      if (trackp->itracksize < 0 && (trackp->flags & TI_NOAUHDR) == 0) {
            lsize = wavsize(f);
            trackp->itracksize = lsize;
            if (trackp->itracksize != lsize)
                  comerrno(EX_BAD, "This OS cannot handle large WAV images.\n");
            if (trackp->itracksize > 0)   /* Force little endian input */
                  trackp->flags |= TI_SWAB;
      }
      if (trackp->itracksize == AU_BAD_CODING) {
            comerrno(EX_BAD, "Inappropriate audio coding in '%s'.\n",
                                          trackp->filename);
      }
      if (trackp->itracksize < 0 &&
                  fstat(f, &st) >= 0 && S_ISREG(st.st_mode)) {
            trackp->itracksize = st.st_size;
      }
      if (trackp->itracksize >= 0) {
            /*
             * We do not allow cdrecord to start if itracksize is not
             * a multiple of isecsize or we are allowed to pad to secsize via -pad.
             * For this reason, we may safely always assume padding.
             */
            trackp->tracksecs = (trackp->itracksize + trackp->isecsize -1) / trackp->isecsize;
            trackp->tracksize = (trackp->itracksize / trackp->isecsize) * trackp->secsize
                              + trackp->itracksize % trackp->isecsize;
      } else {
            trackp->tracksecs = -1L;
      }
}

static BOOL 
checkdsize(SCSI *usalp, cdr_t *dp, long tsize, int flags)
{
      long  startsec = 0L;
      long  endsec = 0L;
      dstat_t     *dsp = dp->cdr_dstat;
      int   profile;

      usalp->silent++;
      (*dp->cdr_next_wr_address)(usalp, (track_t *)0, &startsec);
      usalp->silent--;

      /*
       * This only should happen when the drive is currently in SAO mode.
       * We rely on the drive being in TAO mode, a negative value for
       * startsec is not correct here it may be caused by bad firmware or
       * by a drive in SAO mode. In SAO mode the drive will report the
       * pre-gap as part of the writable area.
       */
      if (startsec < 0)
            startsec = 0;

      /*
       * Size limitations (sectors) for CD's:
       *
       *          404850 == 90 min  Red book calls this the
       *                            first negative time
       *                            allows lead out start up to
       *                            block 404700
       *
       *          449850 == 100 min This is the first time that
       *                            is no more representable
       *                            in a two digit BCD number.
       *                            allows lead out start up to
       *                            block 449700
       *
       *          ~540000 == 120 min      The largest CD ever made.
       *
       *          ~650000 == 1.3 GB a Double Density (DD) CD.
       */

      endsec = startsec + tsize;
      dsp->ds_startsec = startsec;
      dsp->ds_endsec = endsec;


      if (dsp->ds_maxblocks > 0) {
            /*
             * dsp->ds_maxblocks > 0 (disk capacity is known).
             */
            if (lverbose)
                  printf("Blocks total: %ld Blocks current: %ld Blocks remaining: %ld\n",
                              (long)dsp->ds_maxblocks,
                              (long)dsp->ds_maxblocks - startsec,
                              (long)dsp->ds_maxblocks - endsec);

            if (endsec > dsp->ds_maxblocks) {
                  if (dsp->ds_flags & DSF_DVD) {      /* A DVD and not a CD */
                        /*
                         * There is no overburning on DVD...
                         */
                        errmsgno(EX_BAD,
                        "Data does not fit on current disk.\n");
                        goto toolarge;
                  }
                  errmsgno(EX_BAD,
                  "WARNING: Data may not fit on current disk.\n");

                  /* XXX Check for flags & CDR_NO_LOLIMIT */
/*                goto toolarge;*/
            }
            if (lverbose && dsp->ds_maxrblocks > 0)
                  printf("RBlocks total: %ld RBlocks current: %ld RBlocks remaining: %ld\n",
                              (long)dsp->ds_maxrblocks,
                              (long)dsp->ds_maxrblocks - startsec,
                              (long)dsp->ds_maxrblocks - endsec);
            if (dsp->ds_maxrblocks > 0 && endsec > dsp->ds_maxrblocks) {
                  errmsgno(EX_BAD,
                  "Data does not fit on current disk.\n");
                  goto toolarge;
            }
            if ((endsec > dsp->ds_maxblocks && endsec > 404700) ||
                (dsp->ds_maxrblocks > 404700 && 449850 > dsp->ds_maxrblocks)) {
                  /*
                   * Assume that this must be a CD and not a DVD.
                   * So this is a non Red Book compliant CD with a
                   * capacity between 90 and 99 minutes.
                   */
                  if (dsp->ds_maxrblocks > 404700)
                        printf("RedBook total: %ld RedBook current: %ld RedBook remaining: %ld\n",
                              404700L,
                              404700L - startsec,
                              404700L - endsec);
                  if (endsec > dsp->ds_maxblocks && endsec > 404700) {
                        if ((flags & (F_IGNSIZE|F_FORCE)) == 0) {
                              errmsgno(EX_BAD,
                              "Notice: Most recorders cannot write CD's >= 90 minutes.\n");
                              errmsgno(EX_BAD,
                              "Notice: Use -ignsize option to allow >= 90 minutes.\n");
                        }
                        goto toolarge;
                  }
            }
      } else {
            /*
             * dsp->ds_maxblocks == 0 (disk capacity is unknown).
             */
              profile = dp->profile;
              if (endsec >= (4200000)) {
                    errmsgno(EX_BAD,
                  "ERROR: Could not manage to find medium size, and more than 8.0 GB of data.\n");
                    goto toolarge;  
            } else if (profile != 0x2B) { 
                if (endsec >= (2300000)) {
                  errmsgno(EX_BAD,
                        "ERROR: Could not manage to find medium size, and more than 4.3 GB of data for a non dual layer disc.\n");
                  goto toolarge;
                } else if (endsec >= (405000-300)) {            /*<90 min disk or DVD*/
                  errmsgno(EX_BAD,
                        "WARNING: Could not manage to find medium size, and more than 90 mins of data.\n");
                } else if (endsec >= (333000-150)) {        /* 74 min disk*/
                  errmsgno(EX_BAD,
                        "WARNING: Data may not fit on standard 74min disk.\n");
                }
            }
      }
      if (dsp->ds_maxblocks <= 0 || endsec <= dsp->ds_maxblocks)
            return (TRUE);
      /* FALLTHROUGH */
toolarge:
      if (dsp->ds_maxblocks > 0 && endsec > dsp->ds_maxblocks) {
            if ((flags & (F_OVERBURN|F_IGNSIZE|F_FORCE)) != 0) {
                  if (dsp->ds_flags & DSF_DVD) {      /* A DVD and not a CD */
                        errmsgno(EX_BAD,
                        "Notice: -overburn is not expected to work with DVD media.\n");
                  }
                  errmsgno(EX_BAD,
                        "Notice: Overburning active. Trying to write more than the official disk capacity.\n");
                  return (TRUE);
            } else {
                  if ((dsp->ds_flags & DSF_DVD) == 0) {     /* A CD and not a DVD */
                        errmsgno(EX_BAD,
                        "Notice: Use -overburn option to write more than the official disk capacity.\n");
                        errmsgno(EX_BAD,
                        "Notice: Most CD-writers do overburning only on SAO or RAW mode.\n");
                  }
                  return (FALSE);
            }
      }
      if (dsp->ds_maxblocks < 449850) {
            if ((dsp->ds_flags & DSF_DVD) == 0) {     /* A CD and not a DVD */
                  if (endsec <= dsp->ds_maxblocks)
                        return (TRUE);
                  errmsgno(EX_BAD, "Cannot write more than remaining DVD capacity.\n");
                  return (FALSE);
            }
            /*
             * Assume that this must be a CD and not a DVD.
             */
            if (endsec > 449700) {
                  errmsgno(EX_BAD, "Cannot write CD's >= 100 minutes.\n");
                  return (FALSE);
            }
      }
      if ((flags & (F_IGNSIZE|F_FORCE)) != 0)
            return (TRUE);
      return (FALSE);
}

static void 
raise_fdlim()
{
#ifdef      RLIMIT_NOFILE

      struct rlimit     rlim;

      /*
       * Set max # of file descriptors to be able to hold all files open
       */
      getrlimit(RLIMIT_NOFILE, &rlim);
      if (rlim.rlim_cur >= (MAX_TRACK + 10))
            return;

      rlim.rlim_cur = MAX_TRACK + 10;
      if (rlim.rlim_cur > rlim.rlim_max)
            errmsgno(EX_BAD,
                  "Warning: low file descriptor limit (%lld)\n",
                                    (Llong)rlim.rlim_max);
      setrlimit(RLIMIT_NOFILE, &rlim);

#endif      /* RLIMIT_NOFILE */
}

static void 
raise_memlock()
{
#ifdef      RLIMIT_MEMLOCK
      struct rlimit rlim;

      rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;

      if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0)
            errmsg("Warning: Cannot raise RLIMIT_MEMLOCK limits.\n");
#endif      /* RLIMIT_MEMLOCK */
}

char  *opts =
"help,version,checkdrive,prcap,inq,devices,scanbus,reset,abort,overburn,ignsize,useinfo,dev*,timeout#,driver*,driveropts*,setdropts,tsize&,padsize&,pregap&,defpregap&,speed#,load,lock,eject,dummy,msinfo,toc,atip,multi,fix,nofix,waiti,immed,debug#,d+,kdebug#,kd#,verbose+,v+,Verbose+,V+,x+,xd#,silent,s,audio,data,mode2,xa,xa1,xa2,xamix,cdi,isosize,nopreemp,preemp,nocopy,copy,nopad,pad,swab,fs&,ts&,blank&,format,formattype&,pktsize#,packet,noclose,force,tao,dao,sao,raw,raw96r,raw96p,raw16,clone,scms,isrc*,mcn*,index*,cuefile*,textfile*,text,shorttrack,noshorttrack,gracetime#,minbuf#,msifile*";

/*
 * Defines used to find whether a write mode has been specified.
 */
#define     M_TAO       1     /* Track at Once mode */
#define     M_SAO       2     /* Session at Once mode (also known as DAO) */
#define     M_RAW       4     /* Raw mode */
#define     M_PACKET    8     /* Packed mode */
static int 
gargs(int ac, char **av, int *tracksp, track_t *trackp, char **devp, 
            int *timeoutp, cdr_t **dpp, int *speedp, long *flagsp, int *blankp, 
            int *formatp)
{
      int   cac;
      char  * const*cav;
      char  *driver = NULL;
      char  *dev = NULL;
      char  *isrc = NULL;
      char  *mcn = NULL;
      char  *tindex = NULL;
      char  *cuefile = NULL;
      char  *textfile = NULL;
      long  bltype = -1;
      int   doformat = 0;
      int   formattype = -1;
      Llong tracksize;
      Llong padsize;
      long  pregapsize;
      long  defpregap = -1L;
      int   pktsize;
      int   speed = -1;
      int   help = 0;
      int   version = 0;
      int   checkdrive = 0;
      int   setdropts = 0;
      int   prcap = 0;
      int   inq = 0;
      int   scanbus = 0;
      int   reset = 0;
      int   doabort = 0;
      int   overburn = 0;
      int   ignsize = 0;
      int   useinfo = 0;
      int   load = 0;
      int   lock = 0;
      int   eject = 0;
      int   dummy = 0;
      int   msinfo = 0;
      int   toc = 0;
      int   atip = 0;
      int   multi = 0;
      int   fix = 0;
      int   nofix = 0;
      int   waiti = 0;
      int   immed = 0;
      int   audio;
      int   autoaudio = 0;
      int   data;
      int   mode2;
      int   xa;
      int   xa1;
      int   xa2;
      int   xamix;
      int   cdi;
      int   isize;
      int   ispacket = 0;
      int   noclose = 0;
      int   force = 0;
      int   tao = 0;
      int   dao = 0;
      int   raw = 0;
      int   raw96r = 0;
      int   raw96p = 0;
      int   raw16 = 0;
      int   clone = 0;
      int   scms = 0;
      int   preemp = 0;
      int   nopreemp;
      int   copy = 0;
      int   nocopy;
      int   pad = 0;
      int   bswab = 0;
      int   nopad;
      int   usetext = 0;
      int   shorttrack = 0;
      int   noshorttrack;
      int   flags;
      int   tracks = *tracksp;
      int   tracktype = TOC_ROM;
/*    int   sectype = ST_ROM_MODE1 | ST_MODE_1;*/
      int   sectype = SECT_ROM;
      int   dbtype = DB_ROM_MODE1;
      int   secsize = DATA_SEC_SIZE;
      int   dataoff = 16;
      int   ga_ret;
      int   wm = 0;

      trackp[0].flags |= TI_TAO;
      trackp[1].pregapsize = -1;
      *flagsp |= F_WRITE;

      cac = --ac;
      cav = ++av;
      for (; ; cac--, cav++) {
            tracksize = (Llong)-1L;
            padsize = (Llong)0L;
            pregapsize = defpregap;
            audio = data = mode2 = xa = xa1 = xa2 = xamix = cdi = 0;
            isize = nopreemp = nocopy = nopad = noshorttrack = 0;
            pktsize = 0;
            isrc = NULL;
            tindex = NULL;
            /*
             * Get options up to next file type arg.
             */
            if ((ga_ret = getargs(&cac, &cav, opts,
                        &help, &version, &checkdrive, &prcap,
                        &inq, &scandevs, &scanbus, &reset, &doabort, &overburn, &ignsize,
                        &useinfo,
                        devp, timeoutp, &driver, &driveropts, &setdropts,
                        getllnum, &tracksize,
                        getllnum, &padsize,
                        getnum, &pregapsize,
                        getnum, &defpregap,
                        &speed,
                        &load, &lock,
                        &eject, &dummy, &msinfo, &toc, &atip,
                        &multi, &fix, &nofix, &waiti, &immed,
                        &debug, &debug,
                        &kdebug, &kdebug,
                        &lverbose, &lverbose,
                        &scsi_verbose, &scsi_verbose,
                        &xdebug, &xdebug,
                        &silent, &silent,
                        &audio, &data, &mode2,
                        &xa, &xa1, &xa2, &xamix, &cdi,
                        &isize,
                        &nopreemp, &preemp,
                        &nocopy, &copy,
                        &nopad, &pad, &bswab, getnum, &fs, getnum, &bufsize,
                        getbltype, &bltype, &doformat, getformattype, &formattype, &pktsize,
                        &ispacket, &noclose, &force,
                        &tao, &dao, &dao, &raw, &raw96r, &raw96p, &raw16,
                        &clone,
                        &scms, &isrc, &mcn, &tindex,
                        &cuefile, &textfile, &usetext,
                        &shorttrack, &noshorttrack,
                        &gracetime, &dminbuf, &msifile)) < 0) {
                  errmsgno(EX_BAD, "Bad Option: %s.\n", cav[0]);
                  susage(EX_BAD);
            }
            if (help)
                  usage(0);
            if (tracks == 0) {
                  if (driver)
                        set_cdrcmds(driver, dpp);
                  if (version)
                        *flagsp |= F_VERSION;
                  if (checkdrive)
                        *flagsp |= F_CHECKDRIVE;
                  if (prcap)
                        *flagsp |= F_PRCAP;
                  if (inq)
                        *flagsp |= F_INQUIRY;
                  if (scanbus || scandevs) /* scandevs behaves similarly WRT in the legacy code, just the scan operation is different */
                        *flagsp |= F_SCANBUS;
                  if (reset)
                        *flagsp |= F_RESET;
                  if (doabort)
                        *flagsp |= F_ABORT;
                  if (overburn)
                        *flagsp |= F_OVERBURN;
                  if (ignsize)
                        *flagsp |= F_IGNSIZE;
                  if (load)
                        *flagsp |= F_LOAD;
                  if (lock)
                        *flagsp |= F_DLCK;
                  if (eject)
                        *flagsp |= F_EJECT;
                  if (dummy)
                        *flagsp |= F_DUMMY;
                  if (setdropts)
                        *flagsp |= F_SETDROPTS;
                  if(msifile)
                        msinfo++;
                  if (msinfo)
                        *flagsp |= F_MSINFO;
                  if (toc) {
                        *flagsp |= F_TOC;
                        *flagsp &= ~F_WRITE;
                  }
                  if (atip) {
                        *flagsp |= F_PRATIP;
                        *flagsp &= ~F_WRITE;
                  }
                  if (multi) {
                        /*
                         * 2048 Bytes user data
                         */
                        *flagsp |= F_MULTI;
                        tracktype = TOC_XA2;
                        sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1;
                        sectype = SECT_MODE_2_F1;
                        dbtype = DB_XA_MODE2;   /* XXX -multi nimmt DB_XA_MODE2_F1 !!! */
                        secsize = DATA_SEC_SIZE;      /* 2048 */
                        dataoff = 24;
                  }
                  if (fix)
                        *flagsp |= F_FIX;
                  if (nofix)
                        *flagsp |= F_NOFIX;
                  if (waiti)
                        *flagsp |= F_WAITI;
                  if (immed)
                        *flagsp |= F_IMMED;
                  if (force)
                        *flagsp |= F_FORCE;

                  if (bltype >= 0) {
                        *flagsp |= F_BLANK;
                        *blankp = bltype;
                  }
                  if (doformat > 0) {
                        *flagsp |= F_FORMAT;
                        *formatp |= FULL_FORMAT;
                  }
                  if (formattype >= 0) {
                        *flagsp |= F_FORMAT;
                        *formatp |= formattype;
                  }
                  if (ispacket)
                        wm |= M_PACKET;
                  if (tao)
                        wm |= M_TAO;
                  if (dao) {
                        *flagsp |= F_SAO;
                        trackp[0].flags &= ~TI_TAO;
                        trackp[0].flags |= TI_SAO;
                        wm |= M_SAO;

                  } else if ((raw == 0) && (raw96r + raw96p + raw16) > 0)
                        raw = 1;
                  if ((raw != 0) && (raw96r + raw96p + raw16) == 0)
                        raw96r = 1;
                  if (raw96r) {
                        if (!dao)
                              *flagsp |= F_RAW;
                        trackp[0].flags &= ~TI_TAO;
                        trackp[0].flags |= TI_RAW;
                        trackp[0].flags |= TI_RAW96R;
                        wm |= M_RAW;
                  }
                  if (raw96p) {
                        if (!dao)
                              *flagsp |= F_RAW;
                        trackp[0].flags &= ~TI_TAO;
                        trackp[0].flags |= TI_RAW;
                        wm |= M_RAW;
                  }
                  if (raw16) {
                        if (!dao)
                              *flagsp |= F_RAW;
                        trackp[0].flags &= ~TI_TAO;
                        trackp[0].flags |= TI_RAW;
                        trackp[0].flags |= TI_RAW16;
                        wm |= M_RAW;
                  }
                  if (mcn) {
#ifdef      AUINFO
                        setmcn(mcn, &trackp[0]);
#else
                        trackp[0].isrc = malloc(16);
                        fillbytes(trackp[0].isrc, 16, '\0');
                        strncpy(trackp[0].isrc, mcn, 13);
#endif
                        mcn = NULL;
                  }
                  if ((raw96r + raw96p + raw16) > 1) {
                        errmsgno(EX_BAD, "Too many raw modes.\n");
                        comerrno(EX_BAD, "Only one of -raw16, -raw96p, -raw96r allowed.\n");
                  }
                  if ((tao + ispacket + dao + raw) > 1) {
                        errmsgno(EX_BAD, "Too many write modes.\n");
                        comerrno(EX_BAD, "Only one of -packet, -dao, -raw allowed.\n");
                  }
                  if (dao && (raw96r + raw96p + raw16) > 0) {
                        if (raw16)
                              comerrno(EX_BAD, "SAO RAW writing does not allow -raw16.\n");
                        if (!clone)
                              comerrno(EX_BAD, "SAO RAW writing only makes sense in clone mode.\n");
#ifndef     CLONE_WRITE
                        comerrno(EX_BAD, "SAO RAW writing not yet implemented.\n");
#endif
                        comerrno(EX_BAD, "SAO RAW writing not yet implemented.\n");
                  }
                  if (clone) {
                        *flagsp |= F_CLONE;
                        trackp[0].flags |= TI_CLONE;
#ifndef     CLONE_WRITE
                        comerrno(EX_BAD, "Clone writing not compiled in.\n");
#endif
                  }
                  if (textfile) {
                        if (!checktextfile(textfile)) {
                              if ((*flagsp & F_WRITE) != 0) {
                                    comerrno(EX_BAD,
                                          "Cannot use '%s' as CD-Text file.\n",
                                          textfile);
                              }
                        }
                        if ((*flagsp & F_WRITE) != 0) {
                              if ((dao + raw96r + raw96p) == 0)
                                    comerrno(EX_BAD,
                                          "CD-Text needs -dao, -raw96r or -raw96p.\n");
                        }
                        trackp[0].flags |= TI_TEXT;
                  }
                  version = checkdrive = prcap = inq = scanbus = reset = doabort =
                  overburn = ignsize =
                  load = lock = eject = dummy = msinfo = toc = atip = multi = fix = nofix =
                  waiti = immed = force = dao = setdropts = 0;
                  raw96r = raw96p = raw16 = clone = 0;
            } else if ((version + checkdrive + prcap + inq + scanbus +
                      reset + doabort + overburn + ignsize +
                      load + lock + eject + dummy + msinfo + toc + atip + multi + fix + nofix +
                      waiti + immed + force + dao + setdropts +
                      raw96r + raw96p + raw16 + clone) > 0 ||
                        mcn != NULL)
                  comerrno(EX_BAD, "Badly placed option. Global options must be before any track.\n");

            if (nopreemp)
                  preemp = 0;
            if (nocopy)
                  copy = 0;
            if (nopad)
                  pad = 0;
            if (noshorttrack)
                  shorttrack = 0;

            if ((audio + data + mode2 + xa + xa1 + xa2 + xamix) > 1) {
                  errmsgno(EX_BAD, "Too many types for track %d.\n", tracks+1);
                  comerrno(EX_BAD, "Only one of -audio, -data, -mode2, -xa, -xa1, -xa2, -xamix allowed.\n");
            }
            if (ispacket && audio) {
                  comerrno(EX_BAD, "Audio data cannot be written in packet mode.\n");
            }
            /*
             * Check whether the next argument is a file type arg.
             * If this is true, then we got a track file name.
             * If getargs() did previously return NOTAFLAG, we may have hit
             * an argument that has been escaped via "--", so we may not
             * call getfiles() again in this case. If we would call
             * getfiles() and the current arg has been escaped and looks
             * like an option, a call to getfiles() would skip it.
             */
            if (ga_ret != NOTAFLAG)
                  ga_ret = getfiles(&cac, &cav, opts);
            if (autoaudio) {
                  autoaudio = 0;
                  tracktype = TOC_ROM;
                  sectype = ST_ROM_MODE1 | ST_MODE_1;
                  sectype = SECT_ROM;
                  dbtype = DB_ROM_MODE1;
                  secsize = DATA_SEC_SIZE;      /* 2048 */
                  dataoff = 16;
            }
            if (ga_ret == NOTAFLAG && (is_auname(cav[0]) || is_wavname(cav[0]))) {
                  /*
                   * We got a track and autodetection decided that it
                   * is an audio track.
                   */
                  autoaudio++;
                  audio++;
            }
            if (data) {
                  /*
                   * 2048 Bytes user data
                   */
                  tracktype = TOC_ROM;
                  sectype = ST_ROM_MODE1 | ST_MODE_1;
                  sectype = SECT_ROM;
                  dbtype = DB_ROM_MODE1;
                  secsize = DATA_SEC_SIZE;      /* 2048 */
                  dataoff = 16;
            }
            if (mode2) {
                  /*
                   * 2336 Bytes user data
                   */
                  tracktype = TOC_ROM;
                  sectype = ST_ROM_MODE2 | ST_MODE_2;
                  sectype = SECT_MODE_2;
                  dbtype = DB_ROM_MODE2;
                  secsize = MODE2_SEC_SIZE;     /* 2336 */
                  dataoff = 16;
            }
            if (audio) {
                  /*
                   * 2352 Bytes user data
                   */
                  tracktype = TOC_DA;
                  sectype = preemp ? ST_AUDIO_PRE : ST_AUDIO_NOPRE;
                  sectype |= ST_MODE_AUDIO;
                  sectype = SECT_AUDIO;
                  if (preemp)
                        sectype |= ST_PREEMPMASK;
                  dbtype = DB_RAW;
                  secsize = AUDIO_SEC_SIZE;     /* 2352 */
                  dataoff = 0;
            }
            if (xa) {
                  /*
                   * 2048 Bytes user data
                   */
                  if (tracktype != TOC_CDI)
                        tracktype = TOC_XA2;
                  sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1;
                  sectype = SECT_MODE_2_F1;
                  dbtype = DB_XA_MODE2;
                  secsize = DATA_SEC_SIZE;      /* 2048 */
                  dataoff = 24;
            }
            if (xa1) {
                  /*
                   * 8 Bytes subheader + 2048 Bytes user data
                   */
                  if (tracktype != TOC_CDI)
                        tracktype = TOC_XA2;
                  sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1;
                  sectype = SECT_MODE_2_F1;
                  dbtype = DB_XA_MODE2_F1;
                  secsize = 2056;
                  dataoff = 16;
            }
            if (xa2) {
                  /*
                   * 2324 Bytes user data
                   */
                  if (tracktype != TOC_CDI)
                        tracktype = TOC_XA2;
                  sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_2;
                  sectype = SECT_MODE_2_F2;
                  dbtype = DB_XA_MODE2_F2;
                  secsize = 2324;
                  dataoff = 24;
            }
            if (xamix) {
                  /*
                   * 8 Bytes subheader + 2324 Bytes user data
                   */
                  if (tracktype != TOC_CDI)
                        tracktype = TOC_XA2;
                  sectype = ST_ROM_MODE2 | ST_MODE_2_MIXED;
                  sectype = SECT_MODE_2_MIX;
                  dbtype = DB_XA_MODE2_MIX;
                  secsize = 2332;
                  dataoff = 16;
            }
            if (cdi) {
                  tracktype = TOC_CDI;
            }
            if (tracks == 0) {
                  trackp[0].tracktype = tracktype;
                  trackp[0].dbtype = dbtype;
                  trackp[0].isecsize = secsize;
                  trackp[0].secsize = secsize;
                  if ((*flagsp & F_RAW) != 0) {
                        trackp[0].secsize = is_raw16(&trackp[0]) ?
                                    RAW16_SEC_SIZE:RAW96_SEC_SIZE;
                  }
                  if ((*flagsp & F_DUMMY) != 0)
                        trackp[0].tracktype |= TOCF_DUMMY;
                  if ((*flagsp & F_MULTI) != 0)
                        trackp[0].tracktype |= TOCF_MULTI;
            }

            flags = trackp[0].flags;

            if ((sectype & ST_AUDIOMASK) != 0)
                  flags |= TI_AUDIO;
            if (isize) {
                  flags |= TI_ISOSIZE;
                  if ((*flagsp & F_MULTI) != 0)
                        comerrno(EX_BAD, "Cannot get isosize for multi session disks.\n");
                  /*
                   * As we do not get the padding from the ISO-9660
                   * formatting utility, we need to force padding here.
                   */
                  flags |= TI_PAD;
                  if (padsize == (Llong)0L)
                        padsize = (Llong)PAD_SIZE;
            }

            if ((flags & TI_AUDIO) != 0) {
                  if (preemp)
                        flags |= TI_PREEMP;
                  if (copy)
                        flags |= TI_COPY;
                  if (scms)
                        flags |= TI_SCMS;
            }
            if (pad || ((flags & TI_AUDIO) == 0 && padsize > (Llong)0L)) {
                  flags |= TI_PAD;
                  if ((flags & TI_AUDIO) == 0 && padsize == (Llong)0L)
                        padsize = (Llong)PAD_SIZE;
            }
            if (shorttrack && (*flagsp & (F_SAO|F_RAW)) != 0)
                  flags |= TI_SHORT_TRACK;
            if (noshorttrack)
                  flags &= ~TI_SHORT_TRACK;
            if (bswab)
                  flags |= TI_SWAB;
            if (ispacket) {
                  flags |= TI_PACKET;
                  trackp[0].flags &= ~TI_TAO;
            }
            if (noclose)
                  flags |= TI_NOCLOSE;
            if (useinfo)
                  flags |= TI_USEINFO;

            if (ga_ret == NOARGS) {
                  /*
                   * All options have already been parsed and no more
                   * file type arguments are present.
                   */
                  break;
            }
            if (tracks == 0 && (wm == 0)) {
                  errmsgno(EX_BAD, "No write mode specified.\n");
                  errmsgno(EX_BAD, "Assuming -tao mode.\n");
                  errmsgno(EX_BAD, "Future versions of wodim may have different drive dependent defaults.\n");
                  tao = 1;
            }
            tracks++;

            if (tracks > MAX_TRACK)
                  comerrno(EX_BAD, "Track limit (%d) exceeded\n",
                                                MAX_TRACK);
            /*
             * Make 'tracks' immediately usable in track structure.
             */
            {     register int i;
                  for (i = 0; i < MAX_TRACK+2; i++)
                        trackp[i].tracks = tracks;
            }

            if (strcmp("-", cav[0]) == 0)
                  *flagsp |= F_STDIN;

            if (!is_auname(cav[0]) && !is_wavname(cav[0]))
                  flags |= TI_NOAUHDR;

            if ((*flagsp & (F_SAO|F_RAW)) != 0 && (flags & TI_AUDIO) != 0)
                  flags |= TI_PREGAP;     /* Hack for now */
            if (tracks == 1)
                  flags &= ~TI_PREGAP;

            if (tracks == 1 && (pregapsize != -1L && pregapsize != 150))
                  pregapsize = -1L;
            trackp[tracks].filename = cav[0];
            trackp[tracks].trackstart = 0L;
            trackp[tracks].itracksize = tracksize;
            trackp[tracks].tracksize = tracksize;
            trackp[tracks].tracksecs = -1L;
            if (tracksize >= 0) {
                  trackp[tracks].tracksecs = (tracksize+secsize-1)/secsize;
                  if(tracksize<616448)
                        warn_minisize=tracksize;
            }
            if (trackp[tracks].pregapsize < 0)
                  trackp[tracks].pregapsize = pregapsize;
            trackp[tracks+1].pregapsize = -1;
            trackp[tracks].padsecs = (padsize+2047)/2048;
            trackp[tracks].isecsize = secsize;
            trackp[tracks].secsize = secsize;
            trackp[tracks].flags = flags;
            /*
             * XXX Dies ist falsch: auch bei SAO/RAW kann
             * XXX secsize != isecsize sein.
             */
            if ((*flagsp & F_RAW) != 0) {
                  if (is_raw16(&trackp[tracks]))
                        trackp[tracks].secsize = RAW16_SEC_SIZE;
                  else
                        trackp[tracks].secsize = RAW96_SEC_SIZE;
#ifndef     HAVE_LIB_EDC_ECC
                  if ((sectype & ST_MODE_MASK) != ST_MODE_AUDIO) {
                        errmsgno(EX_BAD,
                              "EDC/ECC library not compiled in.\n");
                        comerrno(EX_BAD,
                              "Data sectors are not supported in RAW mode.\n");
                  }
#endif
            }
            trackp[tracks].secspt = 0;    /* transfer size is set up in set_trsizes() */
            trackp[tracks].pktsize = pktsize;
            trackp[tracks].trackno = tracks;
            trackp[tracks].sectype = sectype;
#ifdef      CLONE_WRITE
            if ((*flagsp & F_CLONE) != 0) {
                  trackp[tracks].isecsize = 2448;
                  trackp[tracks].sectype |= ST_MODE_RAW;
                  dataoff = 0;
            }
#endif
            trackp[tracks].dataoff = dataoff;
            trackp[tracks].tracktype = tracktype;
            trackp[tracks].dbtype = dbtype;
            trackp[tracks].flags = flags;
            trackp[tracks].nindex = 1;
            trackp[tracks].tindex = 0;
            if (isrc) {
#ifdef      AUINFO
                  setisrc(isrc, &trackp[tracks]);
#else
                  trackp[tracks].isrc = malloc(16);
                  fillbytes(trackp[tracks].isrc, 16, '\0');
                  strncpy(trackp[tracks].isrc, isrc, 12);
#endif
            }
            if (tindex) {
#ifdef      AUINFO
                  setindex(tindex, &trackp[tracks]);
#endif
            }
      }

      if (dminbuf >= 0) {
            if (dminbuf < 25 || dminbuf > 95)
                  comerrno(EX_BAD,
                  "Bad minbuf=%d option (must be between 25 and 95)\n",
                        dminbuf);
      }

      if (speed < 0 && speed != -1)
            comerrno(EX_BAD, "Bad speed option.\n");

      if (fs < 0L && fs != -1L)
            comerrno(EX_BAD, "Bad fifo size option.\n");

      if (bufsize < 0L && bufsize != -1L)
            comerrno(EX_BAD, "Bad transfer size option.\n");
      if (bufsize < 0L)
            bufsize = CDR_BUF_SIZE;
      if (bufsize > CDR_MAX_BUF_SIZE)
            bufsize = CDR_MAX_BUF_SIZE;

      dev = *devp;
      cdr_defaults(&dev, &speed, &fs, &driveropts);
      if (debug) {
            printf("dev: '%s' speed: %d fs: %ld driveropts '%s'\n",
                              dev, speed, fs, driveropts);
      }
      if (speed >= 0)
            *speedp = speed;

      if (fs < 0L)
            fs = DEFAULT_FIFOSIZE;
      if (fs < 2*bufsize) {
            errmsgno(EX_BAD, "Fifo size %ld too small, turning fifo off.\n", fs);
            fs = 0L;
      }

      if (dev != *devp && (*flagsp & F_SCANBUS) == 0)
            *devp = dev;

      if (*devp &&
          ((strncmp(*devp, "HELP", 4) == 0) ||
          (strncmp(*devp, "help", 4) == 0))) {
            *flagsp |= F_CHECKDRIVE; /* Set this for not calling mlockall() */
            return ispacket;
      }
      if (*flagsp & (F_LOAD|F_DLCK|F_SETDROPTS|F_MSINFO|F_TOC|F_PRATIP|F_FIX|F_VERSION|F_CHECKDRIVE|F_PRCAP|F_INQUIRY|F_SCANBUS|F_RESET|F_ABORT)) {
            if (tracks != 0) {
       fprintf(stderr,
             "No tracks allowed with -load, -lock, -setdropts, -msinfo, -toc, -atip, -fix,\n"
             "-version, -checkdrive, -prcap, -inq, -scanbus, --devices, -reset and -abort options.\n" );
       exit(EXIT_FAILURE);
            }
            return ispacket;
      }
      *tracksp = tracks;
      if (*flagsp & F_SAO) {
            /*
             * Make sure that you change WRITER_MAXWAIT & READER_MAXWAIT
             * too if you change this timeout.
             */
            if (*timeoutp < 200)          /* Lead in size is 2:30 */
                  *timeoutp = 200;  /* 200s is 150s *1.33   */
      }
      if (usetext) {
            trackp[MAX_TRACK+1].flags |= TI_TEXT;
      }
      if (cuefile) {
#ifdef      FUTURE
            if ((*flagsp & F_SAO) == 0 &&
                (*flagsp & F_RAW) == 0) {
#else
            if ((*flagsp & F_SAO) == 0) {
#endif
                  errmsgno(EX_BAD, "The cuefile= option only works with -dao.\n");
                  susage(EX_BAD);
            }
            if (tracks > 0) {
                  errmsgno(EX_BAD, "No tracks allowed with the cuefile= option\n");
                  susage(EX_BAD);
            }
            cuefilename = cuefile;
            return ispacket;
      }
      if (tracks == 0 && (*flagsp & (F_LOAD|F_DLCK|F_EJECT|F_BLANK|F_FORMAT)) == 0) {
            errmsgno(EX_BAD, "No tracks specified. Need at least one.\n");
            susage(EX_BAD);
      }
      return ispacket;
}

static void 
set_trsizes(cdr_t *dp, int tracks, track_t *trackp)
{
      int   i;
      int   secsize;
      int   secspt;

      trackp[1].flags         |= TI_FIRST;
      trackp[tracks].flags    |= TI_LAST;

      if (xdebug)
            printf("Set Transfersizes start\n");
      for (i = 0; i <= tracks+1; i++) {
            if ((dp->cdr_flags & CDR_SWABAUDIO) != 0 &&
                              is_audio(&trackp[i])) {
                  trackp[i].flags ^= TI_SWAB;
            }
            if (!is_audio(&trackp[i]))
                  trackp[i].flags &= ~TI_SWAB;  /* Only swab audio  */

            /*
             * Use the biggest sector size to compute how many
             * sectors may fit into one single DMA buffer.
             */
            secsize = trackp[i].secsize;
            if (trackp[i].isecsize > secsize)
                  secsize = trackp[i].isecsize;

            /*
             * We are using SCSI Group 0 write
             * and cannot write more than 255 secs at once.
             */
            secspt = bufsize/secsize;
            secspt = min(255, secspt);
            trackp[i].secspt = secspt;

            if (is_packet(&trackp[i]) && trackp[i].pktsize > 0) {
                  if (trackp[i].secspt >= trackp[i].pktsize) {
                        trackp[i].secspt = trackp[i].pktsize;
                  } else {
                        comerrno(EX_BAD,
                              "Track %d packet size %d exceeds buffer limit of %d sectors",
                              i, trackp[i].pktsize, trackp[i].secspt);
                  }
            }
            if (xdebug) {
                  printf("Track %d flags %X secspt %d secsize: %d isecsize: %d\n",
                        i, trackp[i].flags, trackp[i].secspt,
                        trackp[i].secsize, trackp[i].isecsize);
            }
      }
      if (xdebug)
            printf("Set Transfersizes end\n");
}

void 
load_media(SCSI *usalp, cdr_t *dp, BOOL doexit)
{
      int   code;
      int   key;
      BOOL  immed = (dp->cdr_cmdflags&F_IMMED) != 0;

      /*
       * Do some preparation before...
       */
      usalp->silent++;              /* Be quiet if this fails           */
      test_unit_ready(usalp);       /* First eat up unit attention            */
      if ((*dp->cdr_load)(usalp, dp) < 0) {     /* now try to load media and  */
            if (!doexit)
                  return;
            comerrno(EX_BAD, "Cannot load media.\n");
      }
      scsi_start_stop_unit(usalp, 1, 0, immed); /* start unit in silent mode  */
      usalp->silent--;

      if (!wait_unit_ready(usalp, 60)) {
            code = usal_sense_code(usalp);
            key = usal_sense_key(usalp);
            usalp->silent++;
            scsi_prevent_removal(usalp, 0); /* In case someone locked it */
            usalp->silent--;

            if (!doexit)
                  return;
            if (key == SC_NOT_READY && (code == 0x3A || code == 0x30))
                  comerrno(EX_BAD, "No disk / Wrong disk!\n");
            comerrno(EX_BAD, "CD/DVD-Recorder not ready.\n");
      }

      scsi_prevent_removal(usalp, 1);
      scsi_start_stop_unit(usalp, 1, 0, immed);
      wait_unit_ready(usalp, 120);
      usalp->silent++;
      if(geteuid() == 0) /* EB: needed? Not allowed for non-root, that is sure. */
      rezero_unit(usalp);     /* Is this needed? Not supported by some drvives */
      usalp->silent--;
      test_unit_ready(usalp);
      scsi_start_stop_unit(usalp, 1, 0, immed);
      wait_unit_ready(usalp, 120);
}

void 
unload_media(SCSI *usalp, cdr_t *dp, int flags)
{
      scsi_prevent_removal(usalp, 0);
      if ((flags & F_EJECT) != 0) {
            if ((*dp->cdr_unload)(usalp, dp) < 0)
                  errmsgno(EX_BAD, "Cannot eject media.\n");
      }
}

void 
reload_media(SCSI *usalp, cdr_t *dp)
{
      char  ans[2];
#ifdef      F_GETFL
      int   f = -1;
#endif

      errmsgno(EX_BAD, "Drive needs to reload the media to return to proper status.\n");
      unload_media(usalp, dp, F_EJECT);

      /*
       * Note that even Notebook drives identify as CDR_TRAYLOAD
       */
      if ((dp->cdr_flags & CDR_TRAYLOAD) != 0) {
            usalp->silent++;
            load_media(usalp, dp, FALSE);
            usalp->silent--;
      }

      usalp->silent++;
      if (((dp->cdr_flags & CDR_TRAYLOAD) == 0) ||
                        !wait_unit_ready(usalp, 5)) {
            static FILE *tty = NULL;

            printf("Re-load disk and hit <CR>");
            if (isgui)
                  printf("\n");
            flush();

            if (tty == NULL) {
                  tty = stdin;
                  if ((dp->cdr_cmdflags & F_STDIN) != 0)
                        tty = fileluopen(STDERR_FILENO, "rw");
            }
#ifdef      F_GETFL
            if (tty != NULL)
                  f = fcntl(fileno(tty), F_GETFL, 0);
            if (f < 0 || (f & O_ACCMODE) == O_WRONLY) {
#ifdef      SIGUSR1
                  signal(SIGUSR1, catchsig);
                  printf("Controlling file not open for reading, send SIGUSR1 to continue.\n");
                  flush();
                  pause();
#endif
            } else
#endif
            if (rols_fgetline(tty, ans, 1) < 0)
                  comerrno(EX_BAD, "Aborted by EOF on input.\n");
      }
      usalp->silent--;

      load_media(usalp, dp, TRUE);
}

void 
set_secsize(SCSI *usalp, int secsize)
{
      if (secsize > 0) {
            /*
             * Try to restore the old sector size.
             */
            usalp->silent++;
            select_secsize(usalp, secsize);
            usalp->silent--;
      }
}

static int 
get_dmaspeed(SCSI *usalp, cdr_t *dp)
{
      int   i;
      long  t;
      int   bs;
      int   tsize;

  if(getenv("CDR_NODMATEST"))
     return -1;

  if (debug || lverbose)
     fprintf( stderr, 
           "Beginning DMA speed test. Set CDR_NODMATEST environment variable if device\n"
           "communication breaks or freezes immediately after that.\n" );

      fillbytes((caddr_t)buf, 4, '\0');
      tsize = 0;
      usalp->silent++;
      i = read_buffer(usalp, buf, 4, 0);
      usalp->silent--;
      if (i < 0)
            return (-1);
      tsize = a_to_u_4_byte(buf);
      if (tsize <= 0)
            return (-1);

      if (gettimeofday(&starttime, (struct timezone *)0) < 0)
            return (-1);

      bs = bufsize;
      if (tsize < bs)
            bs = tsize;
      for (i = 0; i < 100; i++) {
            if (read_buffer(usalp, buf, bs, 0) < 0)
                  return (-1);
      }
      if (gettimeofday(&fixtime, (struct timezone *)0) < 0) {
            errmsg("Cannot get DMA stop time\n");
            return (-1);
      }
      timevaldiff(&starttime, &fixtime);
      tsize = bs * 100;
      t = fixtime.tv_sec * 1000 + fixtime.tv_usec / 1000;
      if (t <= 0)
            return (-1);
#ifdef      DEBUG
      fprintf(stderr, "Read Speed: %lu %ld %ld kB/s %ldx CD %ldx DVD\n",
            tsize, t, tsize/t, tsize/t/176, tsize/t/1385);
#endif

      return (tsize/t);
}


static BOOL 
do_opc(SCSI *usalp, cdr_t *dp, int flags)
{
      if ((flags & F_DUMMY) == 0 && dp->cdr_opc) {
            if (debug || lverbose) {
                  printf("Performing OPC...\n");
                  flush();
            }
            if (dp->cdr_opc(usalp, NULL, 0, TRUE) < 0) {
                  errmsgno(EX_BAD, "OPC failed.\n");
                  if ((flags & F_FORCE) == 0)
                        return (FALSE);
            }
      }
      return (TRUE);
}

static void 
check_recovery(SCSI *usalp, cdr_t *dp, int flags)
{
      if ((*dp->cdr_check_recovery)(usalp, dp)) {
            errmsgno(EX_BAD, "Recovery needed.\n");
            unload_media(usalp, dp, flags);
            comexit(EX_BAD);
      }
}

#ifndef     DEBUG
#define     DEBUG
#endif
void 
audioread(SCSI *usalp, cdr_t *dp, int flags)
{
#ifdef      DEBUG
      int speed = 1;
      int   oflags = dp->cdr_cmdflags;

      dp->cdr_cmdflags &= ~F_DUMMY;
      if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0)
            comexit(-1);
      dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */
      dp->cdr_cmdflags = oflags;

      if ((*dp->cdr_set_secsize)(usalp, 2352) < 0)
            comexit(-1);
      usalp->cap->c_bsize = 2352;

      read_scsi(usalp, buf, 1000, 1);
      printf("XXX:\n");
      write(1, buf, 512); /* FIXME: handle return value */
      unload_media(usalp, dp, flags);
      comexit(0);
#endif
}

static void 
print_msinfo(SCSI *usalp, cdr_t *dp)
{
      long  off;
      long  fa;

      if ((*dp->cdr_session_offset)(usalp, &off) < 0) {
            errmsgno(EX_BAD, "Cannot read session offset\n");
            return;
      }
      if (lverbose)
            printf("session offset: %ld\n", off);

      if (dp->cdr_next_wr_address(usalp, (track_t *)0, &fa) < 0) {
            errmsgno(EX_BAD, "Cannot read first writable address\n");
            return;
      }
      printf("%ld,%ld\n", off, fa);
      if(msifile) {
            FILE *f = fopen(msifile, "w");
            if(f) {
                  fprintf(f, "%ld,%ld", off, fa);
                  fclose(f);
            }
            else {
                  perror("Unable to write multi session info file");
                  exit(EXIT_FAILURE);
            }
      }
}

static void 
print_toc(SCSI *usalp, cdr_t *dp)
{
      int   first;
      int   last;
      long  lba;
      long  xlba;
      struct msf msf;
      int   adr;
      int   control;
      int   mode;
      int   i;

      usalp->silent++;
      if (read_capacity(usalp) < 0) {
            usalp->silent--;
            errmsgno(EX_BAD, "Cannot read capacity\n");
            return;
      }
      usalp->silent--;
      if (read_tochdr(usalp, dp, &first, &last) < 0) {
            errmsgno(EX_BAD, "Cannot read TOC/PMA\n");
            return;
      }
      printf("first: %d last %d\n", first, last);
      for (i = first; i <= last; i++) {
            read_trackinfo(usalp, i, &lba, &msf, &adr, &control, &mode);
            xlba = -150 +
                  msf.msf_frame + (75*msf.msf_sec) + (75*60*msf.msf_min);
            if (xlba == lba/4)
                  lba = xlba;
            print_track(i, lba, &msf, adr, control, mode);
      }
      i = 0xAA;
      read_trackinfo(usalp, i, &lba, &msf, &adr, &control, &mode);
      xlba = -150 +
            msf.msf_frame + (75*msf.msf_sec) + (75*60*msf.msf_min);
      if (xlba == lba/4)
            lba = xlba;
      print_track(i, lba, &msf, adr, control, mode);
      if (lverbose > 1) {
            usalp->silent++;
            if (read_cdtext(usalp) < 0)
                  errmsgno(EX_BAD, "No CD-Text or CD-Text unaware drive.\n");
            usalp->silent++;
      }
}

static void 
print_track(int track, long lba, struct msf *msp, int adr, 
                        int control, int mode)
{
      long  lba_512 = lba*4;

      if (track == 0xAA)
            printf("track:lout ");
      else
            printf("track: %3d ", track);

      printf("lba: %9ld (%9ld) %02d:%02d:%02d adr: %X control: %X mode: %d\n",
                  lba, lba_512,
                  msp->msf_min,
                  msp->msf_sec,
                  msp->msf_frame,
                  adr, control, mode);
}

#ifdef      HAVE_SYS_PRIOCNTL_H     /* The preferred SYSvR4 schduler */

#include <sys/procset.h>      /* Needed for SCO Openserver */
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>

void 
raisepri(int pri)
{
      int         pid;
      int         classes;
      int         ret;
      pcinfo_t    info;
      pcparms_t   param;
      rtinfo_t    rtinfo;
      rtparms_t   rtparam;

      pid = getpid();

      /* get info */
      strcpy(info.pc_clname, "RT");
      classes = priocntl(P_PID, pid, PC_GETCID, (void *)&info);
      if (classes == -1)
            comerr("Cannot get priority class id priocntl(PC_GETCID)\n");

      movebytes(info.pc_clinfo, &rtinfo, sizeof (rtinfo_t));

      /* set priority to max */
      rtparam.rt_pri = rtinfo.rt_maxpri - pri;
      rtparam.rt_tqsecs = 0;
      rtparam.rt_tqnsecs = RT_TQDEF;
      param.pc_cid = info.pc_cid;
      movebytes(&rtparam, param.pc_clparms, sizeof (rtparms_t));
      ret = priocntl(P_PID, pid, PC_SETPARMS, (void *)&param);
      if (ret == -1) {
            errmsg("WARNING: Cannot set priority class parameters priocntl(PC_SETPARMS)\n");
            errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n");
      }
}

#else /* HAVE_SYS_PRIOCNTL_H */

#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0
/*
 * The second best choice: POSIX real time scheduling.
 */
/*
 * XXX Ugly but needed because of a typo in /usr/iclude/sched.h on Linux.
 * XXX This should be removed as soon as we are sure that Linux-2.0.29 is gone.
 */
#ifdef      __linux
#define     _P    __P
#endif

#include <sched.h>

#ifdef      __linux
#undef      _P
#endif

static int 
rt_raisepri(int pri)
{
      struct sched_param scp;

      /*
       * Verify that scheduling is available
       */
#ifdef      _SC_PRIORITY_SCHEDULING
      if (sysconf(_SC_PRIORITY_SCHEDULING) == -1) {
            errmsg("WARNING: RR-scheduler not available, disabling.\n");
            return (-1);
      }
#endif
      fillbytes(&scp, sizeof (scp), '\0');
      scp.sched_priority = sched_get_priority_max(SCHED_RR) - pri;
      if (sched_setscheduler(0, SCHED_RR, &scp) < 0) {
            if(lverbose>2)
       errmsg("WARNING: Cannot set RR-scheduler\n");
            return (-1);
      }
      return (0);
}

#else /* _POSIX_PRIORITY_SCHEDULING */

#ifdef      __CYGWIN32__
/*
 * Win32 specific priority settings.
 */
/*
 * NOTE: Base.h from Cygwin-B20 has a second typedef for BOOL.
 *     We define BOOL to make all static code use BOOL
 *     from Windows.h and use the hidden __SBOOL for
 *     our global interfaces.
 *
 * NOTE: windows.h from Cygwin-1.x includes a structure field named sample,
 *     so me may not define our own 'sample' or need to #undef it now.
 *     With a few nasty exceptions, Microsoft assumes that any global
 *     defines or identifiers will begin with an Uppercase letter, so
 *     there may be more of these problems in the future.
 *
 * NOTE: windows.h defines interface as an alias for struct, this
 *     is used by COM/OLE2, I guess it is class on C++
 *     We man need to #undef 'interface'
 */
#define     BOOL  WBOOL       /* This is the Win BOOL       */
#define     format      __format    /* Avoid format parameter hides global ... */
#include <windows.h>
#undef format
#undef interface

static int 
rt_raisepri(int pri)
{
      int prios[] = {THREAD_PRIORITY_TIME_CRITICAL, THREAD_PRIORITY_HIGHEST};

      /* set priority class */
      if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == FALSE) {
            if(debug || lverbose>2)
       errmsgno(EX_BAD, "No realtime priority class possible.\n");
            return (-1);
      }

      /* set thread priority */
      if (pri >= 0 && pri <= 1 && SetThreadPriority(GetCurrentThread(), prios[pri]) == FALSE) {
            errmsgno(EX_BAD, "Could not set realtime priority.\n");
            return (-1);
      }
      return (0);
}

#else
/*
 * This OS does not support real time scheduling.
 */
static int 
rt_raisepri(int pri)
{
      return (-1);
}

#endif      /* __CYGWIN32__ */

#endif      /* _POSIX_PRIORITY_SCHEDULING */

void 
raisepri(int pri)
{
      if (rt_raisepri(pri) >= 0)
            return;
#if   defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)

      if (setpriority(PRIO_PROCESS, getpid(), -20 + pri) < 0) {
            if(debug || lverbose>2)
       fprintf(stderr,
             "WARNING: Cannot set priority using setpriority(),"
             "increased risk for buffer underruns.\n");
      }
#else
#ifdef      HAVE_DOSSETPRIORITY     /* RT priority on OS/2 */
      /*
       * Set priority to timecritical 31 - pri (arg)
       */
      DosSetPriority(0, 3, 31, 0);
      DosSetPriority(0, 3, -pri, 0);
#else
#if   defined(HAVE_NICE) && !defined(__DJGPP__) /* DOS has nice but no multitasking */
      if (nice(-20 + pri) == -1) {
            errmsg("WARNING: Cannot set priority using nice().\n");
            errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n");
      }
#else
      errmsgno(EX_BAD, "WARNING: Cannot set priority on this OS.\n");
      errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n");
#endif
#endif
#endif
}

#endif      /* HAVE_SYS_PRIOCNTL_H */

#ifdef      HAVE_SELECT
/*
 * sys/types.h and sys/time.h are already included.
 */
#else
#     include     <stropts.h>
#     include     <poll.h>

#ifndef     INFTIM
#define     INFTIM      (-1)
#endif
#endif

#if   defined(HAVE_SELECT) && defined(NEED_SYS_SELECT_H)
#include <sys/select.h>
#endif
#if   defined(HAVE_SELECT) && defined(NEED_SYS_SOCKET_H)
#include <sys/socket.h>
#endif

static void 
wait_input()
{
#ifdef      HAVE_SELECT
      fd_set      in;

      FD_ZERO(&in);
      FD_SET(STDIN_FILENO, &in);
      select(1, &in, NULL, NULL, 0);
#else
      struct pollfd pfd;

      pfd.fd = STDIN_FILENO;
      pfd.events = POLLIN;
      pfd.revents = 0;
      poll(&pfd, (unsigned long)1, INFTIM);
#endif
}

static void 
checkgui()
{
      struct stat st;

      if (fstat(STDERR_FILENO, &st) >= 0 && !S_ISCHR(st.st_mode)) {
            isgui = TRUE;
            if (lverbose > 1)
                  printf("Using remote (pipe) mode for interactive i/o.\n");
      }
}

static int 
getbltype(char *optstr, long *typep)
{
      if (streql(optstr, "all")) {
            *typep = BLANK_DISC;
      } else if (streql(optstr, "disc")) {
            *typep = BLANK_DISC;
      } else if (streql(optstr, "disk")) {
            *typep = BLANK_DISC;
      } else if (streql(optstr, "fast")) {
            *typep = BLANK_MINIMAL;
      } else if (streql(optstr, "minimal")) {
            *typep = BLANK_MINIMAL;
      } else if (streql(optstr, "track")) {
            *typep = BLANK_TRACK;
      } else if (streql(optstr, "unreserve")) {
            *typep = BLANK_UNRESERVE;
      } else if (streql(optstr, "trtail")) {
            *typep = BLANK_TAIL;
      } else if (streql(optstr, "unclose")) {
            *typep = BLANK_UNCLOSE;
      } else if (streql(optstr, "session")) {
            *typep = BLANK_SESSION;
      } else if (streql(optstr, "help")) {
            blusage(0);
      } else {
            fprintf(stderr, "Illegal blanking type '%s'.\n", optstr);
            blusage(EX_BAD);
            return (-1);
      }
      return (TRUE);
}

static int 
getformattype(char *optstr, long *typep)
{
      if (streql(optstr, "full")) {
            *typep = FULL_FORMAT;
      } else if (streql(optstr, "background")) {
            *typep = BACKGROUND_FORMAT;
      } else if (streql(optstr, "force")) {
            *typep = FORCE_FORMAT;
      } else if (streql(optstr, "help")) {
            formattypeusage(0);
      } else {
            fprintf(stderr, "Illegal blanking type '%s'.\n", optstr);
            formattypeusage(EX_BAD);
            return (-1);
      }
      return (TRUE);
}
static void 
print_drflags(cdr_t *dp)
{
      printf("Driver flags   : ");

      if ((dp->cdr_flags & CDR_DVD) != 0)
            printf("DVD ");

      if ((dp->cdr_flags & CDR_MMC3) != 0)
            printf("MMC-3 ");
      else if ((dp->cdr_flags & CDR_MMC2) != 0)
            printf("MMC-2 ");
      else if ((dp->cdr_flags & CDR_MMC) != 0)
            printf("MMC ");

      if ((dp->cdr_flags & CDR_SWABAUDIO) != 0)
            printf("SWABAUDIO ");
      if ((dp->cdr_flags & CDR_BURNFREE) != 0)
            printf("BURNFREE ");
      if ((dp->cdr_flags & CDR_VARIREC) != 0)
            printf("VARIREC ");
      if ((dp->cdr_flags & CDR_GIGAREC) != 0)
            printf("GIGAREC ");
      if ((dp->cdr_flags & CDR_AUDIOMASTER) != 0)
            printf("AUDIOMASTER ");
      if ((dp->cdr_flags & CDR_FORCESPEED) != 0)
            printf("FORCESPEED ");
      if ((dp->cdr_flags & CDR_SPEEDREAD) != 0)
            printf("SPEEDREAD ");
      if ((dp->cdr_flags & CDR_DISKTATTOO) != 0)
            printf("DISKTATTOO ");
      if ((dp->cdr_flags & CDR_SINGLESESS) != 0)
            printf("SINGLESESSION ");
      if ((dp->cdr_flags & CDR_HIDE_CDR) != 0)
            printf("HIDECDR ");
      printf("\n");
}

static void 
print_wrmodes(cdr_t *dp)
{
      BOOL  needblank = FALSE;

      printf("Supported modes: ");
      if ((dp->cdr_flags & CDR_TAO) != 0) {
            printf("TAO");
            needblank = TRUE;
      }
      if ((dp->cdr_flags & CDR_PACKET) != 0) {
            printf("%sPACKET", needblank?" ":"");
            needblank = TRUE;
      }
      if ((dp->cdr_flags & CDR_SAO) != 0) {
            printf("%sSAO", needblank?" ":"");
            needblank = TRUE;
      }
#ifdef      __needed__
      if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW16)) == (CDR_SAO|CDR_SRAW16)) {
            printf("%sSAO/R16", needblank?" ":"");
            needblank = TRUE;
      }
#endif
      if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW96P)) == (CDR_SAO|CDR_SRAW96P)) {
            printf("%sSAO/R96P", needblank?" ":"");
            needblank = TRUE;
      }
      if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW96R)) == (CDR_SAO|CDR_SRAW96R)) {
            printf("%sSAO/R96R", needblank?" ":"");
            needblank = TRUE;
      }
      if ((dp->cdr_flags & (CDR_RAW|CDR_RAW16)) == (CDR_RAW|CDR_RAW16)) {
            printf("%sRAW/R16", needblank?" ":"");
            needblank = TRUE;
      }
      if ((dp->cdr_flags & (CDR_RAW|CDR_RAW96P)) == (CDR_RAW|CDR_RAW96P)) {
            printf("%sRAW/R96P", needblank?" ":"");
            needblank = TRUE;
      }
      if ((dp->cdr_flags & (CDR_RAW|CDR_RAW96R)) == (CDR_RAW|CDR_RAW96R)) {
            printf("%sRAW/R96R", needblank?" ":"");
            needblank = TRUE;
      }
      printf("\n");
}

static BOOL 
check_wrmode(cdr_t *dp, int wmode, int tflags)
{
      int   cdflags = dp->cdr_flags;

      if ((tflags & TI_PACKET) != 0 && (cdflags & CDR_PACKET) == 0) {
            errmsgno(EX_BAD, "Drive does not support PACKET recording.\n");
            return (FALSE);
      }
      if ((tflags & TI_TAO) != 0 && (cdflags & CDR_TAO) == 0) {
            errmsgno(EX_BAD, "Drive does not support TAO recording.\n");
            return (FALSE);
      }
      if ((wmode & F_SAO) != 0) {
            if ((cdflags & CDR_SAO) == 0) {
                  errmsgno(EX_BAD, "Drive does not support SAO recording.\n");
                  if ((cdflags & CDR_RAW) != 0)
                        errmsgno(EX_BAD, "Try -raw option.\n");
                  return (FALSE);
            }
#ifdef      __needed__
            if ((tflags & TI_RAW16) != 0 && (cdflags & CDR_SRAW16) == 0) {
                  errmsgno(EX_BAD, "Drive does not support SAO/RAW16.\n");
                  goto badsecs;
            }
#endif
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW && (cdflags & CDR_SRAW96P) == 0) {
                  errmsgno(EX_BAD, "Drive does not support SAO/RAW96P.\n");
                  goto badsecs;
            }
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R) && (cdflags & CDR_SRAW96R) == 0) {
                  errmsgno(EX_BAD, "Drive does not support SAO/RAW96R.\n");
                  goto badsecs;
            }
      }
      if ((wmode & F_RAW) != 0) {
            if ((cdflags & CDR_RAW) == 0) {
                  errmsgno(EX_BAD, "Drive does not support RAW recording.\n");
                  return (FALSE);
            }
            if ((tflags & TI_RAW16) != 0 && (cdflags & CDR_RAW16) == 0) {
                  errmsgno(EX_BAD, "Drive does not support RAW/RAW16.\n");
                  goto badsecs;
            }
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW && (cdflags & CDR_RAW96P) == 0) {
                  errmsgno(EX_BAD, "Drive does not support RAW/RAW96P.\n");
                  goto badsecs;
            }
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R) && (cdflags & CDR_RAW96R) == 0) {
                  errmsgno(EX_BAD, "Drive does not support RAW/RAW96R.\n");
                  goto badsecs;
            }
      }
      return (TRUE);

badsecs:
      if ((wmode & F_SAO) != 0)
            cdflags &= ~(CDR_RAW16|CDR_RAW96P|CDR_RAW96R);
      if ((wmode & F_RAW) != 0)
            cdflags &= ~(CDR_SRAW96P|CDR_SRAW96R);

      if ((cdflags & (CDR_SRAW96R|CDR_RAW96R)) != 0)
            errmsgno(EX_BAD, "Try -raw96r option.\n");
      else if ((cdflags & (CDR_SRAW96P|CDR_RAW96P)) != 0)
            errmsgno(EX_BAD, "Try -raw96p option.\n");
      else if ((cdflags & CDR_RAW16) != 0)
            errmsgno(EX_BAD, "Try -raw16 option.\n");
      return (FALSE);
}

static void 
set_wrmode(cdr_t *dp, int wmode, int tflags)
{
      dstat_t     *dsp = dp->cdr_dstat;

      if ((tflags & TI_PACKET) != 0) {
            dsp->ds_wrmode = WM_PACKET;
            return;
      }
      if ((tflags & TI_TAO) != 0) {
            dsp->ds_wrmode = WM_TAO;
            return;
      }
      if ((wmode & F_SAO) != 0) {
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == 0) {
                  dsp->ds_wrmode = WM_SAO;
                  return;
            }
            if ((tflags & TI_RAW16) != 0) {           /* Is this needed? */
                  dsp->ds_wrmode = WM_SAO_RAW16;
                  return;
            }
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW) {
                  dsp->ds_wrmode = WM_SAO_RAW96P;
                  return;
            }
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R)) {
                  dsp->ds_wrmode = WM_SAO_RAW96R;
                  return;
            }
      }
      if ((wmode & F_RAW) != 0) {
            if ((tflags & TI_RAW16) != 0) {
                  dsp->ds_wrmode = WM_RAW_RAW16;
                  return;
            }
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW) {
                  dsp->ds_wrmode = WM_RAW_RAW96P;
                  return;
            }
            if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R)) {
                  dsp->ds_wrmode = WM_RAW_RAW96R;
                  return;
            }
      }
      dsp->ds_wrmode = WM_NONE;
}

#if   defined(linux) || defined(__linux) || defined(__linux__)
#ifdef      HAVE_UNAME
#include <sys/utsname.h>
#endif
#endif

#ifdef __linux__
static int 
get_cap(cap_value_t cap_array)
{ 
        int ret;
        cap_t capa;
        capa = cap_get_proc();
        cap_set_flag(capa, CAP_EFFECTIVE,  1, &cap_array, CAP_SET);
        ret = cap_set_proc(capa);
        cap_free(capa);
        return ret; 
}
#endif

Generated by  Doxygen 1.6.0   Back to index