added -y/--side-by-side option
[dfdiff.git] / usr.sbin / burncd / burncd.c
blob87b256765fdb9a3e77b115aede0542c8a431c097
1 /*-
2 * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@freebsd.org>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * $FreeBSD: src/usr.sbin/burncd/burncd.c,v 1.10.2.6 2002/11/20 00:26:18 njl Exp $
29 * $DragonFly: src/usr.sbin/burncd/burncd.c,v 1.3 2006/02/02 17:00:29 eirikn Exp $
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <err.h>
37 #include <sysexits.h>
38 #include <fcntl.h>
39 #include <sys/errno.h>
40 #include <sys/ioctl.h>
41 #include <sys/stat.h>
42 #include <sys/cdio.h>
43 #include <sys/cdrio.h>
44 #include <sys/param.h>
46 #define BLOCKS 16
48 struct track_info {
49 int file;
50 char *file_name;
51 u_int file_size;
52 int block_size;
53 int block_type;
54 int pregap;
55 int addr;
57 static struct track_info tracks[100];
58 static int fd, quiet, verbose, saved_block_size, notracks;
60 void add_track(char *, int, int, int);
61 void do_DAO(int, int);
62 void do_TAO(int, int);
63 int write_file(struct track_info *);
64 int roundup_blocks(struct track_info *);
65 void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
66 void cleanup(int);
67 void usage(void);
69 int
70 main(int argc, char **argv)
72 int ch, arg, addr;
73 int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
74 int nogap = 0, speed = 4 * 177, test_write = 0;
75 int block_size = 0, block_type = 0, cdopen = 0;
76 const char *dev = "/dev/acd0c";
78 while ((ch = getopt(argc, argv, "def:lmnpqs:tv")) != -1) {
79 switch (ch) {
80 case 'd':
81 dao = 1;
82 break;
84 case 'e':
85 eject = 1;
86 break;
88 case 'f':
89 dev = optarg;
90 break;
92 case 'l':
93 list = 1;
94 break;
96 case 'm':
97 multi = 1;
98 break;
100 case 'n':
101 nogap = 1;
102 break;
104 case 'p':
105 preemp = 1;
106 break;
108 case 'q':
109 quiet = 1;
110 break;
112 case 's':
113 if (strcasecmp("max", optarg) == 0)
114 speed = CDR_MAX_SPEED;
115 else
116 speed = atoi(optarg) * 177;
117 if (speed <= 0)
118 errx(EX_USAGE, "Invalid speed: %s", optarg);
119 break;
121 case 't':
122 test_write = 1;
123 break;
125 case 'v':
126 verbose = 1;
127 break;
129 default:
130 usage();
133 argc -= optind;
134 argv += optind;
136 if (argc == 0)
137 usage();
139 if ((fd = open(dev, O_RDWR, 0)) < 0)
140 err(EX_NOINPUT, "open(%s)", dev);
142 if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
143 err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
145 if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
146 err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
148 err_set_exit(cleanup);
150 for (arg = 0; arg < argc; arg++) {
151 if (!strcasecmp(argv[arg], "fixate")) {
152 fixate = 1;
153 break;
155 if (!strcasecmp(argv[arg], "msinfo")) {
156 struct ioc_read_toc_single_entry entry;
157 struct ioc_toc_header header;
159 if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0)
160 err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
161 bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
162 entry.address_format = CD_LBA_FORMAT;
163 entry.track = header.ending_track;
164 if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
165 err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
166 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
167 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
168 fprintf(stdout, "%d,%d\n",
169 ntohl(entry.entry.addr.lba), addr);
171 break;
173 if (!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")){
174 int error, blank, percent;
176 if (!strcasecmp(argv[arg], "erase"))
177 blank = CDR_B_ALL;
178 else
179 blank = CDR_B_MIN;
180 if (!quiet)
181 fprintf(stderr, "%sing CD, please wait..\r",
182 blank == CDR_B_ALL ? "eras" : "blank");
184 if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
185 err(EX_IOERR, "ioctl(CDRIOCBLANK)");
186 while (1) {
187 sleep(1);
188 error = ioctl(fd, CDRIOCGETPROGRESS, &percent);
189 if (percent > 0 && !quiet)
190 fprintf(stderr,
191 "%sing CD - %d %% done \r",
192 blank == CDR_B_ALL ?
193 "eras" : "blank", percent);
194 if (error || percent == 100)
195 break;
197 if (!quiet)
198 printf("\n");
199 continue;
201 if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
202 block_type = CDR_DB_RAW;
203 block_size = 2352;
204 continue;
206 if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
207 block_type = CDR_DB_ROM_MODE1;
208 block_size = 2048;
209 continue;
211 if (!strcasecmp(argv[arg], "mode2")) {
212 block_type = CDR_DB_ROM_MODE2;
213 block_size = 2336;
214 continue;
216 if (!strcasecmp(argv[arg], "xamode1")) {
217 block_type = CDR_DB_XA_MODE1;
218 block_size = 2048;
219 continue;
221 if (!strcasecmp(argv[arg], "xamode2")) {
222 block_type = CDR_DB_XA_MODE2_F2;
223 block_size = 2324;
224 continue;
226 if (!strcasecmp(argv[arg], "vcd")) {
227 block_type = CDR_DB_XA_MODE2_F2;
228 block_size = 2352;
229 dao = 1;
230 nogap = 1;
231 continue;
233 if (!block_size)
234 errx(EX_NOINPUT, "no data format selected");
235 if (list) {
236 char file_buf[MAXPATHLEN + 1], *eol;
237 FILE *fp;
239 if ((fp = fopen(argv[arg], "r")) == NULL)
240 err(EX_NOINPUT, "fopen(%s)", argv[arg]);
242 while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
243 if (*file_buf == '#' || *file_buf == '\n')
244 continue;
245 if ((eol = strchr(file_buf, '\n')))
246 *eol = NULL;
247 add_track(file_buf, block_size, block_type, nogap);
249 if (feof(fp))
250 fclose(fp);
251 else
252 err(EX_IOERR, "fgets(%s)", file_buf);
254 else
255 add_track(argv[arg], block_size, block_type, nogap);
257 if (notracks) {
258 if (ioctl(fd, CDIOCSTART, 0) < 0)
259 err(EX_IOERR, "ioctl(CDIOCSTART)");
260 if (!cdopen) {
261 if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
262 err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
263 cdopen = 1;
265 if (dao)
266 do_DAO(test_write, multi);
267 else
268 do_TAO(test_write, preemp);
270 if (fixate && !dao) {
271 if (!quiet)
272 fprintf(stderr, "fixating CD, please wait..\n");
273 if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
274 err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
277 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
278 err_set_exit(NULL);
279 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
282 if (eject)
283 if (ioctl(fd, CDIOCEJECT) < 0)
284 err(EX_IOERR, "ioctl(CDIOCEJECT)");
285 close(fd);
286 exit(EX_OK);
289 void
290 add_track(char *name, int block_size, int block_type, int nogap)
292 struct stat sb;
293 int file;
294 static int done_stdin = 0;
296 if (!strcmp(name, "-")) {
297 if (done_stdin) {
298 warn("skipping multiple usages of stdin");
299 return;
301 file = STDIN_FILENO;
302 done_stdin = 1;
304 else if ((file = open(name, O_RDONLY, 0)) < 0)
305 err(EX_NOINPUT, "open(%s)", name);
306 if (fstat(file, &sb) < 0)
307 err(EX_IOERR, "fstat(%s)", name);
308 tracks[notracks].file = file;
309 tracks[notracks].file_name = name;
310 if (file == STDIN_FILENO)
311 tracks[notracks].file_size = -1;
312 else
313 tracks[notracks].file_size = sb.st_size;
314 tracks[notracks].block_size = block_size;
315 tracks[notracks].block_type = block_type;
317 if (nogap && notracks)
318 tracks[notracks].pregap = 0;
319 else {
320 if (tracks[notracks - (notracks > 0)].block_type == block_type)
321 tracks[notracks].pregap = 150;
322 else
323 tracks[notracks].pregap = 255;
326 if (verbose) {
327 int pad = 0;
329 if (tracks[notracks].file_size / tracks[notracks].block_size !=
330 roundup_blocks(&tracks[notracks]))
331 pad = 1;
332 fprintf(stderr,
333 "adding type 0x%02x file %s size %d KB %d blocks %s\n",
334 tracks[notracks].block_type, name, (int)sb.st_size/1024,
335 roundup_blocks(&tracks[notracks]),
336 pad ? "(0 padded)" : "");
338 notracks++;
341 void
342 do_DAO(int test_write, int multi)
344 struct cdr_cuesheet sheet;
345 struct cdr_cue_entry cue[100];
346 int format = CDR_SESS_CDROM;
347 int addr, i, j = 0;
349 int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
350 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
352 int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
353 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
355 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
356 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
357 if (verbose)
358 fprintf(stderr, "next writeable LBA %d\n", addr);
360 cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
361 (bt2df[tracks[0].block_type] & 0xf0) |
362 (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
364 for (i = 0; i < notracks; i++) {
365 if (bt2ctl[tracks[i].block_type] < 0 ||
366 bt2df[tracks[i].block_type] < 0)
367 err(EX_IOERR, "track type not supported in DAO mode");
369 if (tracks[i].block_type >= CDR_DB_XA_MODE1)
370 format = CDR_SESS_CDROM_XA;
372 if (i == 0) {
373 addr += tracks[i].pregap;
374 tracks[i].addr = addr;
376 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
377 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
378 0x00, addr);
381 else {
382 if (tracks[i].pregap) {
383 if (tracks[i].block_type > 0x7) {
384 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
385 0x01, i+1, 0x0,
386 (bt2df[tracks[i].block_type] & 0xf0) |
387 (tracks[i].block_type < 8 ? 0x01 :0x04),
388 0x00, addr);
390 else
391 cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
392 0x01, i+1, 0x0,
393 bt2df[tracks[i].block_type],
394 0x00, addr);
396 tracks[i].addr = tracks[i - 1].addr +
397 roundup_blocks(&tracks[i - 1]);
399 cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
400 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
401 0x00, addr + tracks[i].pregap);
403 if (tracks[i].block_type > 0x7)
404 addr += tracks[i].pregap;
406 addr += roundup_blocks(&tracks[i]);
409 cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
410 (bt2df[tracks[i - 1].block_type] & 0xf0) |
411 (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
413 sheet.len = j * 8;
414 sheet.entries = cue;
415 sheet.test_write = test_write;
416 sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
417 sheet.session_format = format;
418 if (verbose) {
419 u_int8_t *ptr = (u_int8_t *)sheet.entries;
421 fprintf(stderr,"CUE sheet:");
422 for (i = 0; i < sheet.len; i++)
423 if (i % 8)
424 fprintf(stderr," %02x", ptr[i]);
425 else
426 fprintf(stderr,"\n%02x", ptr[i]);
427 fprintf(stderr,"\n");
430 if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
431 err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
433 for (i = 0; i < notracks; i++) {
434 if (write_file(&tracks[i]))
435 err(EX_IOERR, "write_file");
438 ioctl(fd, CDRIOCFLUSH);
441 void
442 do_TAO(int test_write, int preemp)
444 struct cdr_track track;
445 int i;
447 for (i = 0; i < notracks; i++) {
448 track.test_write = test_write;
449 track.datablock_type = tracks[i].block_type;
450 track.preemp = preemp;
451 if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
452 err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
454 if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0)
455 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
456 if (!quiet)
457 fprintf(stderr, "next writeable LBA %d\n",
458 tracks[i].addr);
459 if (write_file(&tracks[i]))
460 err(EX_IOERR, "write_file");
461 if (ioctl(fd, CDRIOCFLUSH) < 0)
462 err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
467 write_file(struct track_info *track_info)
469 int size, count, filesize;
470 char buf[2352*BLOCKS];
471 static int tot_size = 0;
473 filesize = track_info->file_size / 1024;
475 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
476 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
478 if (track_info->addr >= 0)
479 lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
481 if (verbose)
482 fprintf(stderr, "addr = %d size = %d blocks = %d\n",
483 track_info->addr, track_info->file_size,
484 roundup_blocks(track_info));
486 if (!quiet) {
487 if (track_info->file == STDIN_FILENO)
488 fprintf(stderr, "writing from stdin\n");
489 else
490 fprintf(stderr,
491 "writing from file %s size %d KB\n",
492 track_info->file_name, filesize);
494 size = 0;
496 while ((count = read(track_info->file, buf,
497 MIN((track_info->file_size - size),
498 track_info->block_size * BLOCKS))) > 0) {
499 int res;
501 if (count % track_info->block_size) {
502 /* pad file to % block_size */
503 bzero(&buf[count],
504 (track_info->block_size * BLOCKS) - count);
505 count = ((count / track_info->block_size) + 1) *
506 track_info->block_size;
508 if ((res = write(fd, buf, count)) != count) {
509 fprintf(stderr, "\nonly wrote %d of %d bytes err=%d\n",
510 res, count, errno);
511 break;
513 size += count;
514 tot_size += count;
515 if (!quiet) {
516 int pct;
518 fprintf(stderr, "written this track %d KB", size/1024);
519 if (track_info->file != STDIN_FILENO && filesize) {
520 pct = (size / 1024) * 100 / filesize;
521 fprintf(stderr, " (%d%%)", pct);
523 fprintf(stderr, " total %d KB\r", tot_size/1024);
525 if (size >= track_info->file_size)
526 break;
529 if (!quiet)
530 fprintf(stderr, "\n");
531 close(track_info->file);
532 return 0;
536 roundup_blocks(struct track_info *track)
538 return ((track->file_size + track->block_size - 1) / track->block_size);
541 void
542 cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
543 int dataform, int scms, int lba)
545 cue->adr = adr;
546 cue->ctl = ctl;
547 cue->track = track;
548 cue->index = idx;
549 cue->dataform = dataform;
550 cue->scms = scms;
551 lba += 150;
552 cue->min = lba / (60*75);
553 cue->sec = (lba % (60*75)) / 75;
554 cue->frame = (lba % (60*75)) % 75;
557 void
558 cleanup(int dummy __unused)
560 if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0)
561 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
564 void
565 usage(void)
567 fprintf(stderr,
568 "Usage: %s [-delmnpqtv] [-f device] [-s speed] [command]"
569 " [command file ...]\n", getprogname());
570 exit(EX_USAGE);