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