From d35747a40c7dea12de95f78a4f283a45ef687597 Mon Sep 17 00:00:00 2001
From: Warren Dukes <warren.dukes@gmail.com>
Date: Mon, 23 Feb 2004 23:41:20 +0000
Subject: import from SF CVS

git-svn-id: https://svn.musicpd.org/mpd/trunk@1 09075e82-0dd4-0310-85a5-a0d7c8717e4f
---
 src/playlist.c | 1236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1236 insertions(+)
 create mode 100644 src/playlist.c

(limited to 'src/playlist.c')

diff --git a/src/playlist.c b/src/playlist.c
new file mode 100644
index 000000000..eec721cfb
--- /dev/null
+++ b/src/playlist.c
@@ -0,0 +1,1236 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "playlist.h"
+#include "player.h"
+#include "command.h"
+#include "ls.h"
+#include "tag.h"
+#include "conf.h"
+#include "directory.h"
+#include "stats.h"
+#include "log.h"
+#include "path.h"
+#include "utils.h"
+#include "sig_handlers.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+
+#define BITS_FOR_VERSION	31
+
+#define PLAYLIST_COMMENT	'#'
+
+#define PLAYLIST_STATE_STOP		0
+#define PLAYLIST_STATE_PLAY		1
+
+#define PLAYLIST_PREV_UNLESS_ELAPSED    10
+
+#define PLAYLIST_STATE_FILE_STATE		"state: "
+#define PLAYLIST_STATE_FILE_RANDOM		"random: "
+#define PLAYLIST_STATE_FILE_REPEAT		"repeat: "
+#define PLAYLIST_STATE_FILE_CURRENT		"current: "
+#define PLAYLIST_STATE_FILE_TIME		"time: "
+#define PLAYLIST_STATE_FILE_CROSSFADE		"crossfade: "
+#define PLAYLIST_STATE_FILE_PLAYLIST_BEGIN	"playlist_begin"
+#define PLAYLIST_STATE_FILE_PLAYLIST_END	"playlist_end"
+
+#define PLAYLIST_STATE_FILE_STATE_PLAY		"play"
+#define PLAYLIST_STATE_FILE_STATE_PAUSE		"pause"
+#define PLAYLIST_STATE_FILE_STATE_STOP		"stop"
+
+#define PLAYLIST_BUFFER_SIZE	2*MAXPATHLEN
+
+typedef struct _Playlist {
+	Song ** songs;
+	int * order;
+	int length;
+	int current;
+	int queued;
+	int repeat;
+	int random;
+	unsigned long version;
+} Playlist;
+
+Playlist playlist;
+int playlist_state = PLAYLIST_STATE_STOP;
+int playlist_max_length;
+int playlist_stopOnError;
+int playlist_errorCount = 0;
+int playlist_queueError;
+int playlist_noGoToNext = 0;
+
+int playlist_saveAbsolutePaths;
+
+char * playlist_stateFile = NULL;
+
+void swapOrder(int a, int b);
+int playPlaylistOrderNumber(FILE * fp, int orderNum);
+void randomizeOrder(int start, int end);
+
+void incrPlaylistVersion() {
+	static unsigned long max = ((unsigned long)1<<BITS_FOR_VERSION)-1;
+	playlist.version++;
+	if(playlist.version>=max) playlist.version = 0;
+}
+
+void initPlaylist() {
+	char * test;
+
+	playlist.length = 0;
+	playlist.repeat = 0;
+	playlist.version = 0;
+	playlist.random = 0;
+	playlist.queued = -1;
+
+	blockTermSignal();
+
+	playlist_max_length = strtol((getConf())[CONF_MAX_PLAYLIST_LENGTH],&test,10);
+	if(*test!='\0') {
+		ERROR("max playlist length \"%s\" is not an integer\n",
+				(getConf())[CONF_MAX_PLAYLIST_LENGTH]);
+		exit(-1);
+	}
+
+	if(strcmp("yes",(getConf())[CONF_SAVE_ABSOLUTE_PATHS_IN_PLAYLISTS])
+			==0) {
+		playlist_saveAbsolutePaths = 1;
+	}
+	else if(strcmp("no",(getConf())[CONF_SAVE_ABSOLUTE_PATHS_IN_PLAYLISTS])
+			==0) {
+		playlist_saveAbsolutePaths = 0;
+	}
+	else {
+		ERROR("save_absolute_paths_in_playlist \"%s\" is not yes or "
+			"no\n",
+			(getConf())[CONF_SAVE_ABSOLUTE_PATHS_IN_PLAYLISTS]);
+		exit(-1);
+	}
+
+	playlist.songs = malloc(sizeof(Song *)*playlist_max_length);
+	playlist.order = malloc(sizeof(Song *)*playlist_max_length);
+
+	memset(playlist.songs,(int)NULL,sizeof(char *)*playlist_max_length);
+
+	srand(time(NULL));
+
+	if(getConf()[CONF_STATE_FILE]) {
+		playlist_stateFile = getConf()[CONF_STATE_FILE];
+	}
+
+	unblockTermSignal();
+}
+
+void finishPlaylist() {
+	stopPlaylist(stderr);
+	clearPlaylist(stderr);
+	free(playlist.songs);
+	free(playlist.order);
+}
+
+int clearPlaylist(FILE * fp) {
+	int i;
+
+	if(stopPlaylist(fp)<0) return -1;
+
+	blockTermSignal();
+	for(i=0;i<playlist.length;i++) playlist.songs[i] = NULL;
+	playlist.length = 0;
+	unblockTermSignal();
+
+	incrPlaylistVersion();
+
+	return 0;
+}
+
+int showPlaylist(FILE * fp) {
+	int i;
+
+	for(i=0;i<playlist.length;i++) {
+		myfprintf(fp,"%i:%s\n",i,(playlist.songs[i])->utf8file);
+	}
+
+	return 0;
+}
+
+void savePlaylistState() {
+	if(playlist_stateFile) {
+		FILE * fp;
+
+		blockTermSignal();
+		while(!(fp = fopen(playlist_stateFile,"w")) && errno==EINTR);
+		if(!fp) {
+			ERROR("problems opening state file \"%s\" for "
+				"writing\n",playlist_stateFile);
+			return;
+		}
+
+		myfprintf(fp,"%s",PLAYLIST_STATE_FILE_STATE);
+		switch(playlist_state) {
+		case PLAYLIST_STATE_PLAY:
+			switch(getPlayerState()) {
+			case PLAYER_STATE_PAUSE:
+				myfprintf(fp,"%s\n",
+					PLAYLIST_STATE_FILE_STATE_PAUSE);
+				break;
+			default:
+				myfprintf(fp,"%s\n",
+					PLAYLIST_STATE_FILE_STATE_PLAY);
+			}
+			myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_CURRENT,
+				playlist.order[playlist.current]);
+			myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_TIME,
+				getPlayerElapsedTime());
+			break;
+		default:
+			myfprintf(fp,"%s\n",PLAYLIST_STATE_FILE_STATE_STOP);
+			break;
+		}
+		myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_RANDOM,
+				playlist.random);
+		myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_REPEAT,
+				playlist.repeat);
+		myfprintf(fp,"%s%i\n",PLAYLIST_STATE_FILE_CROSSFADE,
+				(int)(getPlayerCrossFade()));
+		myfprintf(fp,"%s\n",PLAYLIST_STATE_FILE_PLAYLIST_BEGIN);
+		showPlaylist(fp);
+		myfprintf(fp,"%s\n",PLAYLIST_STATE_FILE_PLAYLIST_END);
+
+		while(fclose(fp) && errno==EINTR);
+		unblockTermSignal();
+	}
+}
+
+void loadPlaylistFromStateFile(FILE * fp, char * buffer, int state, int current,
+		int time) 
+{
+	char * temp;
+	int song;
+
+	if(!myFgets(buffer,PLAYLIST_BUFFER_SIZE,fp)) {
+		ERROR("error parsing state file \"%s\"\n",playlist_stateFile);
+		exit(-1);
+	}
+	while(strcmp(buffer,PLAYLIST_STATE_FILE_PLAYLIST_END)) {
+		song = atoi(strtok(buffer,":"));
+		if(!(temp = strtok(NULL,""))) {
+			ERROR("error parsing state file \"%s\"\n",
+					playlist_stateFile);
+			exit(-1);
+		}
+		if(addToPlaylist(stderr,temp)==0 && current==song) {
+			if(state!=PLAYER_STATE_STOP) {
+				playPlaylist(stderr,playlist.length-1,0);
+			}
+			if(state==PLAYER_STATE_PAUSE) {
+				playerPause(stderr);
+				seekSongInPlaylist(stderr,playlist.length-1,
+						time);
+			}
+		}
+		if(!myFgets(buffer,PLAYLIST_BUFFER_SIZE,fp)) {
+			ERROR("error parsing state file \"%s\"\n",
+					playlist_stateFile);
+			exit(-1);
+		}
+	}
+}
+
+void readPlaylistState() {
+	if(playlist_stateFile) {
+		FILE * fp;
+		struct stat st;
+		int current = -1;
+		int time = 0;
+		int state = PLAYER_STATE_STOP;
+		char buffer[PLAYLIST_BUFFER_SIZE];
+
+		if(stat(playlist_stateFile,&st)<0) return;
+		if(!S_ISREG(st.st_mode)) {
+			ERROR("state file \"%s\" is not a regular "
+				"file\n",playlist_stateFile);
+			exit(-1);
+		}
+
+		fp = fopen(playlist_stateFile,"r");
+		if(!fp) {
+			ERROR("problems opening state file \"%s\" for "
+				"reading\n",playlist_stateFile);
+			exit(-1);
+		}
+
+		while(myFgets(buffer,PLAYLIST_BUFFER_SIZE,fp)) {
+			if(strncmp(buffer,PLAYLIST_STATE_FILE_STATE,
+				strlen(PLAYLIST_STATE_FILE_STATE))==0) {
+				if(strcmp(&(buffer
+					[strlen(PLAYLIST_STATE_FILE_STATE)]),
+					PLAYLIST_STATE_FILE_STATE_PLAY)==0) {
+					state = PLAYER_STATE_PLAY;
+				}
+				else if(strcmp(&(buffer
+					[strlen(PLAYLIST_STATE_FILE_STATE)]),
+					PLAYLIST_STATE_FILE_STATE_PAUSE)==0) {
+					state = PLAYER_STATE_PAUSE;
+				}
+			}
+			else if(strncmp(buffer,PLAYLIST_STATE_FILE_TIME,
+				strlen(PLAYLIST_STATE_FILE_TIME))==0) {
+				time = atoi(&(buffer
+					[strlen(PLAYLIST_STATE_FILE_TIME)]));
+			}
+			else if(strncmp(buffer,PLAYLIST_STATE_FILE_REPEAT,
+				strlen(PLAYLIST_STATE_FILE_REPEAT))==0) {
+				if(strcmp(&(buffer
+					[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
+					"1")==0) {
+					setPlaylistRepeatStatus(stderr,1);
+				}
+				else setPlaylistRepeatStatus(stderr,0);
+			}
+			else if(strncmp(buffer,PLAYLIST_STATE_FILE_CROSSFADE,
+				strlen(PLAYLIST_STATE_FILE_CROSSFADE))==0) {
+				setPlayerCrossFade(atoi(&(buffer[strlen(
+                                       	PLAYLIST_STATE_FILE_CROSSFADE)])));
+			}
+			else if(strncmp(buffer,PLAYLIST_STATE_FILE_RANDOM,
+				strlen(PLAYLIST_STATE_FILE_RANDOM))==0) {
+				if(strcmp(&(buffer
+					[strlen(PLAYLIST_STATE_FILE_RANDOM)]),
+					"1")==0) {
+					setPlaylistRandomStatus(stderr,1);
+				}
+				else setPlaylistRandomStatus(stderr,0);
+			}
+			else if(strncmp(buffer,PLAYLIST_STATE_FILE_CURRENT,
+				strlen(PLAYLIST_STATE_FILE_CURRENT))==0) {
+				if(strlen(buffer)==
+					strlen(PLAYLIST_STATE_FILE_CURRENT)) {
+					ERROR("error parsing state "
+						"file \"%s\"\n",
+						playlist_stateFile);
+					exit(-1);
+				}
+				current = atoi(&(buffer
+					[strlen(PLAYLIST_STATE_FILE_CURRENT)]));
+			}
+			else if(strncmp(buffer,
+				PLAYLIST_STATE_FILE_PLAYLIST_BEGIN,
+				strlen(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)
+				)==0) {
+				if(state==PLAYER_STATE_STOP) current = -1;
+				loadPlaylistFromStateFile(fp,buffer,state,
+						current,time);
+			}
+		}
+
+		fclose(fp);
+	}
+}
+
+int playlistInfo(FILE * fp,int song) {
+	MpdTag * tag;
+	int i;
+	int begin = 0;
+	int end = playlist.length;
+
+	if(song>=0) {
+		begin = song;
+		end = song+1;
+	}
+	if(song>=playlist.length) {
+		myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+
+	for(i=begin;i<end;i++) {
+		myfprintf(fp,"file: %s\n",(playlist.songs[i])->utf8file);
+		if((tag = (playlist.songs[i])->tag)) {
+			printMpdTag(fp,tag);
+		}
+	} 
+
+	return 0;
+}
+
+void swapSongs(int song1, int song2) {
+	Song * temp;
+	
+	temp = playlist.songs[song1];
+	playlist.songs[song1] = playlist.songs[song2];
+	playlist.songs[song2] = temp;
+}
+
+void queueNextSongInPlaylist() {
+	if(playlist.current<playlist.length-1) {
+		playlist.queued = playlist.current+1;
+		DEBUG("playlist: queue song %i:\"%s\"\n",
+				playlist.queued,
+				playlist.songs[playlist.order[
+				playlist.queued]]->utf8file);
+		if(queueSong(playlist.songs[playlist.order[
+				playlist.queued]]->utf8file)<0) {
+			playlist.queued = -1;
+			playlist_queueError = 1;
+		}
+	}
+	else if(playlist.length && playlist.repeat) {
+		if(playlist.length>1 && playlist.random) {
+			randomizeOrder(0,playlist.length);
+		}
+		playlist.queued = 0;
+		DEBUG("playlist: queue song %i:\"%s\"\n",
+				playlist.queued,
+				playlist.songs[playlist.order[
+				playlist.queued]]->utf8file);
+		if(queueSong(playlist.songs[playlist.order[
+				playlist.queued]]->utf8file)<0) {
+			playlist.queued = -1;
+			playlist_queueError = 1;
+		}
+	}
+}
+
+void syncPlaylistWithQueue(int queue) {
+	if(queue && getPlayerQueueState()==PLAYER_QUEUE_BLANK) {
+		queueNextSongInPlaylist();
+	}
+	else if(getPlayerQueueState()==PLAYER_QUEUE_DECODE) {
+		if(playlist.queued!=-1) setQueueState(PLAYER_QUEUE_PLAY);
+		else setQueueState(PLAYER_QUEUE_STOP);
+	}
+	else if(getPlayerQueueState()==PLAYER_QUEUE_EMPTY) {
+		setQueueState(PLAYER_QUEUE_BLANK);
+		if(playlist.queued>=0) {
+			DEBUG("playlist: now playing queued song\n");
+			playlist.current = playlist.queued;
+		}
+		playlist.queued = -1;
+		if(queue) queueNextSongInPlaylist();
+	}
+}
+
+void lockPlaylistInteraction() {
+	if(getPlayerQueueState()==PLAYER_QUEUE_PLAY || 
+		getPlayerQueueState()==PLAYER_QUEUE_FULL) {
+		playerQueueLock();
+		syncPlaylistWithQueue(0);
+	}
+}
+
+void unlockPlaylistInteraction() {
+	playerQueueUnlock();
+}
+
+void clearPlayerQueue() {
+	playlist.queued = -1;
+	switch(getPlayerQueueState()) {
+	case PLAYER_QUEUE_FULL:
+		DEBUG("playlist: dequeue song\n");
+		setQueueState(PLAYER_QUEUE_BLANK);
+		break;
+	case PLAYER_QUEUE_PLAY:
+		DEBUG("playlist: stop decoding queued song\n");
+		setQueueState(PLAYER_QUEUE_STOP);
+		break;
+	}
+}
+
+int addToPlaylist(FILE * fp, char * file) {
+	Song * song;
+
+	DEBUG("add to playlist: %s\n",file);
+	
+	if(!(song = getSong(file))) {
+		myfprintf(fp,"%s \"%s\" is not in the music db\n",COMMAND_RESPOND_ERROR,file);
+		return -1;
+	}
+
+	return addSongToPlaylist(fp,song);
+}
+
+int addSongToPlaylist(FILE * fp, Song * song) {
+	if(playlist.length==playlist_max_length) {
+		myfprintf(fp,"%s playlist is at the max size\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+
+	if(playlist_state==PLAYLIST_STATE_PLAY) {
+		if(playlist.queued>=0 && playlist.current==playlist.length-1) {
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+		}
+	}
+
+	playlist.songs[playlist.length] = song;
+	playlist.order[playlist.length] = playlist.length;
+	playlist.length++;
+
+	if(playlist.random) {
+		int swap;
+		int start;
+		if(playlist_state==PLAYLIST_STATE_STOP) start = 0;
+		else if(playlist.queued>0) start = playlist.queued+1;
+		else start = playlist.current+1;
+		swap = rand()%(playlist.length-start);
+		swap+=start;
+		swapOrder(playlist.length-1,swap);
+	}
+	
+	incrPlaylistVersion();
+
+	return 0;
+}
+
+int swapSongsInPlaylist(FILE * fp, int song1, int song2) {
+	int queuedSong = -1;
+	int currentSong = -1;
+
+	if(song1<0 || song1>=playlist.length) {
+		fprintf(fp,"%s \"%i\" is not in the playlist\n",
+				COMMAND_RESPOND_ERROR,song1);
+		return -1;
+	}
+	if(song2<0 || song2>=playlist.length) {
+		fprintf(fp,"%s \"%i\" is not in the playlist\n",
+				COMMAND_RESPOND_ERROR,song2);
+		return -1;
+	}
+
+	blockTermSignal();
+	
+	if(playlist_state==PLAYLIST_STATE_PLAY) {
+		if(playlist.queued>=0) {
+			queuedSong = playlist.order[playlist.queued];
+		}
+		currentSong = playlist.order[playlist.current];
+
+		if(queuedSong==song1 || queuedSong==song2 || currentSong==song1 
+				|| currentSong==song2)	
+		{
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+		}
+	}
+
+	swapSongs(song1,song2);
+	if(playlist.random) {
+		int i;
+		int k;
+		int j = -1;
+		for(i=0;playlist.order[i]!=song1;i++) {
+			if(playlist.order[i]==song2) j = i;
+		}
+		k = i;
+		for(;j==-1;i++) if(playlist.order[i]==song2) j = i;
+		swapOrder(k,j);
+	}
+	else {
+		if(playlist.current==song1) playlist.current = song2;
+		else if(playlist.current==song2) playlist.current = song1;
+	}
+
+	unblockTermSignal();
+
+	incrPlaylistVersion();
+
+	return 0;
+}
+
+int deleteFromPlaylist(FILE * fp, int song) {
+	int i;
+	int songOrder;
+
+	if(song<0) {
+		myfprintf(fp,"%s need a positive integer\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+	if(song>=playlist.length) {
+		myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+
+	blockTermSignal();
+	if(playlist_state==PLAYLIST_STATE_PLAY) {
+		if(playlist.queued>=0 && (playlist.order[playlist.queued]==song
+			|| playlist.order[playlist.current]==song)) 
+		{
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();	
+		}
+	}
+
+	/* delete song from songs array */
+	for(i=song;i<playlist.length-1;i++) {
+		playlist.songs[i] = playlist.songs[i+1];
+	}
+	/* now find it in the order array */
+	for(i=0;i<playlist.length-1;i++) {
+		if(playlist.order[i]==song) break;
+	}
+	songOrder = i;
+	/* delete the entry from the order array */
+	for(;i<playlist.length-1;i++) playlist.order[i] = playlist.order[i+1];
+	/* readjust values in the order array */
+	for(i=0;i<playlist.length-1;i++) {
+		if(playlist.order[i]>song) playlist.order[i]--;
+	}
+	/* now take care of other misc stuff */
+	playlist.songs[playlist.length-1] = NULL;
+	playlist.length--;
+
+	unblockTermSignal();
+
+	incrPlaylistVersion();
+
+	if(playlist_state!=PLAYLIST_STATE_STOP && playlist.current==songOrder) {
+		/*if(playlist.current>=playlist.length) return playerStop(fp);
+		else return playPlaylistOrderNumber(fp,playlist.current);*/
+		playerStop(stderr);
+		playlist_noGoToNext = 1;
+	}
+	else if(playlist_state!=PLAYLIST_STATE_STOP && 
+			playlist.current>songOrder) {
+		playlist.current--;
+	}
+
+	if(playlist_state!=PLAYLIST_STATE_STOP && playlist.queued>songOrder) {
+		playlist.queued--;
+	}
+
+	return 0;
+}
+
+void deleteASongFromPlaylist(Song * song) {
+	int i;
+	
+	for(i=0;i<playlist.length;i++) {
+		if(song==playlist.songs[i]) {
+			deleteFromPlaylist(stderr,i);
+		}
+	}
+}
+
+void deleteSongsFromPlaylist(SongList * songList) {
+	 ListNode * node = songList->firstNode;
+         Song * song;
+
+         while(node) {
+                 song = (Song *)node->data;
+                 deleteASongFromPlaylist(song);
+                 node = node->nextNode;
+         }
+}
+
+int stopPlaylist(FILE * fp) {
+	DEBUG("playlist: stop\n");
+	if(playerStop(fp)<0) return -1;
+	playerCloseAudio();
+	playlist.queued = -1;
+	playlist_state = PLAYLIST_STATE_STOP;
+	playlist_noGoToNext = 0;
+	/*stats.playTime+=getPlayerElapsedTime();*/
+	if(playlist.random) randomizeOrder(0,playlist.length-1);
+	return 0;
+}
+
+int playPlaylistOrderNumber(FILE * fp, int orderNum) {
+	/*stats.songsPlayed++;*/
+
+	if(playerStop(fp)<0) return -1;
+
+	playlist_state = PLAYLIST_STATE_PLAY;
+	playlist_noGoToNext = 0;
+	playlist.queued = -1;
+	playlist_queueError = 0;
+	playlist.current = orderNum;
+
+	DEBUG("playlist: play %i:\"%s\"\n",orderNum,
+			(playlist.songs[playlist.order[orderNum]])->utf8file);
+
+	if(playerPlay(fp,(playlist.songs[playlist.order[orderNum]])->
+			utf8file)<0) 
+	{
+		stopPlaylist(fp);
+		return -1;
+	}
+
+	return 0;
+}
+
+int playPlaylist(FILE * fp, int song, int stopOnError) {
+	int i = song;
+
+	clearPlayerError();
+
+	if(song==-1) i = 0;
+	else if(song<0) {
+		myfprintf(fp,"%s need integer >= -1\n",COMMAND_RESPOND_ERROR);
+		playlist_state = PLAYLIST_STATE_STOP;
+		return -1;
+	}
+	if(!playlist.length) {
+		myfprintf(fp,"%s playlist is empty\n",COMMAND_RESPOND_ERROR);
+		playlist_state = PLAYLIST_STATE_STOP;
+		return -1;
+	}
+	else if(song>=playlist.length) {
+		myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR);
+		playlist_state = PLAYLIST_STATE_STOP;
+		return -1;
+	}
+
+	if(playlist.random) {
+		/*if(song == -1 && playlist_state==PLAYLIST_STATE_PLAY) {
+			randomizeOrder(0,playlist.length-1);
+		}
+		else {*/
+			if(song>=0) for(i=0;song!=playlist.order[i];i++);
+			if(playlist_state==PLAYLIST_STATE_STOP) {
+				playlist.current = 0;
+			}
+			swapOrder(i,playlist.current);
+			i = playlist.current;
+		/*}*/
+	}
+
+	playlist_stopOnError = stopOnError;
+	playlist_errorCount = 0;
+
+	return playPlaylistOrderNumber(fp,i);
+}
+
+void syncPlayerAndPlaylist() {
+	if(playlist_state!=PLAYLIST_STATE_PLAY) return;
+
+	if(getPlayerState()==PLAYER_STATE_STOP) playPlaylistIfPlayerStopped();
+	else syncPlaylistWithQueue(!playlist_queueError);
+}
+
+int currentSongInPlaylist(FILE * fp) {
+	if(playlist_state!=PLAYLIST_STATE_PLAY) return 0;
+
+	playlist_stopOnError = 0;
+
+	syncPlaylistWithQueue(0);
+
+	if(playlist.current<playlist.length) {
+		return playPlaylistOrderNumber(fp,playlist.current);
+	}
+	else return stopPlaylist(fp);;
+
+	return 0;
+}
+
+int nextSongInPlaylist(FILE * fp) {
+	if(playlist_state!=PLAYLIST_STATE_PLAY) return 0;
+
+	syncPlaylistWithQueue(0);
+	
+	playlist_stopOnError = 0;
+
+	if(playlist.current<playlist.length-1) {
+		/*stats.playTime+=getPlayerElapsedTime();*/
+		return playPlaylistOrderNumber(fp,playlist.current+1);
+	}
+	else if(playlist.length && playlist.repeat) {
+		/*stats.playTime+=getPlayerElapsedTime();*/
+		if(playlist.random) randomizeOrder(0,playlist.length-1);
+		return playPlaylistOrderNumber(fp,0);
+	}
+	else {
+		return stopPlaylist(fp);;
+	}
+
+	return 0;
+}
+
+void playPlaylistIfPlayerStopped() {
+	if(getPlayerState()==PLAYER_STATE_STOP) {
+		int error = getPlayerError();
+
+		if(error==PLAYER_ERROR_NOERROR) playlist_errorCount = 0;
+		else playlist_errorCount++;
+
+		if(playlist_state==PLAYLIST_STATE_PLAY && (
+				(playlist_stopOnError && 
+				error!=PLAYER_ERROR_NOERROR) ||
+				error==PLAYER_ERROR_AUDIO ||
+				error==PLAYER_ERROR_SYSTEM ||
+				playlist_errorCount>=playlist.length)) {
+			stopPlaylist(stderr);
+		}
+		else if(playlist_noGoToNext) currentSongInPlaylist(stderr);
+		else nextSongInPlaylist(stderr);
+	}
+}
+
+int getPlaylistRepeatStatus() {
+	return playlist.repeat;
+}
+
+int getPlaylistRandomStatus() {
+	return playlist.random;
+}
+
+int setPlaylistRepeatStatus(FILE * fp, int status) {
+	if(status!=0 && status!=1) {
+		myfprintf(fp,"%s \"%i\" is not 0 or 1\n",COMMAND_RESPOND_ERROR,status);
+		return -1;
+	}
+
+	if(playlist_state==PLAYLIST_STATE_PLAY) {
+		if(playlist.repeat && !status && playlist.queued==0) {
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+		}
+	}
+
+	playlist.repeat = status;
+
+	return 0;
+}
+
+int moveSongInPlaylist(FILE * fp, int from, int to) {
+	int i;
+	Song * tmpSong;
+	int queuedSong = -1;
+	int currentSong = -1;
+
+	if(from<0 || from>=playlist.length) {
+		fprintf(fp,"%s \"%i\" is not a song in the playlist\n",
+				COMMAND_RESPOND_ERROR,from);
+		return -1;
+	}
+
+	if(to<0 || to>=playlist.length) {
+		fprintf(fp,"%s \"%i\" is not a song in the playlist\n",
+				COMMAND_RESPOND_ERROR,to);
+		return -1;
+	}
+
+	blockTermSignal();
+	
+	if(playlist_state==PLAYLIST_STATE_PLAY) {
+		if(playlist.queued>=0) {
+			queuedSong = playlist.order[playlist.queued];
+		}
+		currentSong = playlist.order[playlist.current];
+		if(queuedSong==from || queuedSong==to || currentSong==from ||
+				currentSong==to)
+		{
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+		}
+	}
+
+	tmpSong = playlist.songs[from];
+	/* move songs to one less in from->to */
+	for(i=from;i<to;i++) playlist.songs[i] = playlist.songs[i+1];
+	/* move songs to one more in to->from */
+	for(i=from;i>to;i--) playlist.songs[i] = playlist.songs[i-1];
+	/* put song at _to_ */
+	playlist.songs[to] = tmpSong;
+	/* now deal with order */
+	if(playlist.random) {
+		for(i=0;i<playlist.length;i++) {
+			if(playlist.order[i]>from && playlist.order[i]<=to) {
+				playlist.order[i]--;
+			}
+			else if(playlist.order[i]<from && 
+					playlist.order[i]>=to) {
+				playlist.order[i]++;
+			}
+			else if(from==playlist.order[i]) {
+				playlist.order[i] = to;
+			}
+		}
+	}
+	else if(playlist.current==from) playlist.current = to;
+	else if(playlist.current>from && playlist.current<=to) {
+		playlist.current--;
+	}
+	else if(playlist.current>=to && playlist.current<from) {
+		playlist.current++;
+	}
+
+	unblockTermSignal();
+
+	incrPlaylistVersion();
+
+	return 0;
+}
+
+void orderPlaylist() {
+	int i;
+
+	playlist.current = playlist.order[playlist.current];
+
+	blockTermSignal();
+	if(playlist_state==PLAYLIST_STATE_PLAY) {
+		if(playlist.queued>=0) {
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+		}
+	}
+
+	for(i=0;i<playlist.length;i++) {
+		playlist.order[i] = i;
+	}
+
+	unblockTermSignal();
+}
+
+void swapOrder(int a, int b) {
+	int bak = playlist.order[a];
+	playlist.order[a] = playlist.order[b];
+	playlist.order[b] = bak;
+}
+
+void randomizeOrder(int start,int end) {
+	int i;
+	int ri;
+
+	DEBUG("playlist: randomize from %i to %i\n",start,end);
+
+	blockTermSignal();
+
+	if(playlist_state==PLAYLIST_STATE_PLAY) {
+		if(playlist.queued>=start && playlist.queued<=end) {
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+		}
+	}
+
+	for(i=start;i<=end;i++) {
+		ri = rand()%(end-start+1)+start;
+		if(ri==playlist.current) playlist.current = i;
+		else if(i==playlist.current) playlist.current = ri;
+		swapOrder(i,ri);
+	}
+
+	unblockTermSignal();
+}
+
+int setPlaylistRandomStatus(FILE * fp, int status) {
+	int statusWas = playlist.random;
+
+	if(status!=0 && status!=1) {
+		myfprintf(fp,"%s \"%i\" is not 0 or 1\n",COMMAND_RESPOND_ERROR,status);
+		return -1;
+	}
+
+	playlist.random = status;
+
+	if(status!=statusWas) {
+		if(playlist.random) {
+			if(playlist_state==PLAYLIST_STATE_PLAY) {
+				randomizeOrder(playlist.current+1,
+						playlist.length-1);
+			}
+			else randomizeOrder(0,playlist.length-1);
+		}
+		else orderPlaylist();
+	}
+
+	return 0;
+}
+
+int previousSongInPlaylist(FILE * fp) {
+	if(playlist_state!=PLAYLIST_STATE_PLAY) return 0;
+
+	syncPlaylistWithQueue(0);
+
+   	if (getPlayerElapsedTime()>PLAYLIST_PREV_UNLESS_ELAPSED) {
+		return playPlaylistOrderNumber(fp,playlist.current);
+   	}
+   	else {
+      		if(playlist.current>0) {
+			return playPlaylistOrderNumber(fp,playlist.current-1);
+      		}
+		else if(playlist.repeat) {
+			return playPlaylistOrderNumber(fp,playlist.length-1);
+		}
+      		else {
+         		return playPlaylistOrderNumber(fp,playlist.current);
+      		}
+	}
+
+	return 0;
+}
+
+int shufflePlaylist(FILE * fp) {
+	int i;
+	int ri;
+
+	if(playlist.length>1) {
+		blockTermSignal();
+		if(playlist_state==PLAYLIST_STATE_PLAY) {
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+			/* put current playing song first */
+			swapSongs(0,playlist.order[playlist.current]);
+			if(playlist.random) {
+				int j;
+				for(j=0;0!=playlist.order[j];j++);
+				playlist.current = j;
+			}
+			else playlist.current = 0;
+			i = 1;
+		}
+		else i = 0;
+		/* shuffle the rest of the list */
+		for(;i<playlist.length;i++) {
+			ri = rand()%(playlist.length-1)+1;
+			swapSongs(i,ri);
+		}
+		unblockTermSignal();
+
+		incrPlaylistVersion();
+	}
+
+	return 0;
+}
+
+int deletePlaylist(FILE * fp, char * utf8file) {
+	char * file = utf8ToFsCharset(utf8file);
+	char * rfile = malloc(strlen(file)+strlen(".")+
+			strlen(PLAYLIST_FILE_SUFFIX)+1);
+	char * actualFile;
+
+	strcpy(rfile,file);
+	strcat(rfile,".");
+	strcat(rfile,PLAYLIST_FILE_SUFFIX);
+
+	if((actualFile = rpp2app(rfile)) && isPlaylist(actualFile)) free(rfile);
+	else {
+		free(rfile);
+		myfprintf(fp,"%s playlist \"%s\" not found\n",
+				COMMAND_RESPOND_ERROR,utf8file);
+		return -1;
+	}
+
+	if(unlink(actualFile)<0) {
+		myfprintf(fp,"%s problems deleting file\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+
+	return 0;
+}
+
+int savePlaylist(FILE * fp, char * utf8file) {
+	FILE * fileP;
+	int i;
+	struct stat st;
+	char * file;
+	char * rfile;
+	char * actualFile;
+
+	if(strstr(utf8file,"/")) {
+		myfprintf(fp,"%s cannot save \"%s\", saving playlists to "
+				"subdirectories is not supported\n",
+				COMMAND_RESPOND_ERROR,utf8file);
+		return -1;
+	}
+
+	file = strdup(utf8ToFsCharset(utf8file));
+
+	rfile = malloc(strlen(file)+strlen(".")+
+			strlen(PLAYLIST_FILE_SUFFIX)+1);
+
+	strcpy(rfile,file);
+	strcat(rfile,".");
+	strcat(rfile,PLAYLIST_FILE_SUFFIX);
+
+	free(file);
+
+	actualFile = rpp2app(rfile);
+
+	free(rfile);
+
+	if(0==stat(actualFile,&st)) {
+		myfprintf(fp,"%s A file or directory already exists with the name \"%s\"\n",COMMAND_RESPOND_ERROR,utf8file);
+		return -1;
+	}
+
+
+	while(!(fileP = fopen(actualFile,"w")) && errno==EINTR);
+	if(fileP==NULL) {
+		myfprintf(fp,"%s Problems opening file\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+
+	for(i=0;i<playlist.length;i++) {
+		if(playlist_saveAbsolutePaths) {
+			myfprintf(fileP,"%s%s\n",musicDir,
+				utf8ToFsCharset((playlist.songs[i])->utf8file));
+		}
+		else myfprintf(fileP,"%s\n",
+				utf8ToFsCharset((playlist.songs[i])->utf8file));
+	}
+
+	while(fclose(fileP) && errno==EINTR);
+
+	return 0;
+}
+
+int loadPlaylist(FILE * fp, char * utf8file) {
+	FILE * fileP;
+	char s[MAXPATHLEN*1];
+	int slength = 0;
+	char * temp = strdup(utf8ToFsCharset(utf8file));
+	char * rfile = malloc(strlen(temp)+strlen(".")+
+			strlen(PLAYLIST_FILE_SUFFIX)+1);
+	char * actualFile;
+	char * parent = parentPath(temp);
+	int parentlen = strlen(parent);
+	char * erroredFile = NULL;
+
+	strcpy(rfile,temp);
+	strcat(rfile,".");
+	strcat(rfile,PLAYLIST_FILE_SUFFIX);
+
+	free(temp);
+
+	if((actualFile = rpp2app(rfile)) && isPlaylist(actualFile)) free(rfile);
+	else {
+		free(rfile);
+		myfprintf(fp,"%s playlist \"%s\" not found\n",
+				COMMAND_RESPOND_ERROR,utf8file);
+		return -1;
+	}
+
+	while(!(fileP = fopen(actualFile,"r")) && errno==EINTR);
+	if(fileP==NULL) {
+		myfprintf(fp,"%s Problems opening file \"%s\"\n",
+				COMMAND_RESPOND_ERROR,utf8file);
+		return -1;
+	}
+
+	while((s[slength] = fgetc(fileP))!=EOF) {
+		if(s[slength]=='\n' || s[slength]=='\0') {
+			s[slength] = '\0';
+			if(strncmp(s,musicDir,strlen(musicDir))==0) {
+				strcpy(s,&(s[strlen(musicDir)]));
+			}
+			else if(parentlen) {
+				temp = strdup(s);
+				memset(s,0,MAXPATHLEN+1);
+				strcpy(s,parent);
+				strncat(s,"/",MAXPATHLEN-parentlen);
+				strncat(s,temp,MAXPATHLEN-parentlen-1);
+				if(strlen(s)>=MAXPATHLEN) {
+					myfprintf(fp,"%s \"%s\" too long\n",COMMAND_RESPOND_ERROR,temp);
+					free(temp);
+					while(fclose(fileP) && errno==EINTR);
+					if(erroredFile) free(erroredFile);
+					return -1;
+				}
+				free(temp);
+			}
+			slength = 0;
+			temp = strdup(fsCharsetToUtf8(s));
+			if(s[0]==PLAYLIST_COMMENT && !getSong(temp)) {
+				free(temp);
+				continue;
+			}
+			if((addToPlaylist(stderr,temp))<0) {
+				if(!erroredFile) erroredFile = strdup(temp);
+			}
+			free(temp);
+		}
+		else if(slength==MAXPATHLEN) {
+			s[slength] = '\0';
+			myfprintf(fp,"%s \"%s\" too long\n",COMMAND_RESPOND_ERROR,s);
+			while(fclose(fileP) && errno==EINTR);
+			if(erroredFile) free(erroredFile);
+			return -1;
+		}
+		else if(s[slength]!='\r') slength++;
+	}
+
+	while(fclose(fileP) && errno==EINTR);
+
+	if(erroredFile) {
+		myfprintf(fp,"%s can't add file \"%s\"\n",COMMAND_RESPOND_ERROR,
+				erroredFile);
+		free(erroredFile);
+		return -1;
+	}
+
+	return 0;
+}
+
+int getPlaylistCurrentSong() {
+	return playlist.order[playlist.current];
+}
+
+unsigned long getPlaylistVersion() {
+	return playlist.version;
+}
+
+int getPlaylistLength() {
+	return playlist.length;
+}
+
+int seekSongInPlaylist(FILE * fp, int song, float time) {
+	int i = song;
+
+	if(song<0) {
+		myfprintf(fp,"%s need integer >= -1\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+	if(!playlist.length) {
+		myfprintf(fp,"%s playlist is empty\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+	else if(song>=playlist.length) {
+		myfprintf(fp,"%s song doesn't exist\n",COMMAND_RESPOND_ERROR);
+		return -1;
+	}
+
+	if(playlist.random) for(i=0;song!=playlist.order[i];i++);
+
+	clearPlayerError();
+	playlist_stopOnError = 1;
+	playlist_errorCount = 0;
+
+	if(playlist_state == PLAYLIST_STATE_PLAY) {
+		if(playlist.queued>=0) {
+			lockPlaylistInteraction();
+			clearPlayerQueue();
+			unlockPlaylistInteraction();
+		}
+	}
+	else if(playPlaylistOrderNumber(fp,i)<0) return -1;
+
+	if(playlist.current!=i) if(playPlaylistOrderNumber(fp,i)<0) return -1;
+	return playerSeek(fp,playlist.songs[playlist.order[i]]->utf8file,time);
+}
-- 
cgit v1.2.3