1 /*
2  * $Id: cdinfo.c,v 1.6 1999/02/14 16:47:40 dirk Exp $
3  *
4  * This file is part of WorkMan, the civilized CD player library
5  * (c) 1991-1997 by Steven Grimm (original author)
6  * (c) by Dirk F�rsterling (current 'author' = maintainer)
7  * The maintainer can be contacted by his e-mail address:
8  * milliByte@DeathsDoor.com
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the Free
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  *
25  * Get information about a CD.
26  */
27 
28 static char cdinfo_id[] = "$Id: cdinfo.c,v 1.6 1999/02/14 16:47:40 dirk Exp $";
29 
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 
36 #include "include/wm_config.h"
37 
38 #include "include/wm_struct.h"
39 #include "include/wm_cdrom.h"
40 #include "include/wm_cdinfo.h"
41 #include "include/wm_database.h"
42 #include "include/wm_helpers.h"
43 
44 struct wm_play *playlist = NULL;
45 struct wm_cdinfo thiscd, *cd = &thiscd;
46 
47 int	cur_track = -1;	/* Current track number, starting at 1 */
48 int	cur_index = 0;	/* Current index mark */
49 int	cur_lasttrack = 999;	/* Last track to play in current chunk */
50 int	cur_firsttrack = 0;	/* First track of current chunk */
51 int	cur_pos_abs;	/* Current absolute position in seconds */
52 int	cur_frame;	/* Current frame number */
53 int	cur_pos_rel;	/* Current track-relative position in seconds */
54 int	cur_tracklen;	/* Length in seconds of current track */
55 int	cur_cdlen;	/* Length in seconds of entire CD */
56 int	cur_ntracks;	/* Number of tracks on CD (= tracks + sections) */
57 int	cur_nsections;	/* Number of sections currently defined */
58 enum wm_cd_modes	cur_cdmode = WM_CDM_EJECTED;
59 int	cur_listno;	/* Current index into the play list, if playing */
60 char *	cur_artist;	/* Name of current CD's artist */
61 char *	cur_cdname;	/* Album name */
62 char *	cur_trackname;	/* Take a guess */
63 char	cur_contd;	/* Continued flag */
64 char	cur_avoid;	/* Avoid flag */
65 
66 int	exit_on_eject = 0;
67 
68 int cur_stopmode = -1;
69 extern int info_modified;
70 
71 /*
72  * insert_trackinfo()
73  *
74  * Add a new track to the CD info structure.  Pass the position of the new
75  * entry in the track list -- 0 will make this the first track, 1 the second,
76  * etc.  The new entry will be zeroed out.
77  */
78 void
insert_trackinfo(num)79 insert_trackinfo(num)
80 	int	num;
81 {
82 	struct wm_trackinfo *newtrk;
83 
84 	/* Easy case: the list is empty */
85 	if (cd->trk == NULL) {
86 		if ((cd->trk = (struct wm_trackinfo *) calloc(1,
87 						sizeof(*newtrk))) == NULL)
88 		{
89 nomem:
90 			perror("insert_trackinfo");
91 			exit(1);
92 		} else {
93 			return;
94                 } /* if() else */
95         } /* if() */
96 	/* Stick the new entry in cd->trk[]. */
97 	if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) *
98 						(cur_ntracks + 1))) == NULL)
99 		goto nomem;
100 
101 	if (num)
102 		memcpy(newtrk, cd->trk, sizeof(*newtrk) * num);
103 	memset(&newtrk[num], 0, sizeof(*newtrk));
104 	if (num < cur_ntracks)
105 		memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) *
106 			(cur_ntracks - num));
107 
108 	free(cd->trk);
109 	cd->trk = newtrk;
110 }
111 
112 /*
113  * split_trackinfo()
114  *
115  * Split a track in two at a particular position (absolute, in frames).  All
116  * internal data structures and variables will be adjusted to the new
117  * numbering scheme.  Pass in the track number (>=1) to split, which is also
118  * the index into cd->trk[] of the new entry.
119  *
120  * If pos is within 1 second of the start of another track, the split fails.
121  *
122  * Returns 1 on success, 0 if the track couldn't be inserted.
123  *
124  * Note: updating user interface elements is up to the caller.
125  */
126 int
split_trackinfo(int pos)127 split_trackinfo( int pos )
128 {
129 	int	i, l, num;
130 
131 	if (pos < cd->trk[0].start)
132 		return (0);
133 
134 	/* First find the appropriate track. */
135 	for (num = 0; num < cur_ntracks; num++)
136 		if (cd->trk[num].start - 75 < pos &&
137 						cd->trk[num].start + 75 > pos)
138 			return (0);
139 		else if (cd->trk[num].start > pos)
140 			break;
141 	if (num == 0)
142 		return (0);
143 
144 	/* Insert the new entry into the track array. */
145 	insert_trackinfo(num);
146 
147 	/* Update the easy variables. */
148 	if (cur_track > num)
149 		cur_track++;
150 	if (cur_firsttrack > num)
151 		cur_firsttrack++;
152 	if (cur_lasttrack > num)
153 		cur_lasttrack++;
154 
155 	/* Update the user-defined playlists. */
156 	if (cd->lists != NULL)
157 		for (l = 0; cd->lists[l].name != NULL; l++)
158 			if (cd->lists[l].list != NULL)
159 				for (i = 0; cd->lists[l].list[i]; i++)
160 					if (cd->lists[l].list[i] > num)
161 						cd->lists[l].list[i]++;
162 
163 	/* Update the internal playlist. */
164 	if (playlist != NULL)
165 		for (i = 0; playlist[i].start; i++)
166 		{
167 			if (playlist[i].start > num)
168 				playlist[i].start++;
169 			if (playlist[i].end > num)
170 				playlist[i].end++;
171 		}
172 
173 	/* Now adjust the information in cd->trk[]. */
174 	cd->trk[num].start = pos;
175 	if (num == cur_ntracks)
176 		cd->trk[num].length = cur_cdlen - pos / 75;
177 	else
178 		cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75;
179 	cd->trk[num - 1].length -= cd->trk[num].length;
180 	if (cur_track == num)
181 		cur_tracklen -= cd->trk[num].length;
182 	cd->trk[num].track = cd->trk[num - 1].track;
183 	cd->trk[num].data = cd->trk[num - 1].data;
184 	cd->trk[num].contd = 1;
185 	cd->trk[num].volume = cd->trk[num - 1].volume;
186 
187 	if (cd->trk[num - 1].section == 0)
188 		cd->trk[num - 1].section = 1;
189 	cd->trk[num].section = cd->trk[num - 1].section + 1;
190 
191 	cur_ntracks++;
192 	cur_nsections++;
193 
194 	for (i = num + 1; i < cur_ntracks; i++)
195 		if (cd->trk[i].track == cd->trk[num].track)
196 			cd->trk[i].section++;
197 
198 	return (1);
199 }
200 
201 /*
202  * remove_trackinfo()
203  *
204  * Remove a track's internal data.  This is similar to split_trackinfo()
205  * above, but simpler.  A track's initial section can't be removed.  Track
206  * numbers start at 0.
207  *
208  * Returns 1 on success, 0 on failure.
209  */
210 int
remove_trackinfo(int num)211 remove_trackinfo( int num )
212 {
213 	int	i, l;
214 
215 	if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
216 		return (0);
217 
218 	cd->trk[num - 1].length += cd->trk[num].length;
219 
220 	for (i = num; i < cur_ntracks - 1; i++)
221 		memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0]));
222 
223 	if (cur_track > num)
224 		cur_track--;
225 	if (cur_firsttrack > num)
226 		cur_firsttrack--;
227 	if (cur_lasttrack > num)
228 		cur_lasttrack--;
229 
230 	/* Update the user-defined playlists. */
231 	if (cd->lists != NULL)
232 		for (l = 0; cd->lists[l].name != NULL; l++)
233 			if (cd->lists[l].list != NULL)
234 				for (i = 0; cd->lists[l].list[i]; i++)
235 					if (cd->lists[l].list[i] > num)
236 						cd->lists[l].list[i]--;
237 
238 	/* Update the internal playlist. */
239 	if (playlist != NULL)
240 		for (i = 0; playlist[i].start; i++)
241 		{
242 			if (playlist[i].start > num)
243 				playlist[i].start--;
244 			if (playlist[i].end > num)
245 				playlist[i].end--;
246 		}
247 
248 	cur_ntracks--;
249 	cur_nsections--;
250 
251 	/*
252 	 * Update the section numbers for this track.  If this is the only
253 	 * user-created section in a track, get rid of the section number
254 	 * in the track's entry.
255 	 */
256 	if (num == cur_ntracks || cd->trk[num - 1].track != cd->trk[num].track)
257 	{
258 		if (cd->trk[num - 1].section == 1)
259 			cd->trk[num - 1].section = 0;
260 	}
261 	else
262 		for (i = num; i < cur_ntracks; i++)
263 			if (cd->trk[i].track == cd->trk[num - 1].track)
264 				cd->trk[i].section--;
265 
266 	return (1);
267 }
268 
269 /*
270  * listentry()
271  *
272  * Return a scrolling list entry.
273  */
274 char *
listentry(int num)275 listentry( int num )
276 {
277 	static char	buf[600];
278 	char		*name, tracknum[20];
279 	int		digits;
280 	int		sdigits;
281 
282 	if (num >= 0 && num < cur_ntracks)
283 	{
284 
285 /*
286 		if (big_spaces)
287 		{
288 			digits = 2;
289 			sdigits = cur_nsections < 9 ? -1 : -2;
290 		}
291 		else
292 		{
293 			digits = cd->trk[num].track < 10 ? 3 : 2;
294 			sdigits = cur_nsections < 9 ? -1 : -3;
295 		}
296 */
297 
298 		digits = 2;
299 		sdigits = cur_nsections < 9 ? -1 : -2;
300 
301 		name = cd->trk[num].songname ? cd->trk[num].songname : "";
302 
303 		if (cur_nsections)
304 	        {
305     			if (cd->trk[num].section > 9)
306 			{
307 				sprintf(tracknum, "%*d.%d", digits,
308 					cd->trk[num].track,
309 					cd->trk[num].section);
310 			} else {
311 				if (cd->trk[num].section)
312 				{
313 					sprintf(tracknum, "%*d.%*d", digits,
314 						cd->trk[num].track, sdigits,
315 						cd->trk[num].section);
316 				} else {
317 					sprintf(tracknum, "%*d%*s", digits,
318 						cd->trk[num].track,
319 						2 - sdigits, " ");
320 /*						2 - sdigits - big_spaces, " ");*/
321 				}
322 			}
323 		} else {
324 			sprintf(tracknum, "%*d", digits, cd->trk[num].track);
325 		}
326 
327 		if (cd->trk[num].data)
328 		{
329 			sprintf(buf, "%s) %3dMB %s", tracknum,
330 				cd->trk[num].length / 1024, name);
331 		} else {
332 			sprintf(buf, "%s) %02d:%02d %s", tracknum,
333 				cd->trk[num].length / 60,
334 				cd->trk[num].length % 60, name);
335                 }
336 
337 		return (buf);
338 	} else {
339 		return (NULL);
340         }
341 } /* listentry() */
342 
343 /*
344  * trackname()
345  *
346  * Return a track's name.
347  */
348 char *
trackname(int num)349 trackname( int num )
350 {
351 	if (num >= 0 && num < cur_ntracks)
352 	{
353 		if (cd->trk[num].songname)
354 		{
355 			return (cd->trk[num].songname);
356 		} else {
357 			return ("");
358 		}
359 	} else {
360 		return (NULL);
361 	}
362 } /* trackname() */
363 
364 /*
365  * tracklen()
366  *
367  * Return a track's length in seconds.
368  */
369 int
tracklen(int num)370 tracklen( int num )
371 {
372 	if (cd != NULL && num >= 0 && num < cur_ntracks)
373 		return (cd->trk[num].length);
374 	else
375 		return (0);
376 }
377 
378 /*
379  * get_default_volume()
380  *
381  * Return the default volume (0-32, 0=none) for the CD or a track.
382  */
383 int
get_default_volume(int track)384 get_default_volume( int track )
385 {
386 	if (! track)
387 		return (cd->volume);
388 	else if (track <= cur_ntracks)
389 		return (cd->trk[track - 1].volume);
390 	else
391 		return (0);
392 }
393 
394 /*
395  * get_contd()
396  *
397  * Return the contd value for a track.
398  */
399 int
get_contd(int num)400 get_contd( int num )
401 {
402 	if (num >= 0 && num < cur_ntracks)
403 		return (cd->trk[num].contd);
404 	else
405 		return (0);
406 }
407 
408 /*
409  * get_avoid()
410  *
411  * Return the avoid value for a track.
412  */
413 int
get_avoid(int num)414 get_avoid( int num )
415 {
416 	if (num >= 0 && num < cur_ntracks)
417 		return (cd->trk[num].avoid);
418 	else
419 		return (0);
420 }
421 
422 /*
423  * get_autoplay()
424  *
425  * Is autoplay set on this disc?
426  */
427 int
get_autoplay(void)428 get_autoplay( void )
429 {
430 	return ( cd->autoplay );
431 }
432 
433 /*
434  * get_playmode()
435  *
436  * Return the default playmode for the CD.
437  */
438 int
get_playmode(void)439 get_playmode( void )
440 {
441 	return ( cd->playmode );
442 }
443 
444 /*
445  * get_runtime()
446  *
447  * Return the total running time for the current playlist in seconds.
448  */
449 int
get_runtime(void)450 get_runtime( void )
451 {
452 	int	i;
453 
454 	if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1)
455 		return (cd == NULL ? 0 : cd->length);
456 
457 	for (i = 0; playlist[i].start; i++)
458 		;
459 
460 	return (playlist[i].starttime);
461 }
462 
463 /*
464  * default_volume()
465  *
466  * Set the default volume for the CD or a track.
467  */
468 void
default_volume(int track,int vol)469 default_volume( int track, int vol )
470 {
471 	if (track == 0)
472 		cd->volume = vol;
473 	else if (track <= cur_ntracks)
474 		cd->trk[track - 1].volume = vol;
475 }
476 
477 /*
478  * Play the next thing on the playlist, if any.
479  */
480 void
play_next_entry(int forward)481 play_next_entry( int forward )
482 {
483 	if (cd == NULL)
484 		return;
485 	if (playlist != NULL && playlist[cur_listno].start)
486 	{
487 		wm_cd_play(playlist[cur_listno].start, 0,
488 			playlist[cur_listno].end);
489 		cur_listno++;
490 	}
491 	else
492 		wm_cd_stop();
493 }
494 
495 /*
496  * Play the next track, following playlists as necessary.
497  */
498 void
play_next_track(int forward)499 play_next_track( int forward )
500 {
501 	if (cd == NULL)
502 		return;
503 
504 	/* Is the current playlist entry done?  Move on, if so. */
505 	if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end)
506 		play_next_entry( forward );
507 	else
508 		wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end);
509 }
510 
511 /*
512  * Play the previous track, hopping around the playlist as necessary.
513  */
514 void
play_prev_track(int forward)515 play_prev_track( int forward )
516 {
517 	if (cd == NULL)
518 		return;
519 
520 	if (playlist == NULL)
521 		return;
522 
523 	/* If we're in the middle of this playlist entry, go back one track */
524 	if (cur_track > playlist[cur_listno - 1].start)
525 		wm_cd_play(cur_track - 1, 0, playlist[cur_listno - 1].end);
526 	else
527 		if (cur_listno > 1)
528 		{
529 			cur_listno--;
530 			wm_cd_play(playlist[cur_listno - 1].end - 1, 0,
531 				playlist[cur_listno - 1].end);
532 		}
533 		else
534 			wm_cd_play(playlist[0].start, 0, playlist[0].end);
535 }
536 
537 /*
538  * stash_cdinfo(artist, cdname)
539  */
540 void
stash_cdinfo(char * artist,char * cdname,int autoplay,int playmode)541 stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode )
542 {
543 	if (cd != NULL)
544 	{
545 		if (strcmp(cd->artist, artist))
546 			info_modified = 1;
547 		strcpy(cd->artist, artist);
548 
549 		if (strcmp(cd->cdname, cdname))
550 			info_modified = 1;
551 		strcpy(cd->cdname, cdname);
552 
553 		if (!!cd->autoplay != !!autoplay)
554 			info_modified = 1;
555 		cd->autoplay = autoplay;
556 
557 		if (!!cd->playmode != !!playmode)
558 			info_modified = 1;
559 		cd->playmode = playmode;
560 	}
561 } /* stash_cdinfo() */
562 
563 /*
564  * wipe_cdinfo()
565  *
566  * Clear out all a CD's soft information (presumably in preparation for
567  * reloading from the database.)
568  */
569 void
wipe_cdinfo(void)570 wipe_cdinfo( void )
571 {
572 	struct wm_playlist	*l;
573 	int		i;
574 
575 	if (cd != NULL)
576 	{
577 		cd->artist[0] = cd->cdname[0] = '\0';
578 		cd->autoplay = cd->playmode = cd->volume = 0;
579 		cd->whichdb = NULL;
580 		freeup(&cd->otherrc);
581 		freeup(&cd->otherdb);
582 
583 		if (thiscd.lists != NULL)
584 		{
585 			for (l = thiscd.lists; l->name != NULL; l++)
586 			{
587 				free(l->name);
588 				free(l->list);
589 			}
590 			freeup( (char **)&thiscd.lists );
591 		}
592 
593 		for (i = 0; i < cur_ntracks; i++)
594 		{
595 			freeup(&cd->trk[i].songname);
596 			freeup(&cd->trk[i].otherrc);
597 			freeup(&cd->trk[i].otherdb);
598 			cd->trk[i].avoid = cd->trk[i].contd = 0;
599 			cd->trk[i].volume = 0;
600 			if (cd->trk[i].section > 1)
601 				remove_trackinfo(i--);
602 		}
603 	}
604 }
605 
606 /*
607  * stash_trkinfo(track, songname, contd, avoid)
608  *
609  * Update information about a track on the current CD.
610  */
611 void
stash_trkinfo(int track,char * songname,int contd,int avoid)612 stash_trkinfo( int track, char *songname, int contd, int avoid )
613 {
614 	if (cd != NULL)
615 	{
616 		track--;
617 		if (!!cd->trk[track].contd != !!contd)
618 			info_modified = 1;
619 		cd->trk[track].contd = track ? contd : 0;
620 
621 		if (!!cd->trk[track].avoid != !!avoid)
622 			info_modified = 1;
623 		cd->trk[track].avoid = avoid;
624 
625 		if ((cd->trk[track].songname == NULL && songname[0]) ||
626 				(cd->trk[track].songname != NULL &&
627 				strcmp(cd->trk[track].songname, songname)))
628 		{
629 			info_modified = 1;
630 			wm_strmcpy(&cd->trk[track].songname, songname);
631 		}
632 	}
633 }
634 
635 /*
636  * new_list()
637  *
638  * Add a playlist to a CD.
639  */
640 struct wm_playlist *
new_list(cd,listname)641 new_list(cd, listname)
642 	struct wm_cdinfo	*cd;
643 	char		*listname;
644 {
645 	int	nlists = 0;
646 	struct wm_playlist *l;
647 
648 	if (cd->lists != NULL)
649 	{
650 		for (nlists = 0; cd->lists[nlists].name != NULL; nlists++)
651 			;
652 		l = (struct wm_playlist *)realloc(cd->lists, (nlists + 2) *
653 			sizeof (struct wm_playlist));
654 	}
655 	else
656 		l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist));
657 
658 	if (l == NULL)
659 		return (NULL);
660 
661 	l[nlists + 1].name = NULL;
662 	l[nlists].name = NULL;		/* so wm_strmcpy doesn't free() it */
663 	wm_strmcpy(&l[nlists].name, listname);
664 	l[nlists].list = NULL;
665 	cd->lists = l;
666 
667 	return (&l[nlists]);
668 }
669 
670 /*
671  * make_playlist()
672  *
673  * Construct a playlist for the current CD.  If we're in shuffle mode, play
674  * each non-avoided track once, keeping continued tracks in the right order.
675  *
676  * If playmode is 2, use playlist number (playmode-2).  XXX should do
677  * bounds checking on this, probably.
678  *
679  * If consecutive tracks are being played, only make one playlist entry for
680  * them, so the CD player won't pause between tracks while we wake up.
681  */
682 void
make_playlist(int playmode,int starttrack)683 make_playlist( int playmode, int starttrack )
684 {
685 	int	i, avoiding = 1, entry = 0, count, track,
686 		*thislist;
687 
688 	cur_listno = 0;
689 	if (playlist != NULL)
690 		free(playlist);
691 	playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
692 	if (playlist == NULL)
693 	{
694 		perror("playlist");
695 		exit(1);
696 	}
697 
698 	/* If this is a data-only CD, we can't play it. */
699 	if ((starttrack && cd->trk[starttrack - 1].data) ||
700 		(cur_ntracks == 1 && cd->trk[0].data))
701 	{
702 		playlist[0].start = 0;
703 		playlist[0].end = 0;
704 		playlist[1].start = 0;
705 		return;
706 	}
707 
708 	if (playmode == 1)
709 	{
710 		char *done = malloc(cur_ntracks);
711 
712 		if (done == NULL)
713 		{
714 			perror("randomizer");
715 			exit(1);
716 		}
717 
718 		count = cur_ntracks;
719 		if (starttrack && cd->trk[starttrack - 1].avoid)
720 			count++;
721 		for (i = 0; i < cur_ntracks; i++)
722 			if (cd->trk[i].contd || cd->trk[i].avoid ||
723 				cd->trk[i].data)
724 			{
725 				done[i] = 1;
726 				count--;
727 			}
728 			else
729 				done[i] = 0;
730 
731 		for (i = 0; i < count; i++)
732 		{
733 			int end;	/* for readability */
734 			if (starttrack)
735 			{
736 				track = starttrack - 1;
737 				starttrack = 0;
738 			}
739 			else
740 				while (done[track = rand() % cur_ntracks])
741 					;
742 
743 			playlist[i].start = track + 1;
744 
745 			/* play all subsequent continuation tracks too */
746 			for (end = track + 1; end < cur_ntracks + 1; end++)
747 				if (! cd->trk[end].contd ||
748 						cd->trk[end].avoid ||
749 						cd->trk[end].data)
750 					break;
751 			playlist[i].end = end + 1;
752 
753 			done[track]++;
754 		}
755 		playlist[i].start = 0;
756 
757 		free(done);
758 	}
759 	else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name)
760 	{
761 		count = 2;	/* one terminating entry, and one for start */
762 		thislist = cd->lists[playmode - 2].list;
763 
764 		for (i = 0; thislist[i]; i++)
765 			if (thislist[i + 1] != thislist[i] + 1)
766 				count++;
767 
768 		if (playlist != NULL)
769 			free(playlist);
770 		playlist = malloc(sizeof (*playlist) * count);
771 		if (playlist == NULL)
772 		{
773 			perror("playlist");
774 			exit(1);
775 		}
776 
777 		count = 0;
778 		if (starttrack)
779 		{
780 			playlist[0].start = starttrack;
781 			for (track = 0; thislist[track]; track++)
782 				if (starttrack == thislist[track])
783 					break;
784 			if (! thislist[track])
785 			{
786 				playlist[0].end = starttrack + 1;
787 				playlist[1].start = thislist[0];
788 				count = 1;
789 				track = 0;
790 			}
791 		}
792 		else
793 		{
794 			playlist[0].start = thislist[0];
795 			track = 0;
796 		}
797 
798 		for (i = track; thislist[i]; i++)
799 			if (thislist[i + 1] != thislist[i] + 1)
800 			{
801 				playlist[count].end = thislist[i] + 1;
802 				count++;
803 				playlist[count].start = thislist[i + 1];
804 			}
805 	}
806 	else
807 	{
808 		for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++)
809 			if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data))
810 			{
811 				playlist[entry].start = i + 1;
812 				avoiding = 0;
813 			}
814 			else if (! avoiding && (cd->trk[i].avoid ||
815 						cd->trk[i].data))
816 			{
817 				playlist[entry].end = i + 1;
818 				avoiding = 1;
819 				entry++;
820 			}
821 		if (! avoiding)
822 			playlist[entry].end = i + 1;
823 		playlist[entry + !avoiding].start = 0;
824 	}
825 
826 	/*
827 	 * Now go through the list, whatever its source, and figure out
828 	 * cumulative starting times for each entry.
829 	 */
830 	entry = count = 0;
831 	do {
832 		playlist[entry].starttime = count;
833 
834 		if (playlist[entry].start)
835 			for (i = playlist[entry].start; i <
836 						playlist[entry].end; i++)
837 				count += cd->trk[i - 1].length;
838 	} while (playlist[entry++].start);
839 }
840 
841 /*
842  * Find a particular track's location in the current playlist.  Sets the
843  * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
844  */
845 void
pl_find_track(int track)846 pl_find_track( int track )
847 {
848 	int	i;
849 
850 	if (playlist == NULL)
851 	{
852 		fprintf(stderr, "Null playlist!  Huh?\n");
853 		return;
854 	}
855 
856 	for (i = 0; playlist[i].start; i++)
857 		if (track >= playlist[i].start && track < playlist[i].end)
858 		{
859 			cur_listno = i + 1;
860 			cur_firsttrack = playlist[i].start;
861 			cur_lasttrack = playlist[i].end - 1;
862 			return;
863 		}
864 
865 	/*
866 	 * Couldn't find the track in question.  Make a special entry with
867 	 * just that track.
868 	 */
869 	if (! playlist[i].start)
870 	{
871 		playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
872 		if (playlist == NULL)
873 		{
874 			perror("playlist realloc");
875 			exit(1);
876 		}
877 
878 		playlist[i + 1].start = playlist[i + 1].end = 0;
879 		playlist[i + 1].starttime = playlist[i].starttime +
880 			cd->trk[track - 1].length;
881 		playlist[i].start = track;
882 		playlist[i].end = track + 1;
883 		cur_listno = i + 1;
884 		cur_firsttrack = track;
885 		cur_lasttrack = track;
886 	}
887 }
888