Revert "Remove the broken burncd(8)."
authorSascha Wildner <saw@online.de>
Sat, 8 Jan 2011 12:47:55 +0000 (13:47 +0100)
committerSascha Wildner <saw@online.de>
Sat, 8 Jan 2011 12:47:55 +0000 (13:47 +0100)
This reverts commit fd6f482a63584f75b8a119c8815ad384815c990a.

A number of people disagreed with this commit, so I'm reverting
it.

The reason I removed burncd(8) was that it didn't work here at
all and that others (on IRC) had reported the same. So bring it
back for now so we can re-evaluate.

Makefile_upgrade.inc
share/man/man4/ata.4
share/man/man4/nata.4
usr.sbin/Makefile
usr.sbin/burncd/Makefile [new file with mode: 0644]
usr.sbin/burncd/burncd.8 [new file with mode: 0644]
usr.sbin/burncd/burncd.c [new file with mode: 0644]

index 616fa76..d3de12b 100644 (file)
@@ -1552,9 +1552,6 @@ TO_REMOVE+=/boot/kernel/ahc_eisa.ko
 TO_REMOVE+=/boot/kernel/ahc_isa.ko
 TO_REMOVE+=/usr/include/netproto/ns
 TO_REMOVE+=/usr/include/netns
-TO_REMOVE+=/usr/sbin/burncd
-TO_REMOVE+=/usr/share/man/cat8/burncd.8.gz
-TO_REMOVE+=/usr/share/man/man8/burncd.8.gz
 
 # XXX Remove when pfsync(4) has been fixed
 TO_REMOVE+=/usr/share/man/cat4/pfsync.4.gz
index cff75fa..414f9ce 100644 (file)
@@ -239,9 +239,9 @@ unless they are run at the non-UDMA4 device's lower speed.
 The driver has been designed to handle that kind of setup but lots of
 older devices do not like this.
 .Sh SEE ALSO
-.Xr cdrecord 1 Pq Pa pkgsrc/sysutils/cdrtools ,
 .Xr nata 4 ,
-.Xr atacontrol 8
+.Xr atacontrol 8 ,
+.Xr burncd 8
 .Sh HISTORY
 The
 .Nm
index 1466cc1..9ea605e 100644 (file)
@@ -206,11 +206,11 @@ as the same numbered device, and not depend on attach order.
 .Pp
 Native Command Queuing (NCQ) on SATA drives is not yet supported.
 .Sh SEE ALSO
-.Xr cdrecord 1 Pq Pa pkgsrc/sysutils/cdrtools ,
 .Xr ahci 4 ,
 .Xr ata 4 ,
 .Xr nataraid 4 ,
 .Xr sili 4 ,
+.Xr burncd 8 ,
 .Xr natacontrol 8
 .Sh HISTORY
 The
index 1000e38..0bfa0fa 100644 (file)
@@ -19,6 +19,7 @@ SUBDIR= 802_11 \
        bootparamd \
        btconfig \
        bthcid \
+       burncd \
        cdcontrol \
        chkgrp \
        chown \
diff --git a/usr.sbin/burncd/Makefile b/usr.sbin/burncd/Makefile
new file mode 100644 (file)
index 0000000..1451dfc
--- /dev/null
@@ -0,0 +1,8 @@
+# $FreeBSD: src/usr.sbin/burncd/Makefile,v 1.2.2.1 2001/04/25 12:09:21 ru Exp $
+# $DragonFly: src/usr.sbin/burncd/Makefile,v 1.2 2003/06/17 04:29:52 dillon Exp $
+
+PROG=  burncd
+MAN=   burncd.8
+WARNS?=        2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/burncd/burncd.8 b/usr.sbin/burncd/burncd.8
new file mode 100644 (file)
index 0000000..d9ab503
--- /dev/null
@@ -0,0 +1,187 @@
+.\"
+.\" Copyright (c) 2000,2001,2002 Søren Schmidt <sos@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer,
+.\"    without modification, immediately at the beginning of the file.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/usr.sbin/burncd/burncd.8,v 1.6.2.8 2003/03/12 22:08:13 trhodes Exp $
+.\" $DragonFly: src/usr.sbin/burncd/burncd.8,v 1.8 2008/05/02 02:05:07 swildner Exp $
+.\"
+.Dd December 4, 2001
+.Os
+.Dt BURNCD 8
+.Sh NAME
+.Nm burncd
+.Nd control the ATAPI CD-R/RW driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl delmnpqtv
+.Op Fl f Ar device
+.Op Fl s Ar speed
+.Op Ar command
+.Op Ar command Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to burn CD-R/RW media using the ATAPI cd driver.
+.Pp
+Available options and operands:
+.Bl -tag -width XXXXXXXXXXXX
+.It Fl d
+burn the CD-R/RW in DAO (disk at once) mode.
+.It Fl e
+eject the media when done.
+.It Fl f Ar device
+set the device to use for the burning process.
+.It Fl l
+read a list of image files from filename.
+.It Fl m
+close disk in multisession mode (otherwise disk is closed as singlesession).
+.It Fl n
+do not write gaps between data tracks in DAO mode.
+.It Fl p
+use preemphasis on audio tracks.
+.It Fl q
+quiet, do not print progress messages.
+.It Fl s Ar speed
+set the speed of the burner device.
+Defaults to 1.
+Specify
+.Dq Li max
+to use the drive's fastest speed.
+.It Fl t
+test write, do not actually write on the media.
+.It Fl v
+verbose, print extra progress messages.
+.El
+.Pp
+.Ar command
+may be one of:
+.Bl -tag -width XXXXXXXXXXXX
+.It Cm msinfo
+Show the first LBA of the last track on the media
+and the next writeable address on the media for use with the
+.Xr mkisofs 8 Ns 's
+.Fl C
+switch when adding additional data to ISO file systems with extra sessions.
+.It Cm blank
+Blank a CD-RW medium.
+This uses the fast blanking method, so data are not physically overwritten,
+only those areas that make the media appear blank for further usage are erased.
+.It Cm erase
+Erase a CD-RW medium.
+This erases the entire media.
+Can take up to 1 hour to finish.
+.It Cm fixate
+Fixate the medium so that the TOC is generated and the media can be used
+in an ordinary CD drive.
+The driver defaults to creating singlesession media (see
+.Fl m
+option).
+Should be the last command to
+.Nm
+as the program exits when this has been done.
+Ignored in DAO mode (see
+.Fl d
+option).
+.It Cm raw | audio
+Set the write mode to produce audio (raw mode) tracks for the following
+images on the command line.
+.It Cm data | mode1
+Set the write mode to produce data (mode1) tracks for the following
+image files
+on the command line.
+.It Cm mode2
+Set the write mode to produce data (mode2) tracks for the following
+image files
+on the command line.
+.It Cm XAmode1
+Set the write mode to produce data (XAmode1) tracks for the following image
+files on the command line.
+.It Cm XAmode2
+Set the write mode to produce data (XAmode2) tracks for the following image
+files on the command line.
+.It Cm vcd
+Set the write mode to produce VCD/SVCD tracks for the following image files
+on the command line.
+This automatically sets DAO
+.Pq Fl d
+and
+.Dq "no gaps"
+.Pq Fl n
+modes.
+.It Ar file
+All other arguments are treated as filenames of images to write to the media,
+or in case the
+.Fl l
+option is used as files containing lists of images.
+.El
+.Pp
+Files whose length are not a multiple of the current media blocksize are
+quietly zero padded to fit the blocksize requirement.
+The conventional filename
+.Fl
+refers to stdin, and can only be used once.
+.Sh EXAMPLES
+The typical usage for burning a data CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0c data file1 fixate"
+.Pp
+The typical usage for burning an audio CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0c audio file1 file2 file3 fixate"
+.Pp
+The typical usage for burning an audio CD-R in DAO mode:
+.Pp
+.Dl "burncd -f /dev/acd0c -d audio file1 file2 file3"
+.Pp
+The typical usage for burning a mixed mode CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0c data file1 audio file2 file3 fixate"
+.Pp
+The typical usage for burning from a compressed image file on stdin:
+.Pp
+.Dl "gunzip -c file.iso.gz | burncd -f /dev/acd0c data - fixate"
+.Pp
+In the examples above, the files burned to data CD-Rs are assumed to
+be ISO9660 file systems.
+.Xr mkisofs 8 ,
+available in the
+.Xr pkgsrc 7
+collection, is commonly used to create ISO9660 file system images
+from a given directory tree.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage was contributed by
+.An S\(/oren Schmidt ,
+Denmark
+.Aq sos@FreeBSD.org .
+.Sh BUGS
+Probably, please report when found.
diff --git a/usr.sbin/burncd/burncd.c b/usr.sbin/burncd/burncd.c
new file mode 100644 (file)
index 0000000..d164d05
--- /dev/null
@@ -0,0 +1,571 @@
+/*-
+ * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/usr.sbin/burncd/burncd.c,v 1.10.2.6 2002/11/20 00:26:18 njl Exp $
+ * $DragonFly: src/usr.sbin/burncd/burncd.c,v 1.4 2008/06/05 18:06:33 swildner Exp $
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sysexits.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/cdio.h>
+#include <sys/cdrio.h>
+#include <sys/param.h>
+
+#define BLOCKS 16
+
+struct track_info {
+       int     file;
+       char    *file_name;
+       u_int   file_size;
+       int     block_size;
+       int     block_type;
+       int     pregap;
+       int     addr;
+};
+static struct track_info tracks[100];
+static int fd, quiet, verbose, saved_block_size, notracks;
+
+void add_track(char *, int, int, int);
+void do_DAO(int, int);
+void do_TAO(int, int);
+int write_file(struct track_info *);
+int roundup_blocks(struct track_info *);
+void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
+void cleanup(int);
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+       int ch, arg, addr;
+       int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
+       int nogap = 0, speed = 4 * 177, test_write = 0;
+       int block_size = 0, block_type = 0, cdopen = 0;
+       const char *dev = "/dev/acd0c";
+
+       while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) {
+               switch (ch) {
+               case 'd':
+                       dao = 1;
+                       break;
+
+               case 'e':
+                       eject = 1;
+                       break;
+
+               case 'f':
+                       dev = optarg;
+                       break;
+
+               case 'l':
+                       list = 1;
+                       break;
+
+               case 'm':
+                       multi = 1;
+                       break;
+
+               case 'n':
+                       nogap = 1;
+                       break;
+
+               case 'p':
+                       preemp = 1;
+                       break;
+
+               case 'q':
+                       quiet = 1;
+                       break;
+
+               case 's':
+                       if (strcasecmp("max", optarg) == 0)
+                               speed = CDR_MAX_SPEED;
+                       else
+                               speed = atoi(optarg) * 177;
+                       if (speed <= 0)
+                               errx(EX_USAGE, "Invalid speed: %s", optarg);
+                       break;
+
+               case 't':
+                       test_write = 1;
+                       break;
+
+               case 'v':
+                       verbose = 1;
+                       break;
+
+               default: 
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if (argc == 0)
+               usage();
+
+       if ((fd = open(dev, O_RDWR, 0)) < 0)
+               err(EX_NOINPUT, "open(%s)", dev);
+
+       if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0) 
+                       err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
+
+       if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0) 
+                       err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
+
+       err_set_exit(cleanup);
+
+       for (arg = 0; arg < argc; arg++) {
+               if (!strcasecmp(argv[arg], "fixate")) {
+                       fixate = 1;
+                       break;
+               }
+               if (!strcasecmp(argv[arg], "msinfo")) {
+                       struct ioc_read_toc_single_entry entry;
+                       struct ioc_toc_header header;
+
+                       if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0) 
+                               err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
+                       bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
+                       entry.address_format = CD_LBA_FORMAT;
+                       entry.track = header.ending_track;
+                       if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0) 
+                               err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
+                       if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) 
+                               err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+                       fprintf(stdout, "%d,%d\n", 
+                               ntohl(entry.entry.addr.lba), addr);
+
+                       break;
+               }
+               if (!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")){
+                       int error, blank, percent;
+
+                       if (!strcasecmp(argv[arg], "erase"))
+                               blank = CDR_B_ALL;
+                       else
+                               blank = CDR_B_MIN;
+                       if (!quiet)
+                               fprintf(stderr, "%sing CD, please wait..\r",
+                                       blank == CDR_B_ALL ? "eras" : "blank");
+
+                       if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
+                               err(EX_IOERR, "ioctl(CDRIOCBLANK)");
+                       while (1) {
+                               sleep(1);
+                               error = ioctl(fd, CDRIOCGETPROGRESS, &percent);
+                               if (percent > 0 && !quiet)
+                                       fprintf(stderr, 
+                                               "%sing CD - %d %% done     \r",
+                                               blank == CDR_B_ALL ? 
+                                               "eras" : "blank", percent);
+                               if (error || percent == 100)
+                                       break;
+                       }
+                       if (!quiet)
+                               printf("\n");
+                       continue;
+               }
+               if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
+                       block_type = CDR_DB_RAW;
+                       block_size = 2352;
+                       continue;
+               }
+               if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
+                       block_type = CDR_DB_ROM_MODE1;
+                       block_size = 2048;
+                       continue;
+               }
+               if (!strcasecmp(argv[arg], "mode2")) {
+                       block_type = CDR_DB_ROM_MODE2;
+                       block_size = 2336;
+                       continue;
+               }
+               if (!strcasecmp(argv[arg], "xamode1")) {
+                       block_type = CDR_DB_XA_MODE1;
+                       block_size = 2048;
+                       continue;
+               }
+               if (!strcasecmp(argv[arg], "xamode2")) {
+                       block_type = CDR_DB_XA_MODE2_F2;
+                       block_size = 2324;
+                       continue;
+               }
+               if (!strcasecmp(argv[arg], "vcd")) {
+                       block_type = CDR_DB_XA_MODE2_F2;
+                       block_size = 2352;
+                       dao = 1;
+                       nogap = 1;
+                       continue;
+               }
+               if (!block_size)
+                       errx(EX_NOINPUT, "no data format selected");
+               if (list) {
+                       char file_buf[MAXPATHLEN + 1], *eol;
+                       FILE *fp;
+
+                       if ((fp = fopen(argv[arg], "r")) == NULL)
+                               err(EX_NOINPUT, "fopen(%s)", argv[arg]);
+
+                       while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
+                               if (*file_buf == '#' || *file_buf == '\n')
+                                       continue;
+                               if ((eol = strchr(file_buf, '\n')))
+                                       *eol = 0;
+                               add_track(file_buf, block_size, block_type, nogap);
+                       }
+                       if (feof(fp))
+                               fclose(fp);
+                       else
+                               err(EX_IOERR, "fgets(%s)", file_buf);
+               }
+               else
+                       add_track(argv[arg], block_size, block_type, nogap);
+       }
+       if (notracks) {
+               if (ioctl(fd, CDIOCSTART, 0) < 0)
+                       err(EX_IOERR, "ioctl(CDIOCSTART)");
+               if (!cdopen) {
+                       if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
+                               err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
+                       cdopen = 1;
+               }
+               if (dao) 
+                       do_DAO(test_write, multi);
+               else
+                       do_TAO(test_write, preemp);
+       }
+       if (fixate && !dao) {
+               if (!quiet)
+                       fprintf(stderr, "fixating CD, please wait..\n");
+               if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
+                       err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
+       }
+
+       if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
+               err_set_exit(NULL);
+               err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+       }
+
+       if (eject)
+               if (ioctl(fd, CDIOCEJECT) < 0)
+                       err(EX_IOERR, "ioctl(CDIOCEJECT)");
+       close(fd);
+       exit(EX_OK);
+}
+
+void
+add_track(char *name, int block_size, int block_type, int nogap)
+{
+       struct stat sb;
+       int file;
+       static int done_stdin = 0;
+
+       if (!strcmp(name, "-")) {
+               if (done_stdin) {
+                       warn("skipping multiple usages of stdin");
+                       return;
+               }
+               file = STDIN_FILENO;
+               done_stdin = 1;
+       }
+       else if ((file = open(name, O_RDONLY, 0)) < 0)
+               err(EX_NOINPUT, "open(%s)", name);
+       if (fstat(file, &sb) < 0)
+               err(EX_IOERR, "fstat(%s)", name);
+       tracks[notracks].file = file;
+       tracks[notracks].file_name = name;
+       if (file == STDIN_FILENO)
+               tracks[notracks].file_size = -1;
+       else
+               tracks[notracks].file_size = sb.st_size;
+       tracks[notracks].block_size = block_size;
+       tracks[notracks].block_type = block_type;
+
+       if (nogap && notracks)
+               tracks[notracks].pregap = 0;
+       else {
+               if (tracks[notracks - (notracks > 0)].block_type == block_type)
+                       tracks[notracks].pregap = 150;
+               else
+                       tracks[notracks].pregap = 255;
+       }
+
+       if (verbose) {
+               int pad = 0;
+
+               if (tracks[notracks].file_size / tracks[notracks].block_size !=
+                   roundup_blocks(&tracks[notracks]))
+                       pad = 1;
+               fprintf(stderr, 
+                       "adding type 0x%02x file %s size %d KB %d blocks %s\n",
+                       tracks[notracks].block_type, name, (int)sb.st_size/1024,
+                       roundup_blocks(&tracks[notracks]),
+                       pad ? "(0 padded)" : "");
+       }
+       notracks++;
+}
+
+void
+do_DAO(int test_write, int multi)
+{
+       struct cdr_cuesheet sheet;
+       struct cdr_cue_entry cue[100];
+       int format = CDR_SESS_CDROM;
+       int addr, i, j = 0;
+
+       int bt2ctl[16] = { 0x0,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
+                          0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  -1,  -1 };
+
+       int bt2df[16] = { 0x0,    -1,   -1,   -1,   -1,   -1,   -1,   -1,
+                         0x10, 0x30, 0x20,   -1, 0x21,   -1,   -1,   -1 };
+       
+       if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) 
+               err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+       if (verbose)
+               fprintf(stderr, "next writeable LBA %d\n", addr);
+
+       cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
+               (bt2df[tracks[0].block_type] & 0xf0) | 
+               (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
+
+       for (i = 0; i < notracks; i++) {
+               if (bt2ctl[tracks[i].block_type] < 0 ||
+                   bt2df[tracks[i].block_type] < 0)
+                       err(EX_IOERR, "track type not supported in DAO mode");
+
+               if (tracks[i].block_type >= CDR_DB_XA_MODE1)
+                       format = CDR_SESS_CDROM_XA;
+
+               if (i == 0) {
+                       addr += tracks[i].pregap;
+                       tracks[i].addr = addr;
+
+                       cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 
+                               0x01, i+1, 0x1, bt2df[tracks[i].block_type],
+                               0x00, addr);
+
+               }
+               else {
+                       if (tracks[i].pregap) {
+                               if (tracks[i].block_type > 0x7) {
+                                       cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 
+                                               0x01, i+1, 0x0,
+                                               (bt2df[tracks[i].block_type] & 0xf0) | 
+                                               (tracks[i].block_type < 8 ? 0x01 :0x04),
+                                               0x00, addr);
+                               }
+                               else
+                                       cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 
+                                               0x01, i+1, 0x0,
+                                               bt2df[tracks[i].block_type],
+                                               0x00, addr);
+                       }
+                       tracks[i].addr = tracks[i - 1].addr +
+                               roundup_blocks(&tracks[i - 1]);
+
+                       cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
+                               0x01, i+1, 0x1, bt2df[tracks[i].block_type],
+                               0x00, addr + tracks[i].pregap);
+
+                       if (tracks[i].block_type > 0x7)
+                               addr += tracks[i].pregap;
+               }
+               addr += roundup_blocks(&tracks[i]);
+       }
+
+       cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
+               (bt2df[tracks[i - 1].block_type] & 0xf0) | 
+               (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
+
+       sheet.len = j * 8;
+       sheet.entries = cue;
+       sheet.test_write = test_write;
+       sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
+       sheet.session_format = format;
+       if (verbose) {
+               u_int8_t *ptr = (u_int8_t *)sheet.entries;
+               
+               fprintf(stderr,"CUE sheet:");
+               for (i = 0; i < sheet.len; i++)
+                       if (i % 8)
+                               fprintf(stderr," %02x", ptr[i]);
+                       else
+                               fprintf(stderr,"\n%02x", ptr[i]);
+               fprintf(stderr,"\n");
+       }
+       
+       if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
+               err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
+
+       for (i = 0; i < notracks; i++) {
+               if (write_file(&tracks[i]))
+                       err(EX_IOERR, "write_file");
+       }
+
+       ioctl(fd, CDRIOCFLUSH);
+}
+
+void
+do_TAO(int test_write, int preemp)
+{
+       struct cdr_track track;
+       int i;
+
+       for (i = 0; i < notracks; i++) {
+               track.test_write = test_write;
+               track.datablock_type = tracks[i].block_type;
+               track.preemp = preemp;
+               if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
+                       err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
+
+               if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0)
+                       err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+               if (!quiet)
+                       fprintf(stderr, "next writeable LBA %d\n",
+                               tracks[i].addr);
+               if (write_file(&tracks[i]))
+                       err(EX_IOERR, "write_file");
+               if (ioctl(fd, CDRIOCFLUSH) < 0)
+                       err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
+       }
+}
+
+int
+write_file(struct track_info *track_info)
+{
+       int size, count, filesize;
+       char buf[2352*BLOCKS];
+       static int tot_size = 0;
+
+       filesize = track_info->file_size / 1024;
+
+       if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
+               err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+
+       if (track_info->addr >= 0)
+               lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
+
+       if (verbose)
+               fprintf(stderr, "addr = %d size = %d blocks = %d\n",
+                       track_info->addr, track_info->file_size,
+                       roundup_blocks(track_info));
+
+       if (!quiet) {
+               if (track_info->file == STDIN_FILENO)
+                       fprintf(stderr, "writing from stdin\n");
+               else
+                       fprintf(stderr, 
+                               "writing from file %s size %d KB\n",
+                               track_info->file_name, filesize);
+       }
+       size = 0;
+
+       while ((count = read(track_info->file, buf,
+                            MIN((track_info->file_size - size),
+                                track_info->block_size * BLOCKS))) > 0) {      
+               int res;
+
+               if (count % track_info->block_size) {
+                       /* pad file to % block_size */
+                       bzero(&buf[count],
+                             (track_info->block_size * BLOCKS) - count);
+                       count = ((count / track_info->block_size) + 1) *
+                               track_info->block_size;
+               }
+               if ((res = write(fd, buf, count)) != count) {
+                       fprintf(stderr, "\nonly wrote %d of %d bytes err=%d\n",
+                               res, count, errno);
+                       break;
+               }
+               size += count;
+               tot_size += count;
+               if (!quiet) {
+                       int pct;
+
+                       fprintf(stderr, "written this track %d KB", size/1024);
+                       if (track_info->file != STDIN_FILENO && filesize) {
+                               pct = (size / 1024) * 100 / filesize;
+                               fprintf(stderr, " (%d%%)", pct);
+                       }
+                       fprintf(stderr, " total %d KB\r", tot_size/1024);
+               }
+               if (size >= track_info->file_size)
+                       break;
+       }
+
+       if (!quiet)
+               fprintf(stderr, "\n");
+       close(track_info->file);
+       return 0;
+}
+
+int
+roundup_blocks(struct track_info *track)
+{
+       return ((track->file_size + track->block_size - 1) / track->block_size);
+}
+
+void
+cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
+       int dataform, int scms, int lba)
+{
+       cue->adr = adr;
+       cue->ctl = ctl;
+       cue->track = track;
+       cue->index = idx;
+       cue->dataform = dataform;
+       cue->scms = scms;
+       lba += 150;
+       cue->min = lba / (60*75);
+       cue->sec = (lba % (60*75)) / 75;
+       cue->frame = (lba % (60*75)) % 75;
+}
+
+void
+cleanup(int dummy __unused)
+{
+       if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) 
+               err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+}
+
+void
+usage(void)
+{
+       fprintf(stderr,
+           "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
+           " [command file ...]\n", getprogname());
+       exit(EX_USAGE);
+}