]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/burncd/burncd.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / burncd / burncd.c
1 /*-
2  * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@freebsd.org>
3  * All rights reserved.
4  *
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.
16  *
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.
27  *
28  * $FreeBSD$
29  */
30
31 #include <unistd.h>
32 #include <signal.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <err.h>
38 #include <sysexits.h>
39 #include <fcntl.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/stat.h>
43 #include <sys/cdio.h>
44 #include <sys/cdrio.h>
45 #include <sys/dvdio.h>
46 #include <sys/param.h>
47 #include <arpa/inet.h>
48
49 #define BLOCKS  16
50
51 struct track_info {
52         int     file;
53         char    file_name[MAXPATHLEN + 1];
54         off_t   file_size;
55         int     block_size;
56         int     block_type;
57         int     pregap;
58         int     addr;
59 };
60 static struct track_info tracks[100];
61 static int quiet, verbose, saved_block_size, notracks;
62 static volatile sig_atomic_t global_fd_for_cleanup;
63
64 void add_track(char *, int, int, int);
65 void do_DAO(int fd, int, int);
66 void do_TAO(int fd, int, int, int);
67 void do_format(int, int, char *);
68 int write_file(int fd, struct track_info *);
69 int roundup_blocks(struct track_info *);
70 void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
71 void cleanup(int);
72 void cleanup_flush(void);
73 void cleanup_signal(int);
74 void usage(void);
75
76 int
77 main(int argc, char **argv)
78 {
79         int arg, addr, ch, fd;
80         int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
81         int nogap = 0, speed = 4 * 177, test_write = 0, force = 0;
82         int block_size = 0, block_type = 0, cdopen = 0, dvdrw = 0;
83         const char *dev, *env_speed;
84
85         if ((dev = getenv("CDROM")) == NULL)
86                 dev = "/dev/acd0";
87
88         env_speed = getenv("BURNCD_SPEED");
89
90         while ((ch = getopt(argc, argv, "def:Flmnpqs:tv")) != -1) {
91                 switch (ch) {
92                 case 'd':
93                         dao = 1;
94                         break;
95
96                 case 'e':
97                         eject = 1;
98                         break;
99
100                 case 'f':
101                         dev = optarg;
102                         break;
103
104                 case 'F':
105                         force = 1;
106                         break;
107
108                 case 'l':
109                         list = 1;
110                         break;
111
112                 case 'm':
113                         multi = 1;
114                         break;
115
116                 case 'n':
117                         nogap = 1;
118                         break;
119
120                 case 'p':
121                         preemp = 1;
122                         break;
123
124                 case 'q':
125                         quiet = 1;
126                         break;
127
128                 case 's':
129                         env_speed = optarg;
130                         break;
131
132                 case 't':
133                         test_write = 1;
134                         break;
135
136                 case 'v':
137                         verbose = 1;
138                         break;
139
140                 default:
141                         usage();
142                 }
143         }
144         argc -= optind;
145         argv += optind;
146
147         if (env_speed == NULL)
148                 ;
149         else if (strcasecmp("max", env_speed) == 0)
150                 speed = CDR_MAX_SPEED;
151         else
152                 speed = atoi(env_speed) * 177;
153         if (speed <= 0)
154                 errx(EX_USAGE, "Invalid speed: %s", env_speed);
155
156         if (argc == 0)
157                 usage();
158
159         if ((fd = open(dev, O_RDWR, 0)) < 0)
160                 err(EX_NOINPUT, "open(%s)", dev);
161
162         if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
163                 err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
164
165         if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
166                 err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
167
168         global_fd_for_cleanup = fd;
169         err_set_exit(cleanup);
170         signal(SIGHUP, cleanup_signal);
171         signal(SIGINT, cleanup_signal);
172         signal(SIGTERM, cleanup_signal);
173
174         for (arg = 0; arg < argc; arg++) {
175                 if (!strcasecmp(argv[arg], "fixate")) {
176                         fixate = 1;
177                         continue;
178                 }
179                 if (!strcasecmp(argv[arg], "eject")) {
180                         eject = 1;
181                         break;
182                 }
183                 if (!strcasecmp(argv[arg], "msinfo")) {
184                         struct ioc_read_toc_single_entry entry;
185                         struct ioc_toc_header header;
186
187                         if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0)
188                                 err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
189                         bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
190                         entry.address_format = CD_LBA_FORMAT;
191                         entry.track = header.ending_track;
192                         if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
193                                 err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
194                         if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
195                                 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
196                         fprintf(stdout, "%d,%d\n",
197                                 ntohl(entry.entry.addr.lba), addr);
198
199                         break;
200                 }
201                 if ((!strcasecmp(argv[arg], "erase") ||
202                      !strcasecmp(argv[arg], "blank")) && !test_write) {
203                         int blank, pct, last = 0;
204
205                         if (!strcasecmp(argv[arg], "erase"))
206                                 blank = CDR_B_ALL;
207                         else
208                                 blank = CDR_B_MIN;
209                         if (!quiet)
210                                 fprintf(stderr, "%sing CD, please wait..\r",
211                                         blank == CDR_B_ALL ? "eras" : "blank");
212
213                         if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
214                                 err(EX_IOERR, "ioctl(CDRIOCBLANK)");
215                         while (1) {
216                                 sleep(1);
217                                 if (ioctl(fd, CDRIOCGETPROGRESS, &pct) == -1)
218                                         err(EX_IOERR,"ioctl(CDRIOGETPROGRESS)");
219                                 if (pct > 0 && !quiet)
220                                         fprintf(stderr,
221                                                 "%sing CD - %d %% done     \r",
222                                                 blank == CDR_B_ALL ?
223                                                 "eras" : "blank", pct);
224                                 if (pct == 100 || (pct == 0 && last > 90))
225                                         break;
226                                 last = pct;
227                         }
228                         if (!quiet)
229                                 printf("\n");
230                         continue;
231                 }
232                 if (!strcasecmp(argv[arg], "format") && !test_write) {
233                         if (arg + 1 < argc &&
234                                 (!strcasecmp(argv[arg + 1], "dvd+rw") ||
235                                 !strcasecmp(argv[arg + 1], "dvd-rw")))
236                                 do_format(fd, force, argv[arg + 1]);
237                         else
238                                 errx(EX_NOINPUT, "format media type invalid");
239                         arg++;
240                         continue;
241                 }
242                 if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
243                         block_type = CDR_DB_RAW;
244                         block_size = 2352;
245                         continue;
246                 }
247                 if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
248                         block_type = CDR_DB_ROM_MODE1;
249                         block_size = 2048;
250                         continue;
251                 }
252                 if (!strcasecmp(argv[arg], "mode2")) {
253                         block_type = CDR_DB_ROM_MODE2;
254                         block_size = 2336;
255                         continue;
256                 }
257                 if (!strcasecmp(argv[arg], "xamode1")) {
258                         block_type = CDR_DB_XA_MODE1;
259                         block_size = 2048;
260                         continue;
261                 }
262                 if (!strcasecmp(argv[arg], "xamode2")) {
263                         block_type = CDR_DB_XA_MODE2_F2;
264                         block_size = 2324;
265                         continue;
266                 }
267                 if (!strcasecmp(argv[arg], "vcd")) {
268                         block_type = CDR_DB_XA_MODE2_F2;
269                         block_size = 2352;
270                         dao = 1;
271                         nogap = 1;
272                         continue;
273                 }
274                 if (!strcasecmp(argv[arg], "dvdrw")) {
275                         block_type = CDR_DB_ROM_MODE1;
276                         block_size = 2048;
277                         dvdrw = 1;
278                         continue;
279                 }
280
281                 if (!block_size)
282                         errx(EX_NOINPUT, "no data format selected");
283                 if (list) {
284                         char file_buf[MAXPATHLEN + 1], *eol;
285                         FILE *fp;
286
287                         if ((fp = fopen(argv[arg], "r")) == NULL)
288                                 err(EX_NOINPUT, "fopen(%s)", argv[arg]);
289
290                         while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
291                                 if (*file_buf == '#' || *file_buf == '\n')
292                                         continue;
293                                 if ((eol = strchr(file_buf, '\n')))
294                                         *eol = '\0';
295                                 add_track(file_buf, block_size, block_type, nogap);
296                         }
297                         if (feof(fp))
298                                 fclose(fp);
299                         else
300                                 err(EX_IOERR, "fgets(%s)", file_buf);
301                 }
302                 else
303                         add_track(argv[arg], block_size, block_type, nogap);
304         }
305         if (notracks) {
306                 if (dvdrw && notracks > 1)
307                         errx(EX_USAGE, "DVD's only have 1 track");
308                 if (ioctl(fd, CDIOCSTART, 0) < 0)
309                         err(EX_IOERR, "ioctl(CDIOCSTART)");
310                 if (!cdopen) {
311                         if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
312                                 err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
313                         cdopen = 1;
314                 }
315                 if (dao)
316                         do_DAO(fd, test_write, multi);
317                 else
318                         do_TAO(fd, test_write, preemp, dvdrw);
319         }
320         if (!test_write && fixate && !dao && !dvdrw) {
321                 if (!quiet)
322                         fprintf(stderr, "fixating CD, please wait..\n");
323                 if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
324                         err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
325         }
326
327         if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
328                 err_set_exit(NULL);
329                 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
330         }
331
332         if (eject)
333                 if (ioctl(fd, CDIOCEJECT) < 0)
334                         err(EX_IOERR, "ioctl(CDIOCEJECT)");
335
336         signal(SIGHUP, SIG_DFL);
337         signal(SIGINT, SIG_DFL);
338         signal(SIGTERM, SIG_DFL);
339         close(fd);
340         exit(EX_OK);
341 }
342
343 void
344 add_track(char *name, int block_size, int block_type, int nogap)
345 {
346         struct stat sb;
347         int file;
348         static int done_stdin = 0;
349
350         if (!strcmp(name, "-")) {
351                 if (done_stdin) {
352                         warn("skipping multiple usages of stdin");
353                         return;
354                 }
355                 file = STDIN_FILENO;
356                 done_stdin = 1;
357         }
358         else if ((file = open(name, O_RDONLY, 0)) < 0)
359                 err(EX_NOINPUT, "open(%s)", name);
360         if (fstat(file, &sb) < 0)
361                 err(EX_IOERR, "fstat(%s)", name);
362         tracks[notracks].file = file;
363         strncpy(tracks[notracks].file_name, name, MAXPATHLEN);
364         if (file == STDIN_FILENO)
365                 tracks[notracks].file_size = -1;
366         else
367                 tracks[notracks].file_size = sb.st_size;
368         tracks[notracks].block_size = block_size;
369         tracks[notracks].block_type = block_type;
370
371         if (nogap && notracks)
372                 tracks[notracks].pregap = 0;
373         else {
374                 if (tracks[notracks - (notracks > 0)].block_type == block_type)
375                         tracks[notracks].pregap = 150;
376                 else
377                         tracks[notracks].pregap = 255;
378         }
379
380         if (verbose) {
381                 int pad = 0;
382
383                 if (tracks[notracks].file_size / tracks[notracks].block_size !=
384                     roundup_blocks(&tracks[notracks]))
385                         pad = 1;
386                 fprintf(stderr,
387                         "adding type 0x%02x file %s size %jd KB %d blocks %s\n",
388                         tracks[notracks].block_type, name,
389                         (intmax_t)sb.st_size/1024,
390                         roundup_blocks(&tracks[notracks]),
391                         pad ? "(0 padded)" : "");
392         }
393         notracks++;
394 }
395
396 void
397 do_DAO(int fd, int test_write, int multi)
398 {
399         struct cdr_cuesheet sheet;
400         struct cdr_cue_entry cue[100];
401         int format = CDR_SESS_CDROM;
402         int addr, i, j = 0;
403
404         int bt2ctl[16] = { 0x0,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
405                            0x4, 0x4, 0x4, 0x4, 0x4, 0x4,  -1,  -1 };
406
407         int bt2df[16] = { 0x0,    -1,   -1,   -1,   -1,   -1,   -1,   -1,
408                           0x10, 0x30, 0x20,   -1, 0x21,   -1,   -1,   -1 };
409
410         if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
411                 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
412         if (verbose)
413                 fprintf(stderr, "next writeable LBA %d\n", addr);
414
415         cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
416                 (bt2df[tracks[0].block_type] & 0xf0) |
417                 (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
418
419         for (i = 0; i < notracks; i++) {
420                 if (bt2ctl[tracks[i].block_type] < 0 ||
421                     bt2df[tracks[i].block_type] < 0)
422                         errx(EX_IOERR, "track type not supported in DAO mode");
423
424                 if (tracks[i].block_type >= CDR_DB_XA_MODE1)
425                         format = CDR_SESS_CDROM_XA;
426
427                 if (i == 0) {
428                         addr += tracks[i].pregap;
429                         tracks[i].addr = addr;
430
431                         cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
432                                 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
433                                 0x00, addr);
434
435                 }
436                 else {
437                         if (tracks[i].pregap) {
438                                 if (tracks[i].block_type > 0x7) {
439                                         cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
440                                                 0x01, i+1, 0x0,
441                                                 (bt2df[tracks[i].block_type] & 0xf0) |
442                                                 (tracks[i].block_type < 8 ? 0x01 :0x04),
443                                                 0x00, addr);
444                                 }
445                                 else
446                                         cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
447                                                 0x01, i+1, 0x0,
448                                                 bt2df[tracks[i].block_type],
449                                                 0x00, addr);
450                         }
451                         tracks[i].addr = tracks[i - 1].addr +
452                                 roundup_blocks(&tracks[i - 1]);
453
454                         cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
455                                 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
456                                 0x00, addr + tracks[i].pregap);
457
458                         if (tracks[i].block_type > 0x7)
459                                 addr += tracks[i].pregap;
460                 }
461                 addr += roundup_blocks(&tracks[i]);
462         }
463
464         cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
465                 (bt2df[tracks[i - 1].block_type] & 0xf0) |
466                 (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
467
468         sheet.len = j * 8;
469         sheet.entries = cue;
470         sheet.test_write = test_write;
471         sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
472         sheet.session_format = format;
473         if (verbose) {
474                 u_int8_t *ptr = (u_int8_t *)sheet.entries;
475
476                 fprintf(stderr,"CUE sheet:");
477                 for (i = 0; i < sheet.len; i++)
478                         if (i % 8)
479                                 fprintf(stderr," %02x", ptr[i]);
480                         else
481                                 fprintf(stderr,"\n%02x", ptr[i]);
482                 fprintf(stderr,"\n");
483         }
484
485         if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
486                 err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
487
488         for (i = 0; i < notracks; i++) {
489                 if (write_file(fd, &tracks[i])) {
490                         cleanup_flush();
491                         err(EX_IOERR, "write_file");
492                 }
493         }
494
495         ioctl(fd, CDRIOCFLUSH);
496 }
497
498 void
499 do_TAO(int fd, int test_write, int preemp, int dvdrw)
500 {
501         struct cdr_track track;
502         int i;
503
504         for (i = 0; i < notracks; i++) {
505                 track.test_write = test_write;
506                 track.datablock_type = tracks[i].block_type;
507                 track.preemp = preemp;
508                 if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
509                         err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
510
511                 if (dvdrw)
512                         tracks[i].addr = 0;
513                 else
514                         if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR,
515                                   &tracks[i].addr) < 0)
516                                 err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
517
518                 if (!quiet)
519                         fprintf(stderr, "next writeable LBA %d\n",
520                                 tracks[i].addr);
521                 if (write_file(fd, &tracks[i])) {
522                         cleanup_flush();
523                         err(EX_IOERR, "write_file");
524                 }
525                 if (ioctl(fd, CDRIOCFLUSH) < 0)
526                         err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
527         }
528 }
529
530 #define NTOH3B(x)       ((x&0x0000ff)<<16) | (x&0x00ff00) | ((x&0xff0000)>>16)
531
532 void
533 do_format(int the_fd, int force, char *type)
534 {
535         struct cdr_format_capacities capacities;
536         struct cdr_format_params format_params;
537         int count, i, pct, last = 0;
538
539         if (ioctl(the_fd, CDRIOCREADFORMATCAPS, &capacities) == -1)
540                 err(EX_IOERR, "ioctl(CDRIOCREADFORMATCAPS)");
541
542         if (verbose) {
543                 fprintf(stderr, "format list entries=%zd\n",
544                         capacities.length / sizeof(struct cdr_format_capacity));
545                 fprintf(stderr, "current format: blocks=%u type=0x%x block_size=%u\n",
546                         ntohl(capacities.blocks), capacities.type,
547                         NTOH3B(capacities.block_size));
548         }
549
550         count = capacities.length / sizeof(struct cdr_format_capacity);
551         if (verbose) {
552                 for (i = 0; i < count; ++i)
553                         fprintf(stderr,
554                                 "format %d: blocks=%u type=0x%x param=%u\n",
555                                 i, ntohl(capacities.format[i].blocks),
556                                 capacities.format[i].type,
557                                 NTOH3B(capacities.format[i].param));
558         }
559
560         for (i = 0; i < count; ++i) {
561                 if (!strcasecmp(type, "dvd+rw")) {
562                         if (capacities.format[i].type == 0x26) {
563                                 break;
564                         }
565                 }
566                 if (!strcasecmp(type, "dvd-rw")) {
567                         if (capacities.format[i].type == 0x0) {
568                                 break;
569                         }
570                 }
571         }
572         if (i == count)
573                 errx(EX_IOERR, "could not find a valid format capacity");
574
575         if (!quiet)
576                 fprintf(stderr,"formatting with blocks=%u type=0x%x param=%u\n",
577                         ntohl(capacities.format[i].blocks),
578                         capacities.format[i].type,
579                         NTOH3B(capacities.format[i].param));
580
581         if (!force && capacities.type == 2)
582                 errx(EX_IOERR, "media already formatted (use -F to override)");
583
584         memset(&format_params, 0, sizeof(struct cdr_format_params));
585         format_params.fov = 1;
586         format_params.immed = 1;
587         format_params.length = ntohs(sizeof(struct cdr_format_capacity));
588         memcpy(&format_params.format, &capacities.format[i],
589                 sizeof(struct cdr_format_capacity));
590
591         if(ioctl(the_fd, CDRIOCFORMAT, &format_params) == -1)
592                 err(EX_IOERR, "ioctl(CDRIOCFORMAT)");
593
594         while (1) {
595                 sleep(1);
596                 if (ioctl(the_fd, CDRIOCGETPROGRESS, &pct) == -1)
597                         err(EX_IOERR, "ioctl(CDRIOGETPROGRESS)");
598                 if (pct > 0 && !quiet)
599                         fprintf(stderr, "formatting DVD - %d %% done     \r",
600                                 pct);
601                 if (pct == 100 || (pct == 0 && last > 90))
602                         break;
603                 last = pct;
604         }
605         if (!quiet)
606                 fprintf(stderr, "\n");
607 }
608
609 int
610 write_file(int fd, struct track_info *track_info)
611 {
612         off_t size, count, filesize;
613         char buf[2352*BLOCKS];
614         static off_t tot_size = 0;
615
616         filesize = track_info->file_size / 1024;
617
618         if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
619                 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
620
621         if (track_info->addr >= 0)
622                 lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
623
624         if (verbose)
625                 fprintf(stderr, "addr = %d size = %jd blocks = %d\n",
626                         track_info->addr, (intmax_t)track_info->file_size,
627                         roundup_blocks(track_info));
628
629         if (!quiet) {
630                 if (track_info->file == STDIN_FILENO)
631                         fprintf(stderr, "writing from stdin\n");
632                 else
633                         fprintf(stderr,
634                                 "writing from file %s size %jd KB\n",
635                                 track_info->file_name, (intmax_t)filesize);
636         }
637         size = 0;
638
639         while ((count = read(track_info->file, buf,
640                              track_info->file_size == -1
641                                 ? track_info->block_size * BLOCKS
642                                 : MIN((track_info->file_size - size),
643                                       track_info->block_size * BLOCKS))) > 0) {
644                 int res;
645
646                 if (count % track_info->block_size) {
647                         /* pad file to % block_size */
648                         bzero(&buf[count],
649                               (track_info->block_size * BLOCKS) - count);
650                         count = ((count / track_info->block_size) + 1) *
651                                 track_info->block_size;
652                 }
653                 if ((res = write(fd, buf, count)) != count) {
654                         if (res == -1) {
655                                 fprintf(stderr, "\n");
656                                 close(track_info->file);
657                                 return errno;
658                         } else
659                                 fprintf(stderr, "\nonly wrote %d of %jd"
660                                     " bytes\n", res, (intmax_t)count);
661                         break;
662                 }
663                 size += count;
664                 tot_size += count;
665                 if (!quiet) {
666                         int pct;
667
668                         fprintf(stderr, "written this track %jd KB",
669                             (intmax_t)size/1024);
670                         if (track_info->file != STDIN_FILENO && filesize) {
671                                 pct = (size / 1024) * 100 / filesize;
672                                 fprintf(stderr, " (%d%%)", pct);
673                         }
674                         fprintf(stderr, " total %jd KB\r",
675                             (intmax_t)tot_size / 1024);
676                 }
677                 if (track_info->file_size != -1
678                     && size >= track_info->file_size)
679                         break;
680         }
681
682         if (!quiet)
683                 fprintf(stderr, "\n");
684         close(track_info->file);
685         return 0;
686 }
687
688 int
689 roundup_blocks(struct track_info *track)
690 {
691         return ((track->file_size + track->block_size - 1) / track->block_size);
692 }
693
694 void
695 cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
696         int dataform, int scms, int lba)
697 {
698         cue->adr = adr;
699         cue->ctl = ctl;
700         cue->track = track;
701         cue->index = idx;
702         cue->dataform = dataform;
703         cue->scms = scms;
704         lba += 150;
705         cue->min = lba / (60*75);
706         cue->sec = (lba % (60*75)) / 75;
707         cue->frame = (lba % (60*75)) % 75;
708 }
709
710 void
711 cleanup(int dummy __unused)
712 {
713         if (ioctl(global_fd_for_cleanup, CDRIOCSETBLOCKSIZE,
714             &saved_block_size) < 0)
715                 err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
716 }
717
718 void
719 cleanup_flush(void)
720 {
721         if (ioctl(global_fd_for_cleanup, CDRIOCFLUSH) < 0)
722                 err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
723 }
724
725 void
726 cleanup_signal(int sig)
727 {
728         signal(sig, SIG_IGN);
729         ioctl(global_fd_for_cleanup, CDRIOCFLUSH);
730         write(STDERR_FILENO, "\nAborted\n", 10);
731         _exit(EXIT_FAILURE);
732 }
733
734 void
735 usage(void)
736 {
737         fprintf(stderr,
738             "usage: %s [-deFlmnpqtv] [-f device] [-s speed] [command]"
739             " [command file ...]\n", getprogname());
740         exit(EX_USAGE);
741 }