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