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