/*-
* Copyright (c) 2000,2001 Søren Schmidt
* 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$
*/
#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
void cleanup(int);
void write_file(const char *, int);
void usage(const char *);
static int fd, quiet, saved_block_size;
static struct cdr_track track;
int
main(int argc, char **argv)
{
int ch, arg, addr;
int eject=0, list=0, multi=0, preemp=0, speed=1, test_write=0;
char *devname = "/dev/acd0c", *prog_name;
int block_size = 0;
prog_name = argv[0];
while ((ch = getopt(argc, argv, "ef:lmpqs:t")) != -1) {
switch (ch) {
case 'e':
eject = 1;
break;
case 'f':
devname = optarg;
break;
case 'l':
list = 1;
break;
case 'm':
multi = 1;
break;
case 'p':
preemp = 1;
break;
case 'q':
quiet = 1;
break;
case 's':
speed = atoi(optarg);
if (speed <= 0)
errx(EX_USAGE, "Invalid speed: %s", optarg);
break;
case 't':
test_write = 1;
break;
default:
usage(prog_name);
}
}
argc -= optind;
argv += optind;
if (argc == 0)
usage(prog_name);
if ((fd = open(devname, O_RDWR, 0)) < 0)
err(EX_NOINPUT, "open(%s)", devname);
if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
err_set_exit(cleanup);
for (arg = 0; arg < argc; arg++) {
if (!strcmp(argv[arg], "fixate")) {
if (!quiet)
fprintf(stderr, "fixating CD, please wait..\n");
if (ioctl(fd, CDRIOCCLOSEDISK, &multi) < 0)
err(EX_IOERR, "ioctl(CDRIOCCLOSEDISK)");
break;
}
if (!strcmp(argv[arg], "msinfo")) {
struct ioc_read_toc_single_entry entry;
bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
entry.address_format = CD_LBA_FORMAT;
if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
fprintf(stderr, "%d, %d\n",
ntohl(entry.entry.addr.lba), addr);
break;
}
if (!strcmp(argv[arg], "erase") || !strcmp(argv[arg], "blank")){
int error, blank, percent;
if (!strcmp(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 (!strcmp(argv[arg], "audio") || !strcmp(argv[arg], "raw")) {
track.test_write = test_write;
track.datablock_type = CDR_DB_RAW;
track.preemp = preemp;
block_size = 2352;
continue;
}
if (!strcmp(argv[arg], "data") || !strcmp(argv[arg], "mode1")) {
track.test_write = test_write;
track.datablock_type = CDR_DB_ROM_MODE1;
track.preemp = 0;
block_size = 2048;
continue;
}
if (!strcmp(argv[arg], "mode2")) {
track.test_write = test_write;
track.datablock_type = CDR_DB_ROM_MODE2;
track.preemp = 0;
block_size = 2336;
continue;
}
if (!strcmp(argv[arg], "XAmode1")) {
track.test_write = test_write;
track.datablock_type = CDR_DB_XA_MODE1;
track.preemp = 0;
block_size = 2048;
continue;
}
if (!block_size)
err(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 = NULL;
write_file(file_buf, block_size);
}
if (feof(fp))
fclose(fp);
else
err(EX_IOERR, "fgets(%s)", file_buf);
}
else
write_file(argv[arg], block_size);
}
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
cleanup(int dummy)
{
if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0)
err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
}
void
usage(const char *prog_name)
{
fprintf(stderr, "Usage: %s [-f device] [-s speed] [-e] [-l] [-m] [-p]\n"
"\t[-q] [command] [command filename...]\n", prog_name);
exit(EX_USAGE);
}
void
write_file(const char *name, int block_size)
{
int addr, count, file, filesize, size;
char buf[2352*BLOCKS];
struct stat stat;
static int cdopen, done_stdin, tot_size = 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 (!cdopen) {
if (ioctl(fd, CDRIOCOPENDISK) < 0)
err(EX_IOERR, "ioctl(CDRIOCOPENDISK)");
cdopen = 1;
}
if (ioctl(fd, CDRIOCOPENTRACK, &track) < 0)
err(EX_IOERR, "ioctl(CDRIOCOPENTRACK)");
if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
if (fstat(file, &stat) < 0)
err(EX_IOERR, "fstat(%s)", name);
filesize = stat.st_size / 1024;
if (!quiet) {
fprintf(stderr, "next writeable LBA %d\n", addr);
if (file == STDIN_FILENO)
fprintf(stderr, "writing from stdin\n");
else
fprintf(stderr,
"writing from file %s size %d KB\n",
name, filesize);
}
lseek(fd, addr * block_size, SEEK_SET);
size = 0;
if (filesize == 0)
filesize++; /* cheat, avoid divide by zero */
while ((count = read(file, buf, block_size * BLOCKS)) > 0) {
int res;
if (count % block_size) {
/* pad file to % block_size */
bzero(&buf[count], block_size * BLOCKS - count);
count = ((count / block_size) + 1) * block_size;
}
if ((res = write(fd, buf, count)) != count) {
fprintf(stderr, "\nonly wrote %d of %d bytes\n",
res, count);
break;
}
size += count;
tot_size += count;
if (!quiet) {
int pct;
fprintf(stderr, "written this track %d KB", size/1024);
if (file != STDIN_FILENO) {
pct = (size / 1024) * 100 / filesize;
fprintf(stderr, " (%d%%)", pct);
}
fprintf(stderr, " total %d KB\r", tot_size/1024);
}
}
if (!quiet)
fprintf(stderr, "\n");
close(file);
if (ioctl(fd, CDRIOCCLOSETRACK) < 0)
err(EX_IOERR, "ioctl(CDRIOCCLOSETRACK)");
}