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