Blame src/playlist.c

Packit c32a2d
/*
Packit c32a2d
	playlist: playlist logic
Packit c32a2d
Packit c32a2d
	copyright 1995-2008 by the mpg123 project - free software under the terms of the LGPL 2.1
Packit c32a2d
	see COPYING and AUTHORS files in distribution or http://mpg123.org
Packit c32a2d
	initially written by Michael Hipp, outsourced/reorganized by Thomas Orgis
Packit c32a2d
Packit c32a2d
	If we officially support Windows again, we should have this reworked to really cope with Windows paths, too.
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
/* Need random(). */
Packit c32a2d
#define _DEFAULT_SOURCE
Packit c32a2d
#define _BSD_SOURCE
Packit c32a2d
Packit c32a2d
#include "mpg123app.h"
Packit c32a2d
#include "sysutil.h"
Packit c32a2d
#include "getlopt.h" /* for loptind */
Packit c32a2d
#include "term.h" /* for term_restore */
Packit c32a2d
#include "playlist.h"
Packit c32a2d
#include "httpget.h"
Packit c32a2d
#include <time.h> /* For srand(). */
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
#ifdef HAVE_RANDOM
Packit c32a2d
#define RAND random
Packit c32a2d
#define SRAND srandom
Packit c32a2d
#else
Packit c32a2d
#define RAND rand
Packit c32a2d
#define SRAND srand
Packit c32a2d
#endif
Packit c32a2d
Packit c32a2d
/* increase linebuf in blocks of ... bytes */
Packit c32a2d
#define LINEBUF_STEP 100
Packit c32a2d
Packit c32a2d
enum playlist_type { UNKNOWN = 0, M3U, PLS, NO_LIST };
Packit c32a2d
Packit c32a2d
typedef struct listitem
Packit c32a2d
{
Packit c32a2d
	char* url; /* the filename */
Packit c32a2d
	char freeit; /* if it was allocated and should be free()d here */
Packit c32a2d
	size_t playcount; /* has been played as ...th track in overall counting */
Packit c32a2d
} listitem;
Packit c32a2d
Packit c32a2d
typedef struct playlist_struct
Packit c32a2d
{
Packit c32a2d
	FILE* file; /* the current playlist stream */
Packit c32a2d
	size_t entry; /* entry in the playlist file */
Packit c32a2d
	size_t playcount; /* overall track counter for playback */
Packit c32a2d
	long loop;    /* repeat a track n times */
Packit c32a2d
	size_t size;
Packit c32a2d
	size_t fill;
Packit c32a2d
	size_t pos; /* (next) position, internal use */
Packit c32a2d
	size_t num; /* current track number */
Packit c32a2d
	size_t alloc_step;
Packit c32a2d
	struct listitem* list;
Packit c32a2d
	mpg123_string linebuf;
Packit c32a2d
	mpg123_string dir;
Packit c32a2d
	enum playlist_type type;
Packit c32a2d
#if defined (WANT_WIN32_SOCKETS)
Packit c32a2d
	int sockd; /* Is Win32 socket descriptor working? */
Packit c32a2d
#endif
Packit c32a2d
} playlist_struct;
Packit c32a2d
Packit c32a2d
/* one global instance... add a pointer to this to every function definition and you have OO-style... */
Packit c32a2d
static playlist_struct pl;
Packit c32a2d
Packit c32a2d
static int add_next_file (int argc, char *argv[]);
Packit c32a2d
static void shuffle_playlist(void);
Packit c32a2d
static void init_playlist(void);
Packit c32a2d
static int add_copy_to_playlist(char* new_entry);
Packit c32a2d
static int add_to_playlist(char* new_entry, char freeit);
Packit c32a2d
Packit c32a2d
/* used to be init_input */
Packit c32a2d
void prepare_playlist(int argc, char** argv)
Packit c32a2d
{
Packit c32a2d
	/*
Packit c32a2d
		fetch all playlist entries ... I don't consider playlists to be an endless stream.
Packit c32a2d
		If you want to intentionally hang mpg123 on some other prog that may take infinite time to produce the full list (perhaps load tracks on demand), then just use the remote control and let that program print "load filename" instead of "filename".
Packit c32a2d
		We may even provide a simple wrapper script that emulates the old playlist reading behaviour (for files and stdin, http playlists are actually a strong point on reading the list in _before_ starting playback since http connections don't last forever).
Packit c32a2d
	*/
Packit c32a2d
	init_playlist();
Packit c32a2d
	while (add_next_file(argc, argv)) {}
Packit c32a2d
	if(param.verbose > 1)
Packit c32a2d
	{
Packit c32a2d
		fprintf(stderr, "\nplaylist in normal order:\n");
Packit c32a2d
		print_playlist(stderr, 0);
Packit c32a2d
		fprintf(stderr, "\n");
Packit c32a2d
	}
Packit c32a2d
	if(param.shuffle == 1) shuffle_playlist();
Packit c32a2d
	/* Don't need these anymore, we have copies! */
Packit c32a2d
	mpg123_free_string(&pl.linebuf);
Packit c32a2d
	mpg123_free_string(&pl.dir);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Return a random number >= 0 and < n */
Packit c32a2d
static size_t rando(size_t n)
Packit c32a2d
{
Packit c32a2d
	long ran;
Packit c32a2d
	long limit = RAND_MAX - (RAND_MAX % (long)n);
Packit c32a2d
	if(n<2) return 0; /* Better settle that here than in an endless loop... */
Packit c32a2d
	do{ ran = RAND(); }while( ran >= limit );
Packit c32a2d
	return (size_t)(ran%n);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
char *get_next_file(void)
Packit c32a2d
{
Packit c32a2d
	struct listitem *newitem = NULL;
Packit c32a2d
Packit c32a2d
	/* Zero looping is nothing, as is nothing at all. */
Packit c32a2d
	if(pl.fill == 0 || param.loop == 0)
Packit c32a2d
		return NULL;
Packit c32a2d
Packit c32a2d
	++pl.playcount;
Packit c32a2d
Packit c32a2d
	/* normal order, just pick next thing */
Packit c32a2d
	if(param.shuffle < 2)
Packit c32a2d
	{
Packit c32a2d
		do
Packit c32a2d
		{
Packit c32a2d
			if(pl.pos < pl.fill)
Packit c32a2d
			{
Packit c32a2d
				newitem = &pl.list[pl.pos];
Packit c32a2d
				pl.num = pl.pos+1;
Packit c32a2d
			}
Packit c32a2d
			else newitem = NULL;
Packit c32a2d
			/* if we have rounds left, decrease loop, else reinit loop because it's a new track */
Packit c32a2d
			if(pl.loop > 0) --pl.loop; /* loop for current track... */
Packit c32a2d
			if(pl.loop == 0)
Packit c32a2d
			{
Packit c32a2d
				pl.loop = param.loop;
Packit c32a2d
				++pl.pos;
Packit c32a2d
			}
Packit c32a2d
		} while(pl.loop == 0 && newitem != NULL);
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		/* Handle looping first, but only if there is a random track selection
Packit c32a2d
		   presently active (see playlist_jump() for interaction). */
Packit c32a2d
		if(!(pl.num && ((pl.loop > 0 && --pl.loop) || pl.loop < 0)))
Packit c32a2d
		{
Packit c32a2d
			/* Randomly select the next track. */
Packit c32a2d
			do /* limiting randomness: don't repeat too early */
Packit c32a2d
			{
Packit c32a2d
				pl.pos = rando(pl.fill);
Packit c32a2d
			} while( pl.list[pl.pos].playcount
Packit c32a2d
				&& (pl.playcount - pl.list[pl.pos].playcount) <= pl.fill/2 );
Packit c32a2d
			pl.loop = param.loop;
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		newitem = &pl.list[pl.pos];
Packit c32a2d
		pl.num = pl.pos+1;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	/* "-" is STDOUT, "" is dumb, NULL is nothing */
Packit c32a2d
	if(newitem != NULL)
Packit c32a2d
	{
Packit c32a2d
		/* Remember the playback position of the track. */
Packit c32a2d
		newitem->playcount = pl.playcount;
Packit c32a2d
		return newitem->url;
Packit c32a2d
	}
Packit c32a2d
	else return NULL;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
size_t playlist_pos(size_t *total, long *loop)
Packit c32a2d
{
Packit c32a2d
	if(total)
Packit c32a2d
		*total = pl.fill;
Packit c32a2d
	if(loop)
Packit c32a2d
		*loop = pl.loop;
Packit c32a2d
	return pl.num;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void playlist_jump(ssize_t incr)
Packit c32a2d
{
Packit c32a2d
	size_t off = incr < 0 ? -incr : incr;
Packit c32a2d
Packit c32a2d
	pl.loop = 0; /* This loop is done, always. */
Packit c32a2d
	/* Straight or shuffled lists can be jumped around in. */
Packit c32a2d
	if(pl.fill && param.shuffle < 2)
Packit c32a2d
	{
Packit c32a2d
		debug3("jump %"SIZE_P" (%ld) + %"SSIZE_P, pl.pos, pl.loop, incr);
Packit c32a2d
		if(pl.pos)
Packit c32a2d
			--pl.pos;
Packit c32a2d
		/* Now we're at the _current_ position. */
Packit c32a2d
		if(incr < 0)
Packit c32a2d
			pl.pos -= off > pl.pos ? pl.pos : off;
Packit c32a2d
		else
Packit c32a2d
		{
Packit c32a2d
			if(off >= pl.fill - pl.pos)
Packit c32a2d
				pl.pos = pl.fill; /* Any value >= pl.fill would actually be OK. */
Packit c32a2d
			else
Packit c32a2d
				pl.pos += off;
Packit c32a2d
		}
Packit c32a2d
		debug2("jumped %"SIZE_P" (%ld)", pl.pos, pl.loop);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Directory jumping based on comparing the directory part of the playlist
Packit c32a2d
   URLs. */
Packit c32a2d
static int cmp_dir(const char* patha, const char* pathb)
Packit c32a2d
{
Packit c32a2d
	size_t dirlen[2];
Packit c32a2d
	dirlen[0] = dir_length(patha);
Packit c32a2d
	dirlen[1] = dir_length(pathb);
Packit c32a2d
	return (dirlen[0] < dirlen[1])
Packit c32a2d
	?	-1
Packit c32a2d
	:	( dirlen[0] > dirlen[1]
Packit c32a2d
		?	1
Packit c32a2d
		:	memcmp(patha, pathb, dirlen[0])
Packit c32a2d
		);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void playlist_next_dir(void)
Packit c32a2d
{
Packit c32a2d
	if(pl.fill && param.shuffle < 2)
Packit c32a2d
	{
Packit c32a2d
		size_t npos = pl.pos ? pl.pos-1 : 0;
Packit c32a2d
		do ++npos;
Packit c32a2d
		while(npos < pl.fill && !cmp_dir(pl.list[npos-1].url, pl.list[npos].url));
Packit c32a2d
		pl.pos = npos;
Packit c32a2d
	}
Packit c32a2d
	pl.loop = 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void playlist_prev_dir(void)
Packit c32a2d
{
Packit c32a2d
	if(pl.fill && param.shuffle < 2)
Packit c32a2d
	{
Packit c32a2d
		size_t npos = pl.pos ? pl.pos-1 : 0;
Packit c32a2d
		/* 1. Find end of previous directory. */
Packit c32a2d
		if(npos && npos < pl.fill)
Packit c32a2d
			do --npos;
Packit c32a2d
			while(npos && !cmp_dir(pl.list[npos+1].url, pl.list[npos].url));
Packit c32a2d
		/* npos == the last track of previous directory */
Packit c32a2d
		/* 2. Find the first track of this directory */
Packit c32a2d
		if(npos < pl.fill)
Packit c32a2d
			while(npos && !cmp_dir(pl.list[npos-1].url, pl.list[npos].url))
Packit c32a2d
				--npos;
Packit c32a2d
		pl.pos = npos;
Packit c32a2d
	}
Packit c32a2d
	pl.loop = 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* It doesn't really matter on program exit, but anyway...
Packit c32a2d
   Make sure you don't free() an item of argv! */
Packit c32a2d
void free_playlist(void)
Packit c32a2d
{
Packit c32a2d
	if(pl.list != NULL)
Packit c32a2d
	{
Packit c32a2d
		debug("going to free() the playlist");
Packit c32a2d
		while(pl.fill)
Packit c32a2d
		{
Packit c32a2d
			--pl.fill;
Packit c32a2d
			debug1("free()ing entry %lu", (unsigned long)pl.fill);
Packit c32a2d
			if(pl.list[pl.fill].freeit) free(pl.list[pl.fill].url);
Packit c32a2d
		}
Packit c32a2d
		free(pl.list);
Packit c32a2d
		pl.list = NULL;
Packit c32a2d
		pl.size = 0;
Packit c32a2d
		debug("free()d the playlist");
Packit c32a2d
	}
Packit c32a2d
	mpg123_free_string(&pl.linebuf);
Packit c32a2d
	mpg123_free_string(&pl.dir);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* the constructor... */
Packit c32a2d
static void init_playlist(void)
Packit c32a2d
{
Packit c32a2d
	SRAND(time(NULL));
Packit c32a2d
	pl.file = NULL;
Packit c32a2d
	pl.entry = 0;
Packit c32a2d
	pl.playcount = 0;
Packit c32a2d
	pl.size = 0;
Packit c32a2d
	pl.fill = 0;
Packit c32a2d
	pl.pos = 0;
Packit c32a2d
	pl.num = 0;
Packit c32a2d
	if(APPFLAG(MPG123APP_CONTINUE) && param.listentry > 0)
Packit c32a2d
	pl.pos = param.listentry - 1;
Packit c32a2d
Packit c32a2d
	pl.list = NULL;
Packit c32a2d
	pl.alloc_step = 10;
Packit c32a2d
	mpg123_init_string(&pl.dir);
Packit c32a2d
	mpg123_init_string(&pl.linebuf);
Packit c32a2d
	pl.type = UNKNOWN;
Packit c32a2d
	pl.loop = param.loop;
Packit c32a2d
#ifdef WANT_WIN32_SOCKETS
Packit c32a2d
	pl.sockd = -1;
Packit c32a2d
#endif
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/*
Packit c32a2d
	slightly modified find_next_file from mpg123.c
Packit c32a2d
	now doesn't return the next entry but adds it to playlist struct
Packit c32a2d
	returns 1 if it found something, 0 on end
Packit c32a2d
*/
Packit c32a2d
static int add_next_file (int argc, char *argv[])
Packit c32a2d
{
Packit c32a2d
	int firstline = 0;
Packit c32a2d
Packit c32a2d
	/* hack for url that has been detected as track, not playlist */
Packit c32a2d
	if(pl.type == NO_LIST) return 0;
Packit c32a2d
Packit c32a2d
	/* Get playlist dirname to append it to the files in playlist */
Packit c32a2d
	if (param.listname)
Packit c32a2d
	{
Packit c32a2d
		char* slashpos;
Packit c32a2d
		/* Oh, right... that doesn't look good for Windows... */
Packit c32a2d
		if ((slashpos=strrchr(param.listname, '/')))
Packit c32a2d
		{
Packit c32a2d
			/* up to and including /, with space for \0 */
Packit c32a2d
			if(mpg123_resize_string(&pl.dir, 2 + slashpos - param.listname))
Packit c32a2d
			{
Packit c32a2d
				memcpy(pl.dir.p, param.listname, pl.dir.size-1);
Packit c32a2d
				pl.dir.p[pl.dir.size-1] = 0;
Packit c32a2d
			}
Packit c32a2d
			else
Packit c32a2d
			{
Packit c32a2d
				error("cannot allocate memory for list directory!");
Packit c32a2d
				pl.dir.size = 0;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if (param.listname || pl.file)
Packit c32a2d
	{
Packit c32a2d
		size_t line_offset = 0;
Packit c32a2d
#ifndef WANT_WIN32_SOCKETS
Packit c32a2d
		if (!pl.file)
Packit c32a2d
#else
Packit c32a2d
		if (!pl.file && pl.sockd == -1)
Packit c32a2d
#endif
Packit c32a2d
		{
Packit c32a2d
			/* empty or "-" */
Packit c32a2d
			if (!*param.listname || !strcmp(param.listname, "-"))
Packit c32a2d
			{
Packit c32a2d
				pl.file = stdin;
Packit c32a2d
				param.listname = NULL;
Packit c32a2d
				pl.entry = 0;
Packit c32a2d
			}
Packit c32a2d
			else if (!strncmp(param.listname, "http://", 7))
Packit c32a2d
			{
Packit c32a2d
				int fd;
Packit c32a2d
				struct httpdata htd;
Packit c32a2d
				httpdata_init(&htd);
Packit c32a2d
#ifndef WANT_WIN32_SOCKETS
Packit c32a2d
				fd = http_open(param.listname, &htd);
Packit c32a2d
#else
Packit c32a2d
				fd = win32_net_http_open(param.listname, &htd);
Packit c32a2d
#endif
Packit c32a2d
				debug1("htd.content_type.p: %p", (void*) htd.content_type.p);
Packit c32a2d
				if(!APPFLAG(MPG123APP_IGNORE_MIME) && htd.content_type.p != NULL)
Packit c32a2d
				{
Packit c32a2d
					int mimi;
Packit c32a2d
					debug1("htd.content_type.p value: %s", htd.content_type.p);
Packit c32a2d
					mimi = debunk_mime(htd.content_type.p);
Packit c32a2d
Packit c32a2d
					if(mimi & IS_M3U) pl.type = M3U;
Packit c32a2d
					else if(mimi & IS_PLS)	pl.type = PLS;
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
#ifndef WANT_WIN32_SOCKETS
Packit c32a2d
						if(fd >= 0) close(fd);
Packit c32a2d
#else
Packit c32a2d
						if(fd != SOCKET_ERROR) win32_net_close(fd);
Packit c32a2d
#endif
Packit c32a2d
						fd = -1;
Packit c32a2d
						
Packit c32a2d
						if(mimi & IS_FILE)
Packit c32a2d
						{
Packit c32a2d
							pl.type = NO_LIST;
Packit c32a2d
							if(param.listentry < 0)
Packit c32a2d
							{
Packit c32a2d
								printf("#note you gave me a file url, no playlist, so...\n#entry 1\n%s\n", param.listname);
Packit c32a2d
								return 0;
Packit c32a2d
							}
Packit c32a2d
							else
Packit c32a2d
							{
Packit c32a2d
								fprintf(stderr, "Note: MIME type indicates that this is no playlist but an mpeg audio file... reopening as such.\n");
Packit c32a2d
								add_to_playlist(param.listname, 0);
Packit c32a2d
								return 1;
Packit c32a2d
							}
Packit c32a2d
						}
Packit c32a2d
						error1("Unknown playlist MIME type %s; maybe "PACKAGE_NAME" can support it in future if you report this to the maintainer.", htd.content_type.p);
Packit c32a2d
					}
Packit c32a2d
					httpdata_free(&htd);
Packit c32a2d
				}
Packit c32a2d
				if(fd < 0)
Packit c32a2d
				{
Packit c32a2d
					param.listname = NULL;
Packit c32a2d
					pl.file = NULL;
Packit c32a2d
#ifdef WANT_WIN32_SOCKETS
Packit c32a2d
					pl.sockd = -1;
Packit c32a2d
#endif
Packit c32a2d
					error("Invalid playlist from http_open()!\n");
Packit c32a2d
				}
Packit c32a2d
				else
Packit c32a2d
				{
Packit c32a2d
					pl.entry = 0;
Packit c32a2d
#ifndef WANT_WIN32_SOCKETS
Packit c32a2d
					pl.file = compat_fdopen(fd,"r");
Packit c32a2d
#else
Packit c32a2d
					pl.sockd = fd;
Packit c32a2d
#endif
Packit c32a2d
				}
Packit c32a2d
			}
Packit c32a2d
			else if (!(pl.file = fopen(param.listname, "rb")))
Packit c32a2d
			{
Packit c32a2d
				perror (param.listname);
Packit c32a2d
				return 0;
Packit c32a2d
			}
Packit c32a2d
			else
Packit c32a2d
			{
Packit c32a2d
				debug("opened ordinary list file");
Packit c32a2d
				pl.entry = 0;
Packit c32a2d
			}
Packit c32a2d
			if (param.verbose && pl.file) fprintf (stderr, "Using playlist from %s ...\n",	param.listname ? param.listname : "standard input");
Packit c32a2d
			firstline = 1; /* just opened */
Packit c32a2d
		}
Packit c32a2d
		/* reading the file line by line */
Packit c32a2d
#ifndef WANT_WIN32_SOCKETS
Packit c32a2d
		while (pl.file)
Packit c32a2d
#else
Packit c32a2d
		while (pl.file || (pl.sockd) != -1)
Packit c32a2d
#endif
Packit c32a2d
		{
Packit c32a2d
			/*
Packit c32a2d
				now read a string of arbitrary size...
Packit c32a2d
				read a chunk, see if lineend, realloc, read another chunk
Packit c32a2d
				
Packit c32a2d
				fgets reads at most size-1 bytes and always appends the \0 
Packit c32a2d
			*/
Packit c32a2d
			size_t have = 0;
Packit c32a2d
			do
Packit c32a2d
			{
Packit c32a2d
				/* have is the length of the string read, without the closing \0 */
Packit c32a2d
				if(pl.linebuf.size <= have+1)
Packit c32a2d
				{
Packit c32a2d
					if(!mpg123_resize_string(&pl.linebuf, pl.linebuf.size+LINEBUF_STEP))
Packit c32a2d
					{
Packit c32a2d
						error("cannot increase line buffer");
Packit c32a2d
						break;
Packit c32a2d
					}
Packit c32a2d
				}
Packit c32a2d
				/* I rely on fgets writing the \0 at the end! */
Packit c32a2d
#ifndef WANT_WIN32_SOCKETS
Packit c32a2d
				if(fgets(pl.linebuf.p+have, pl.linebuf.size-have, pl.file))
Packit c32a2d
#else
Packit c32a2d
				if( (pl.file ? (fgets(pl.linebuf.p+have, pl.linebuf.size-have, pl.file)) : (win32_net_fgets(pl.linebuf.p+have, pl.linebuf.size-have, pl.sockd))))
Packit c32a2d
#endif
Packit c32a2d
				{
Packit c32a2d
					have += strlen(pl.linebuf.p+have);
Packit c32a2d
					debug2("have read %lu characters into linebuf: [%s]", (unsigned long)have, pl.linebuf.p);
Packit c32a2d
				}
Packit c32a2d
				else
Packit c32a2d
				{
Packit c32a2d
					debug("fgets failed to deliver something... file ended?");
Packit c32a2d
					break;
Packit c32a2d
				}
Packit c32a2d
			} while(have && pl.linebuf.p[have-1] != '\r' && pl.linebuf.p[have-1] != '\n');
Packit c32a2d
			if(have)
Packit c32a2d
			{
Packit c32a2d
				pl.linebuf.p[strcspn(pl.linebuf.p, "\t\n\r")] = '\0';
Packit c32a2d
				/* a bit of fuzzyness */
Packit c32a2d
				if(firstline)
Packit c32a2d
				{
Packit c32a2d
					if(pl.type == UNKNOWN)
Packit c32a2d
					{
Packit c32a2d
						if(!strcmp("[playlist]", pl.linebuf.p))
Packit c32a2d
						{
Packit c32a2d
							fprintf(stderr, "Note: detected Shoutcast/Winamp PLS playlist\n");
Packit c32a2d
							pl.type = PLS;
Packit c32a2d
							continue;
Packit c32a2d
						}
Packit c32a2d
						else if
Packit c32a2d
						(
Packit c32a2d
							(!strncasecmp("#M3U", pl.linebuf.p ,4))
Packit c32a2d
							||
Packit c32a2d
							(!strncasecmp("#EXTM3U", pl.linebuf.p ,7))
Packit c32a2d
							||
Packit c32a2d
							(param.listname != NULL && (strrchr(param.listname, '.')) != NULL && !strcasecmp(".m3u", strrchr(param.listname, '.')))
Packit c32a2d
						)
Packit c32a2d
						{
Packit c32a2d
							if(param.verbose) fprintf(stderr, "Note: detected M3U playlist type\n");
Packit c32a2d
							pl.type = M3U;
Packit c32a2d
						}
Packit c32a2d
						else
Packit c32a2d
						{
Packit c32a2d
							if(param.verbose) fprintf(stderr, "Note: guessed M3U playlist type\n");
Packit c32a2d
							pl.type = M3U;
Packit c32a2d
						}
Packit c32a2d
					}
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
						if(param.verbose)
Packit c32a2d
						{
Packit c32a2d
							fprintf(stderr, "Note: Interpreting as ");
Packit c32a2d
							switch(pl.type)
Packit c32a2d
							{
Packit c32a2d
								case M3U: fprintf(stderr, "M3U"); break;
Packit c32a2d
								case PLS: fprintf(stderr, "PLS (Winamp/Shoutcast)"); break;
Packit c32a2d
								default: fprintf(stderr, "???");
Packit c32a2d
							}
Packit c32a2d
							fprintf(stderr, " playlist\n");
Packit c32a2d
						}
Packit c32a2d
					}
Packit c32a2d
					firstline = 0;
Packit c32a2d
				}
Packit c32a2d
				#if !defined(WIN32)
Packit c32a2d
				{
Packit c32a2d
				size_t i;
Packit c32a2d
				/* convert \ to / (from MS-like directory format) */
Packit c32a2d
				for (i=0;pl.linebuf.p[i]!='\0';i++)
Packit c32a2d
				{
Packit c32a2d
					if (pl.linebuf.p[i] == '\\')	pl.linebuf.p[i] = '/';
Packit c32a2d
				}
Packit c32a2d
				}
Packit c32a2d
				#endif
Packit c32a2d
				if (pl.linebuf.p[0]=='\0') continue; /* skip empty lines... */
Packit c32a2d
				if (((pl.type == M3U) && (pl.linebuf.p[0]=='#')))
Packit c32a2d
				{
Packit c32a2d
					/* a comment line in m3u file */
Packit c32a2d
					if(param.listentry < 0) printf("%s\n", pl.linebuf.p);
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				/* real filename may start at an offset */
Packit c32a2d
				line_offset = 0;
Packit c32a2d
				/* extract path out of PLS */
Packit c32a2d
				if(pl.type == PLS)
Packit c32a2d
				{
Packit c32a2d
					if(!strncasecmp("File", pl.linebuf.p, 4))
Packit c32a2d
					{
Packit c32a2d
						/* too lazy to really check for file number... would have to change logic to support unordered file entries anyway */
Packit c32a2d
						char* in_line;
Packit c32a2d
						if((in_line = strchr(pl.linebuf.p+4, '=')) != NULL)
Packit c32a2d
						{
Packit c32a2d
							/* FileN=? */
Packit c32a2d
							if(in_line[1] != 0)
Packit c32a2d
							{
Packit c32a2d
								++in_line;
Packit c32a2d
								line_offset = (size_t) (in_line-pl.linebuf.p);
Packit c32a2d
							}
Packit c32a2d
							else
Packit c32a2d
							{
Packit c32a2d
								fprintf(stderr, "Warning: Invalid PLS line (empty filename) - corrupt playlist file?\n");
Packit c32a2d
								continue;
Packit c32a2d
							}
Packit c32a2d
						}
Packit c32a2d
						else
Packit c32a2d
						{
Packit c32a2d
							fprintf(stderr, "Warning: Invalid PLS line (no '=' after 'File') - corrupt playlist file?\n");
Packit c32a2d
							continue;
Packit c32a2d
						}
Packit c32a2d
					}
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
						if(param.listentry < 0) printf("#metainfo %s\n", pl.linebuf.p);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				/* make paths absolute */
Packit c32a2d
				/* Windows knows absolute paths with c: in front... should handle this if really supporting win32 again */
Packit c32a2d
				if
Packit c32a2d
				(
Packit c32a2d
					(pl.dir.p != NULL)
Packit c32a2d
					&& (pl.linebuf.p[line_offset]!='/')
Packit c32a2d
					&& (pl.linebuf.p[line_offset]!='\\')
Packit c32a2d
					&& strncmp(pl.linebuf.p+line_offset, "http://", 7)
Packit c32a2d
				)
Packit c32a2d
				{
Packit c32a2d
					size_t need;
Packit c32a2d
					need = pl.dir.size + strlen(pl.linebuf.p+line_offset);
Packit c32a2d
					if(pl.linebuf.size < need)
Packit c32a2d
					{
Packit c32a2d
						if(!mpg123_resize_string(&pl.linebuf, need))
Packit c32a2d
						{
Packit c32a2d
							error("unable to enlarge linebuf for appending path! skipping");
Packit c32a2d
							continue;
Packit c32a2d
						}
Packit c32a2d
					}
Packit c32a2d
					/* move to have the space at beginning */
Packit c32a2d
					memmove(pl.linebuf.p+pl.dir.size-1, pl.linebuf.p+line_offset, strlen(pl.linebuf.p+line_offset)+1);
Packit c32a2d
					/* prepend path */
Packit c32a2d
					memcpy(pl.linebuf.p, pl.dir.p, pl.dir.size-1);
Packit c32a2d
					line_offset = 0;
Packit c32a2d
				}
Packit c32a2d
				++pl.entry;
Packit c32a2d
				if(param.listentry < 0) printf("#entry %lu\n%s\n", (unsigned long)pl.entry,pl.linebuf.p+line_offset);
Packit c32a2d
				else if((param.listentry == 0) || (param.listentry == pl.entry) || APPFLAG(MPG123APP_CONTINUE))
Packit c32a2d
				{
Packit c32a2d
					add_copy_to_playlist(pl.linebuf.p+line_offset);
Packit c32a2d
					return 1;
Packit c32a2d
				}
Packit c32a2d
			}
Packit c32a2d
			else
Packit c32a2d
			{
Packit c32a2d
				if (param.listname)
Packit c32a2d
				if(pl.file) fclose (pl.file);
Packit c32a2d
				param.listname = NULL;
Packit c32a2d
				pl.file = NULL;
Packit c32a2d
#ifdef WANT_WIN32_SOCKETS
Packit c32a2d
				if( pl.sockd != -1)
Packit c32a2d
				{
Packit c32a2d
				  win32_net_close(pl.sockd);
Packit c32a2d
				  pl.sockd = -1;
Packit c32a2d
				}
Packit c32a2d
#endif
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	if(loptind < argc)
Packit c32a2d
	{
Packit c32a2d
		add_to_playlist(argv[loptind++], 0);
Packit c32a2d
		return 1;
Packit c32a2d
	}
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void shuffle_playlist(void)
Packit c32a2d
{
Packit c32a2d
	size_t loop;
Packit c32a2d
	size_t rannum;
Packit c32a2d
	if(pl.fill >= 2)
Packit c32a2d
	{
Packit c32a2d
		/* Refer to bug 1777621 for discussion on that.
Packit c32a2d
		   It's Durstenfeld... */
Packit c32a2d
		for (loop = 0; loop < pl.fill; loop++)
Packit c32a2d
		{
Packit c32a2d
			struct listitem tmp;
Packit c32a2d
			rannum = loop + rando(pl.fill-loop);
Packit c32a2d
			/*
Packit c32a2d
				Small test on your binary operation skills (^ is XOR):
Packit c32a2d
				a = b^(a^b)
Packit c32a2d
				b = (a^b)^(b^(a^b))
Packit c32a2d
				And, understood? ;-)
Packit c32a2d
				
Packit c32a2d
				pl.list[loop] ^= pl.list[rannum];
Packit c32a2d
				pl.list[rannum] ^= pl.list[loop];
Packit c32a2d
				pl.list[loop] ^= pl.list[rannum];
Packit c32a2d
				
Packit c32a2d
				But since this is not allowed with pointers and any speed gain questionable (well, saving _some_ memory...), doing it the lame way:
Packit c32a2d
			*/
Packit c32a2d
			tmp = pl.list[rannum];
Packit c32a2d
			pl.list[rannum] = pl.list[loop];
Packit c32a2d
			pl.list[loop] = tmp;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	if(param.verbose > 1)
Packit c32a2d
	{
Packit c32a2d
		/* print them */
Packit c32a2d
		fprintf(stderr, "\nshuffled playlist:\n");
Packit c32a2d
		print_playlist(stderr, 0);
Packit c32a2d
		fprintf(stderr, "\n");
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void print_playlist(FILE* out, int showpos)
Packit c32a2d
{
Packit c32a2d
	size_t loop;
Packit c32a2d
	for (loop = 0; loop < pl.fill; loop++)
Packit c32a2d
	{
Packit c32a2d
		char *pre = "";
Packit c32a2d
		if(showpos)
Packit c32a2d
		pre = (loop+1==pl.num) ? "> " : "  ";
Packit c32a2d
Packit c32a2d
		fprintf(out, "%s%s\n", pre, pl.list[loop].url);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
Packit c32a2d
static int add_copy_to_playlist(char* new_entry)
Packit c32a2d
{
Packit c32a2d
	char* cop;
Packit c32a2d
	if((cop = (char*) malloc(strlen(new_entry)+1)) != NULL)
Packit c32a2d
	{
Packit c32a2d
		strcpy(cop, new_entry);
Packit c32a2d
		return add_to_playlist(cop, 1);
Packit c32a2d
	}
Packit c32a2d
	else return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* add new entry to playlist - no string copy, just the pointer! */
Packit c32a2d
static int add_to_playlist(char* new_entry, char freeit)
Packit c32a2d
{
Packit c32a2d
	if(pl.fill == pl.size)
Packit c32a2d
	{
Packit c32a2d
		struct listitem* tmp = NULL;
Packit c32a2d
		/* enlarge the list */
Packit c32a2d
		tmp = (struct listitem*) safe_realloc(pl.list, (pl.size + pl.alloc_step) * sizeof(struct listitem));
Packit c32a2d
		if(!tmp)
Packit c32a2d
		{
Packit c32a2d
			error("unable to allocate more memory for playlist");
Packit c32a2d
			perror("");
Packit c32a2d
			return 0;
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
		{
Packit c32a2d
			pl.list = tmp;
Packit c32a2d
			pl.size += pl.alloc_step;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	/* paranoid */
Packit c32a2d
	if(pl.fill < pl.size)
Packit c32a2d
	{
Packit c32a2d
		pl.list[pl.fill].freeit = freeit;
Packit c32a2d
		pl.list[pl.fill].url = new_entry;
Packit c32a2d
		pl.list[pl.fill].playcount = 0;
Packit c32a2d
		++pl.fill;
Packit c32a2d
	}
Packit c32a2d
	else
Packit c32a2d
	{
Packit c32a2d
		error("playlist memory still too small?!");
Packit c32a2d
		return 0;
Packit c32a2d
	}
Packit c32a2d
	return 1;
Packit c32a2d
}
Packit c32a2d