Bug Summary

File:src/usr.bin/cdio/rip.c
Warning:line 171, column 9
Assigned value is garbage or undefined

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name rip.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/cdio/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/cdio/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/cdio/rip.c
1/* $OpenBSD: rip.c,v 1.18 2019/06/28 13:35:00 deraadt Exp $ */
2
3/*
4 * Copyright (c) 2007 Alexey Vatchenko <av@bsdua.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18#include <sys/types.h>
19#include <sys/signal.h>
20#include <sys/device.h>
21
22#include <sys/cdio.h>
23#include <sys/ioctl.h>
24#include <sys/scsiio.h>
25#include <sys/stat.h>
26#include <sys/time.h>
27
28#include <scsi/scsi_all.h>
29#include <scsi/scsi_disk.h>
30#include <scsi/scsiconf.h>
31#include <scsi/cd.h>
32
33#include <ctype.h>
34#include <err.h>
35#include <errno(*__errno()).h>
36#include <fcntl.h>
37#include <sndio.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <time.h>
42#include <unistd.h>
43
44#include "extern.h"
45
46extern int fd;
47extern int msf;
48extern struct cd_toc_entry *toc_buffer;
49
50extern u_int msf2lba(u_char m, u_char s, u_char f);
51extern int read_toc_entrys(int size);
52
53/*
54 * Arguments parser
55 */
56TAILQ_HEAD(track_pair_head, track_pair)struct track_pair_head { struct track_pair *tqh_first; struct
track_pair **tqh_last; }
;
57
58static int _parse_val(char *start, char *nxt, int *val);
59static int _parse_pair(char *start, char *nxt, int *val1, int *val2);
60static int _add_pair(struct track_pair_head *head, int val1, int val2,
61 int issorted);
62
63struct track_pair {
64 u_char start;
65 u_char end;
66 TAILQ_ENTRY(track_pair)struct { struct track_pair *tqe_next; struct track_pair **tqe_prev
; }
list;
67};
68
69void parse_tracks_init(struct track_pair_head *head);
70void parse_tracks_final(struct track_pair_head *head);
71int parse_tracks(struct track_pair_head *head, u_char first, u_char last,
72 const char *arg, int issorted);
73int parse_tracks_add(struct track_pair_head *head, u_char first,
74 u_char last, int issorted);
75
76/*
77 * Tracks ripping
78 */
79/* Header of the canonical WAVE file */
80static u_char wavehdr[44] = {
81 'R', 'I', 'F', 'F', 0x0, 0x0, 0x0, 0x0, 'W', 'A', 'V', 'E',
82 'f', 'm', 't', ' ', 0x10, 0x0, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0,
83 0x44, 0xac, 0x0, 0x0, 0x10, 0xb1, 0x2, 0x0, 0x4, 0x0, 0x10, 0x0,
84 'd', 'a', 't', 'a', 0x0, 0x0, 0x0, 0x0
85};
86
87static int write_sector(int, u_char *, u_int32_t);
88
89int read_data_sector(u_int32_t, u_char *, u_int32_t);
90
91struct track {
92 int fd; /* descriptor of output file */
93 struct sio_hdl *hdl; /* sndio handle */
94 struct sio_par par; /* sndio parameters */
95 u_int track; /* track number */
96 char name[12]; /* output file name, i.e. trackXX.wav/trackXX.dat */
97 u_char isaudio; /* true if audio track, otherwise it's data track */
98 u_int32_t start_lba; /* starting address of this track */
99 u_int32_t end_lba; /* starting address of the next track */
100};
101
102int read_track(struct track *);
103
104int rip_next_track(struct track *);
105int play_next_track(struct track *);
106
107static int rip_tracks_loop(struct track_pair *tp, u_int n_tracks,
108 int (*next_track)(struct track *));
109
110int rip_tracks(char *arg, int (*next_track)(struct track *),
111 int issorted);
112
113/* Next-Track function exit codes */
114#define NXTRACK_OK0 0
115#define NXTRACK_FAIL1 1
116#define NXTRACK_SKIP2 2
117
118static int
119_parse_val(char *start, char *nxt, int *val)
120{
121 char *p;
122 int i, base, n;
123
124 n = nxt - start;
125
126 if (n > 3 || n < 1)
25
Assuming 'n' is > 3
127 return (-1);
128 for (p = start; p < nxt; p++) {
129 if (!isdigit((unsigned char)*p))
130 return (-1);
131 }
132
133 *val = 0;
134 base = 1;
135 for (i = 0; i < n; i++) {
136 *val += base * (start[n - i - 1] - '0');
137 base *= 10;
138 }
139 return (0);
140}
141
142static int
143_parse_pair(char *start, char *nxt, int *val1, int *val2)
144{
145 char *p, *delim;
146 int error;
147
148 delim = NULL((void *)0);
149 p = start;
150 while (p < nxt) {
19
Loop condition is true. Entering loop body
22
Loop condition is false. Execution continues on line 156
151 if (*p == '-')
20
Assuming the condition is false
21
Taking false branch
152 delim = p;
153 p++;
154 }
155
156 if (delim
22.1
'delim' is equal to NULL
!= NULL((void *)0)) {
23
Taking false branch
157 error = 0;
158 if (delim - start < 1)
159 *val1 = -1;
160 else
161 error = _parse_val(start, delim, val1);
162
163 if (error == 0) {
164 if ((nxt - delim - 1) < 1)
165 *val2 = -1;
166 else
167 error = _parse_val(delim + 1, nxt, val2);
168 }
169 } else {
170 error = _parse_val(start, nxt, val1);
24
Calling '_parse_val'
26
Returning from '_parse_val'
171 *val2 = *val1;
27
Assigned value is garbage or undefined
172 }
173
174 if (error == 0) {
175 if (*val1 > 99 || *val2 > 99)
176 error = -1;
177 }
178
179 return (error);
180}
181
182static int
183_add_pair(struct track_pair_head *head, int val1, int val2, int issorted)
184{
185 u_char v1, v2, v3;
186 struct track_pair *tp, *entry;
187 int fix;
188
189 v1 = (u_char)val1;
190 v2 = (u_char)val2;
191
192 if (issorted) {
193 /* 1. Fix order */
194 if (v1 > v2) {
195 v3 = v1;
196 v1 = v2;
197 v2 = v3;
198 }
199
200 /* 2. Find closest range and fix it */
201 fix = 0;
202 TAILQ_FOREACH(entry, head, list)for((entry) = ((head)->tqh_first); (entry) != ((void *)0);
(entry) = ((entry)->list.tqe_next))
{
203 if (v1 + 1 == entry->start || v1 == entry->start)
204 fix = 1;
205 else if (v1 > entry->start && v1 <= entry->end + 1)
206 fix = 1;
207 else if (v2 + 1 == entry->start || v2 == entry->start)
208 fix = 1;
209 else if (v2 > entry->start && v2 <= entry->end + 1)
210 fix = 1;
211 if (fix)
212 break;
213 }
214
215 if (fix) {
216 if (v1 < entry->start)
217 entry->start = v1;
218 if (v2 > entry->end)
219 entry->end = v2;
220
221 return (0);
222 }
223 }
224
225 tp = malloc(sizeof(*tp));
226 if (tp == NULL((void *)0))
227 return (-1);
228
229 tp->start = v1;
230 tp->end = v2;
231 TAILQ_INSERT_TAIL(head, tp, list)do { (tp)->list.tqe_next = ((void *)0); (tp)->list.tqe_prev
= (head)->tqh_last; *(head)->tqh_last = (tp); (head)->
tqh_last = &(tp)->list.tqe_next; } while (0)
;
232
233 return (0);
234}
235
236void
237parse_tracks_init(struct track_pair_head *head)
238{
239
240 memset(head, 0, sizeof(*head));
241 TAILQ_INIT(head)do { (head)->tqh_first = ((void *)0); (head)->tqh_last =
&(head)->tqh_first; } while (0)
;
242}
243
244void
245parse_tracks_final(struct track_pair_head *head)
246{
247 struct track_pair *tp;
248
249 while ((tp = TAILQ_FIRST(head)((head)->tqh_first)) != NULL((void *)0)) {
250 TAILQ_REMOVE(head, tp, list)do { if (((tp)->list.tqe_next) != ((void *)0)) (tp)->list
.tqe_next->list.tqe_prev = (tp)->list.tqe_prev; else (head
)->tqh_last = (tp)->list.tqe_prev; *(tp)->list.tqe_prev
= (tp)->list.tqe_next; ; ; } while (0)
;
251 free(tp);
252 }
253}
254
255int
256parse_tracks(struct track_pair_head *head, u_char first, u_char last,
257 const char *arg, int issorted)
258{
259 char *p, *nxt;
260 int error, val1, val2;
12
'val1' declared without an initial value
261
262 p = (char *)arg;
263 for (;;) {
13
Loop condition is true. Entering loop body
264 /* Skip trailing spaces */
265 while (*p != '\0' && isspace((unsigned char)*p))
14
Loop condition is false. Execution continues on line 267
266 ++p;
267 if (*p == '\0')
15
Taking false branch
268 break;
269
270 /* Search for the next space symbol */
271 nxt = p;
272 while (*nxt != '\0' && !isspace((unsigned char)*nxt))
16
Loop condition is true. Entering loop body
17
Assuming the condition is false
273 ++nxt;
274 /* ``nxt'' can't be equal to ``p'' here */
275 error = _parse_pair(p, nxt, &val1, &val2);
18
Calling '_parse_pair'
276 if (error != 0)
277 break; /* parse error */
278
279 if (val1 == -1)
280 val1 = first;
281 if (val2 == -1)
282 val2 = last;
283
284 error = _add_pair(head, val1, val2, issorted);
285 if (error != 0)
286 break; /* allocation error */
287
288 p = nxt;
289 }
290
291 return (0);
292}
293
294int
295parse_tracks_add(struct track_pair_head *head, u_char first, u_char last,
296 int issorted)
297{
298
299 return _add_pair(head, first, last, issorted);
300}
301
302static int
303write_sector(int fd, u_char *sec, u_int32_t secsize)
304{
305 ssize_t res;
306
307 while (secsize > 0) {
308 res = write(fd, sec, secsize);
309 if (res == -1)
310 return (-1);
311
312 sec += res;
313 secsize -= res;
314 }
315
316 return (0);
317}
318
319/*
320 * ERRORS
321 * The function can return
322 * [EBUSY] Device is busy.
323 * [ETIMEDOUT] Operation timeout.
324 * [EIO] Any other errors.
325 * [EAGAIN] The operation must be made again. XXX - not implemented
326 */
327int
328read_data_sector(u_int32_t lba, u_char *sec, u_int32_t secsize)
329{
330 scsireq_t scr;
331 u_char *cmd;
332 int error;
333
334 memset(&scr, 0, sizeof(scr));
335
336 cmd = (u_char *)scr.cmd;
337 cmd[0] = 0xbe; /* READ CD */
338 _lto4b(lba, cmd + 2); /* Starting Logical Block Address */
339 _lto3b(1, cmd + 6); /* Transfer Length in Blocks */
340 cmd[9] = 0x10; /* User Data field */
341
342 scr.flags = SCCMD_ESCAPE0x00000010 | SCCMD_READ0x00000001;
343 scr.databuf = sec;
344 scr.datalen = secsize;
345 scr.cmdlen = 12;
346 scr.timeout = 120000;
347 scr.senselen = SENSEBUFLEN48;
348
349 /* XXX - what's wrong with DVD? */
350
351 error = ioctl(fd, SCIOCCOMMAND(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof
(scsireq_t) & 0x1fff) << 16) | ((('Q')) << 8)
| ((1)))
, &scr);
352 if (error == -1)
353 return (EIO5);
354 else if (scr.retsts == SCCMD_BUSY0x02)
355 return (EBUSY16);
356 else if (scr.retsts == SCCMD_TIMEOUT0x01)
357 return (ETIMEDOUT60);
358 else if (scr.retsts != SCCMD_OK0x00)
359 return (EIO5);
360
361 return (0);
362}
363
364int
365read_track(struct track *ti)
366{
367 struct timespec ts, ots, ats;
368 u_int32_t i, blksize, n_sec;
369 u_char *sec;
370 int error;
371
372 n_sec = ti->end_lba - ti->start_lba;
373 blksize = (ti->isaudio) ? 2352 : 2048;
374 sec = malloc(blksize);
375 if (sec == NULL((void *)0))
376 return (-1);
377
378 timespecclear(&ots)(&ots)->tv_sec = (&ots)->tv_nsec = 0;
379 ats.tv_sec = 1;
380 ats.tv_nsec = 0;
381
382 for (i = 0; i < n_sec; ) {
383 clock_gettime(CLOCK_MONOTONIC3, &ts);
384 if (timespeccmp(&ts, &ots, >)(((&ts)->tv_sec == (&ots)->tv_sec) ? ((&ts)
->tv_nsec > (&ots)->tv_nsec) : ((&ts)->tv_sec
> (&ots)->tv_sec))
) {
385 fprintf(stderr(&__sF[2]), "\rtrack %u '%c' %08u/%08u %3u%%",
386 ti->track,
387 (ti->isaudio) ? 'a' : 'd', i, n_sec,
388 100 * i / n_sec);
389 timespecadd(&ts, &ats, &ots)do { (&ots)->tv_sec = (&ts)->tv_sec + (&ats
)->tv_sec; (&ots)->tv_nsec = (&ts)->tv_nsec +
(&ats)->tv_nsec; if ((&ots)->tv_nsec >= 1000000000L
) { (&ots)->tv_sec++; (&ots)->tv_nsec -= 1000000000L
; } } while (0)
;
390 }
391
392 error = read_data_sector(i + ti->start_lba, sec, blksize);
393 if (error == 0) {
394 if (ti->fd >= 0 &&
395 (write_sector(ti->fd, sec, blksize) != 0)) {
396 free(sec);
397 warnx("\nerror while writing to the %s file",
398 ti->name);
399 return (-1);
400 }
401 if (ti->hdl != NULL((void *)0) &&
402 (sio_write(ti->hdl, sec, blksize) == 0)) {
403 sio_close(ti->hdl);
404 ti->hdl = NULL((void *)0);
405 warnx("\nerror while writing to audio output");
406 return (-1);
407 }
408
409 i++;
410 } else if (error != EAGAIN35) {
411 free(sec);
412 warnx("\nerror while reading from device");
413 return (-1);
414 }
415 }
416
417 free(sec);
418 fprintf(stderr(&__sF[2]), "\rtrack %u '%c' %08u/%08u 100%%\n",
419 ti->track,
420 (ti->isaudio) ? 'a' : 'd', i, n_sec);
421 return (0);
422}
423
424int
425rip_next_track(struct track *info)
426{
427 int error;
428 u_int32_t size;
429
430 info->fd = open(info->name, O_CREAT0x0200 | O_TRUNC0x0400 | O_RDWR0x0002,
431 S_IRUSR0000400 | S_IWUSR0000200);
432 if (info->fd == -1) {
433 warnx("can't open %s file", info->name);
434 return (NXTRACK_FAIL1);
435 }
436
437 if (info->isaudio) {
438 /*
439 * Prepend audio track with Wave header
440 */
441 size = 2352 * (info->end_lba - info->start_lba);
442 *(u_int32_t *)(wavehdr + 4) = htole32(size + 36)((__uint32_t)(size + 36));
443 *(u_int32_t *)(wavehdr + 40) = htole32(size)((__uint32_t)(size));
444 error = write_sector(info->fd, wavehdr, sizeof(wavehdr));
445 if (error == -1) {
446 warnx("can't write WAVE header for %s file",
447 info->name);
448 return (NXTRACK_FAIL1);
449 }
450 }
451
452 return (NXTRACK_OK0);
453}
454
455int
456play_next_track(struct track *info)
457{
458 if (!info->isaudio)
459 return (NXTRACK_SKIP2);
460
461 if (info->hdl != NULL((void *)0))
462 return (NXTRACK_OK0);
463
464 info->hdl = sio_open(NULL((void *)0), SIO_PLAY1, 0);
465 if (info->hdl == NULL((void *)0)) {
466 warnx("could not open audio backend");
467 goto bad;
468 }
469
470 sio_initpar(&info->par);
471
472 info->par.rate = 44100;
473 info->par.pchan = 2;
474 info->par.bits = 16;
475 info->par.sig = 1;
476 info->par.le = 1;
477 info->par.appbufsz = info->par.rate * 3 / 4;
478
479 if (sio_setpar(info->hdl, &info->par) == 0) {
480 warnx("could not set audio parameters");
481 goto bad;
482 }
483
484 if (sio_getpar(info->hdl, &info->par) == 0) {
485 warnx("could not get audio parameters");
486 goto bad;
487 }
488
489 if (info->par.le != 1 ||
490 info->par.sig != 1 ||
491 info->par.bits != 16 ||
492 info->par.pchan != 2 ||
493 (info->par.rate > 44100 * 1.05 || info->par.rate < 44100 * 0.95)) {
494 warnx("could not configure audio parameters as desired");
495 goto bad;
496 }
497
498 if (sio_start(info->hdl) == 0) {
499 warnx("could not start audio output");
500 goto bad;
501 }
502
503 return (NXTRACK_OK0);
504
505bad:
506 if (info->hdl != NULL((void *)0)) {
507 sio_close(info->hdl);
508 info->hdl = NULL((void *)0);
509 }
510 return (NXTRACK_FAIL1);
511}
512
513static int
514rip_tracks_loop(struct track_pair *tp, u_int n_tracks,
515 int (*next_track)(struct track *))
516{
517 struct track info;
518 u_char trk;
519 u_int i;
520 char order;
521 int error;
522
523 info.fd = -1;
524 info.hdl = NULL((void *)0);
525
526 order = (tp->start > tp->end) ? -1 : 1;
527 trk = tp->start;
528 for (;;) {
529 error = 0;
530 for (i = 0; i < n_tracks; i++) {
531 if (trk == toc_buffer[i].track)
532 break;
533 }
534
535 if (i != n_tracks) {
536 /* Track is found */
537 info.track = toc_buffer[i].track;
538 info.isaudio = (toc_buffer[i].control & 4) == 0;
539 snprintf(info.name, sizeof(info.name), "track%02u.%s",
540 toc_buffer[i].track,
541 (info.isaudio) ? "wav" : "dat");
542
543 if (msf) {
544 info.start_lba = msf2lba(
545 toc_buffer[i].addr.msf.minute,
546 toc_buffer[i].addr.msf.second,
547 toc_buffer[i].addr.msf.frame);
548 info.end_lba = msf2lba(
549 toc_buffer[i + 1].addr.msf.minute,
550 toc_buffer[i + 1].addr.msf.second,
551 toc_buffer[i + 1].addr.msf.frame);
552 } else {
553 info.start_lba = toc_buffer[i].addr.lba;
554 info.end_lba = toc_buffer[i + 1].addr.lba;
555 }
556
557 error = next_track(&info);
558 if (error == NXTRACK_FAIL1) {
559 error = -1;
560 break;
561 } else if (error != NXTRACK_SKIP2) {
562 error = read_track(&info);
563 if (info.fd >= 0) {
564 close(info.fd);
565 info.fd = -1;
566 }
567 if (error != 0) {
568 warnx("can't rip %u track",
569 toc_buffer[i].track);
570 break;
571 }
572 }
573 }
574
575 if (trk == tp->end)
576 break;
577 trk += order;
578 }
579
580 if (info.hdl != NULL((void *)0)) {
581 sio_close(info.hdl);
582 info.hdl = NULL((void *)0);
583 }
584
585 return (error);
586}
587
588int
589rip_tracks(char *arg, int (*next_track)(struct track *), int issorted)
590{
591 struct track_pair_head list;
592 struct track_pair *tp;
593 struct ioc_toc_header h;
594 u_int n;
595 int rc;
596
597 rc = ioctl(fd, CDIOREADTOCHEADER((unsigned long)0x40000000 | ((sizeof(struct ioc_toc_header) &
0x1fff) << 16) | ((('c')) << 8) | ((4)))
, &h);
598 if (rc == -1)
2
Assuming the condition is false
3
Taking false branch
599 return (rc);
600
601 if (h.starting_track > h.ending_track) {
4
Assuming field 'starting_track' is <= field 'ending_track'
5
Taking false branch
602 warnx("TOC starting_track > TOC ending_track");
603 return (0);
604 }
605
606 n = h.ending_track - h.starting_track + 1;
607 rc = read_toc_entrys((n + 1) * sizeof(struct cd_toc_entry));
608 if (rc < 0)
6
Assuming 'rc' is >= 0
7
Taking false branch
609 return (rc);
610
611 parse_tracks_init(&list);
612 /* We assume that all spaces are skipped in ``arg''. */
613 if (arg == NULL((void *)0) || *arg == '\0') {
8
Assuming 'arg' is not equal to NULL
9
Assuming the condition is false
10
Taking false branch
614 rc = parse_tracks_add(&list, h.starting_track, h.ending_track,
615 0);
616 } else {
617 rc = parse_tracks(&list, h.starting_track, h.ending_track, arg,
11
Calling 'parse_tracks'
618 issorted);
619 }
620 if (rc < 0) {
621 warnx("can't create track list");
622 parse_tracks_final(&list);
623 return (rc);
624 }
625
626 TAILQ_FOREACH(tp, &list, list)for((tp) = ((&list)->tqh_first); (tp) != ((void *)0); (
tp) = ((tp)->list.tqe_next))
{
627 rc = rip_tracks_loop(tp, n, next_track);
628 if (rc < 0)
629 break;
630 }
631
632 parse_tracks_final(&list);
633 return (0);
634}
635
636int
637cdrip(char *arg)
638{
639 return rip_tracks(arg, rip_next_track, 1);
640}
641
642int
643cdplay(char *arg)
644{
645 return rip_tracks(arg, play_next_track, 0);
1
Calling 'rip_tracks'
646}