[xiph-commits] r12591 - in trunk/ezstream: . conf doc src

moritz at svn.xiph.org moritz at svn.xiph.org
Wed Feb 28 13:26:23 PST 2007


Author: moritz
Date: 2007-02-28 13:26:16 -0800 (Wed, 28 Feb 2007)
New Revision: 12591

Modified:
   trunk/ezstream/NEWS
   trunk/ezstream/conf/ezstream_reencoding_example_mp3.xml
   trunk/ezstream/conf/ezstream_reencoding_example_vorbis.xml
   trunk/ezstream/doc/ezstream.1.in
   trunk/ezstream/src/configfile.c
   trunk/ezstream/src/configfile.h
   trunk/ezstream/src/ezstream.c
   trunk/ezstream/src/playlist.c
   trunk/ezstream/src/playlist.h
Log:
Add new playlist scripting feature (works similar to Ices 2.x.)


Modified: trunk/ezstream/NEWS
===================================================================
--- trunk/ezstream/NEWS	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/NEWS	2007-02-28 21:26:16 UTC (rev 12591)
@@ -6,6 +6,9 @@
  * New features:
    - Playlist shuffling support, enabled via the new <shuffle> configuration
      option.
+   - Playlist scripting support: Indicate that the executable in <filename>
+     should be run each time to get a new media filename to stream, by setting
+     the new <playlist_program> configuration option to 1.
    - Add feature to skip the currently streaming track, done by sending the
      SIGUSR1 signal to the ezstream process.
    - New command line option `-q': Suppress standard error output from external
@@ -15,6 +18,8 @@
      on the command line.
    - Thorough configuration file checks with helpful error messages.
    - The @M@ metadata placeholder is now supported in <decode>.
+   - Playlists may now have the '.txt' filename extension in addition to
+     '.m3u'.
 
  * Fixes:
    - At least one stack and one heap overflow have been fixed.

Modified: trunk/ezstream/conf/ezstream_reencoding_example_mp3.xml
===================================================================
--- trunk/ezstream/conf/ezstream_reencoding_example_mp3.xml	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/conf/ezstream_reencoding_example_mp3.xml	2007-02-28 21:26:16 UTC (rev 12591)
@@ -1,5 +1,5 @@
 <!--
-   EXAMPLE: MP3 playlist stream WITH reencoding and sequential playback
+   EXAMPLE: MP3 stream using an external playlist program, WITH reencoding
 
    This example streams a playlist that may contain MP3, Ogg Vorbis and FLAC
    files. Ezstream will use external decoders to read the media files, and
@@ -14,12 +14,12 @@
        output format of the stream.
      -->
     <format>MP3</format>
-    <filename>playlist.m3u</filename>
+    <filename>playlist.pl</filename>
     <!--
-       Explicitly disable playlist shuffling. Sequential playback is also the
-       default.
+       Indicate that <filename> contains an executable program or script,
+       which prints a single line with a filename to standard output:
      -->
-    <shuffle>0</shuffle>
+    <playlist_program>1</playlist_program>
     <!--
       The following settings are used to describe your stream to the server.
       It's up to you to make sure that the bitrate/samplerate/channels

Modified: trunk/ezstream/conf/ezstream_reencoding_example_vorbis.xml
===================================================================
--- trunk/ezstream/conf/ezstream_reencoding_example_vorbis.xml	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/conf/ezstream_reencoding_example_vorbis.xml	2007-02-28 21:26:16 UTC (rev 12591)
@@ -18,6 +18,11 @@
     <!-- Enable playlist shuffling: -->
     <shuffle>1</shuffle>
     <!--
+       The file in <filename> is a regular playlist and not a program.
+       For demonstrational purposes, explicitly state this here:
+     -->
+    <playlist_program>0</playlist_program>
+    <!--
       The following settings are used to describe your stream to the server.
       It's up to you to make sure that the bitrate/quality/samplerate/channels
       information matches up with your oggenc encoder settings below.

Modified: trunk/ezstream/doc/ezstream.1.in
===================================================================
--- trunk/ezstream/doc/ezstream.1.in	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/doc/ezstream.1.in	2007-02-28 21:26:16 UTC (rev 12591)
@@ -106,7 +106,7 @@
 It contains all other configuration elements.
 .El
 .Ss Global configuration elements
-Each of the global configuration elements have the \&<ezstream\ /\&> element as
+Each of the global configuration elements have the \&<ezstream/\&> element as
 their parent.
 .Bl -ohang
 .It Sy \&<url\ /\&>
@@ -143,7 +143,10 @@
 to simply pass through the data, which may or may not work.
 .It Sy \&<filename\ /\&>
 .Pq Mandatory.
-Set the path and name of a single media file, a playlist or the keyword
+Set the path and name of a single media file, a playlist, the name of an
+external program
+.Pq see below ,
+or the keyword
 .Pa stdin
 for streaming from standard input.
 Playlists are recognized by their filename extension and end with either
@@ -156,16 +159,24 @@
 .Sq Li #
 sign at the beginning of a line and ignored by
 .Nm .
+.It Sy \&<playlist_program\ /\&>
+Indicates that the file in \&<filename/\&> is actually an executable program
+or script.
+This program is supposed to print
+.Pq to standard output
+one line with the name of a file that should be streamed next and then exit.
 .It Sy \&<shuffle\ /\&>
 .Pq Optional.
 Set to
 .Sy 1
 .Pq one
-to randomly shuffle the entries of the playlist specified in \&<filename\ /\&>.
+to randomly shuffle the entries of the playlist specified in \&<filename/\&>.
 Files are played sequentially if set to
 .Sy 0
 .Pq zero
-or when the \&<shuffle\ /\&> element is absent.
+or when the \&<shuffle/\&> element is absent.
+This option will be ignored if \&<playlist_program/\&> is set to 1
+.Pq one.
 .It Sy \&<svrinfoname\ /\&>
 .Pq Optional.
 Set the name of the broadcast.
@@ -216,14 +227,14 @@
 .Sy 0
 .Pq zero ,
 the Icecast server will not submit this stream to a YP directory, which is also
-the default if the \&<svrinfopublic\ /\&> element is absent.
+the default if the \&<svrinfopublic/\&> element is absent.
 .It Sy \&<reencode\ /\&>
 .Pq Optional.
 Element that contains child elements, which specify if and how reencoding
 should be done.
 .El
 .Ss Reencoding settings
-Each of the reencoding configuration elements have the \&<reencode\ /\&>
+Each of the reencoding configuration elements have the \&<reencode/\&>
 element as their parent.
 .Bl -ohang
 .It Sy \&<enable\ /\&>
@@ -234,22 +245,22 @@
 If set to
 .Sy 0
 .Pq zero ,
-no reencoding will be done, which is also the default if the \&<enable\ /\&>
+no reencoding will be done, which is also the default if the \&<enable/\&>
 element is absent.
 .It Sy \&<encdec\ /\&>
 Element that contains child elements, which specify how to decode and encode
 a certain media file format for streaming.
-Each format is described by a separate \&<encdec\ /\&> element.
+Each format is described by a separate \&<encdec/\&> element.
 .El
 .Ss Dencoder/Encoder settings
-Each of the decoder/encoder configuration elements have the \&<encdec\ /\&>
+Each of the decoder/encoder configuration elements have the \&<encdec/\&>
 element as their parent.
 .Bl -ohang
 .It Sy \&<format\ /\&>
 This element is used by
 .Nm
 to find the appropriate encoder for the output stream format specified in the
-\&<format\ /\&> element inside the global configuration.
+\&<format/\&> element inside the global configuration.
 It is recommended that this element is always supplied, even for currently
 unsupported output formats, with content such as
 .Sy VORBIS ,
@@ -275,13 +286,13 @@
 During runtime, the placeholder
 .Sq Li @T@
 is replaced with the fully qualified name of the media file, as specified in
-the \&<filename\ /\&> element or a playlist file.
+the \&<filename/\&> element or a playlist file.
 It should always be enclosed in quotes, to prevent problems with filenames that
 contain whitespaces.
 .Pp
 The metadata placeholder,
 .Sq @M@ ,
-is also available in the \&<decode\ /\&> element.
+is also available in the \&<decode/\&> element.
 That way it can be used for combined de-/encoder programs that produce readily
 streamable data.
 .Pp

Modified: trunk/ezstream/src/configfile.c
===================================================================
--- trunk/ezstream/src/configfile.c	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/src/configfile.c	2007-02-28 21:26:16 UTC (rev 12591)
@@ -140,7 +140,7 @@
 	xmlDocPtr	 doc;
 	xmlNodePtr	 cur;
 	char		*ls_xmlContentPtr;
-	int		 shuffle_set, svrinfopublic_set;
+	int		 shuffle_set, svrinfopublic_set, program_set;
 
 	xmlLineNumbersDefault(1);
 	if ((doc = xmlParseFile(fileName)) == NULL) {
@@ -160,6 +160,7 @@
 
 	shuffle_set = 0;
 	svrinfopublic_set = 0;
+	program_set = 0;
 	cur = cur->xmlChildrenNode;
 	while (cur != NULL) {
 		if (!xmlStrcmp(cur->name, BAD_CAST "url")) {
@@ -219,6 +220,22 @@
 				xmlFree(ls_xmlContentPtr);
 			}
 		}
+		if (!xmlStrcmp(cur->name, BAD_CAST "playlist_program")) {
+			if (program_set) {
+				printf("%s[%ld]: Error: Cannot have multiple <playlist_program> elements.\n",
+				       fileName, xmlGetLineNo(cur));
+				goto config_error;
+			}
+			if (cur->xmlChildrenNode != NULL) {
+				int tmp;
+
+				ls_xmlContentPtr = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+				tmp = atoi(ls_xmlContentPtr);
+				ezConfig.fileNameIsProgram = (tmp == 0) ? 0 : 1;
+				xmlFree(ls_xmlContentPtr);
+				program_set = 1;
+			}
+		}
 		if (!xmlStrcmp(cur->name, BAD_CAST "shuffle")) {
 			if (shuffle_set) {
 				printf("%s[%ld]: Error: Cannot have multiple <shuffle> elements.\n",

Modified: trunk/ezstream/src/configfile.h
===================================================================
--- trunk/ezstream/src/configfile.h	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/src/configfile.h	2007-02-28 21:26:16 UTC (rev 12591)
@@ -57,6 +57,7 @@
 	FORMAT_ENCDEC	*encoderDecoders[MAX_FORMAT_ENCDEC];
 	int		 numEncoderDecoders;
 	int		 shuffle;
+	int		 fileNameIsProgram;
 } EZCONFIG;
 
 EZCONFIG *	getEZConfig(void);

Modified: trunk/ezstream/src/ezstream.c
===================================================================
--- trunk/ezstream/src/ezstream.c	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/src/ezstream.c	2007-02-28 21:26:16 UTC (rev 12591)
@@ -550,7 +550,7 @@
 {
 	unsigned char	 buff[4096];
 	size_t		 read, total, oldTotal;
-	int		 retval = 0;
+	int		 ret = 0;
 #ifdef HAVE_GETTIMEOFDAY
 	double		 kbps = -1.0;
 	struct timeval	 timeStamp, *startTime = (struct timeval *)tv;
@@ -567,21 +567,21 @@
 
 	total = oldTotal = 0;
 	while ((read = fread(buff, 1, sizeof(buff), filepstream)) > 0) {
-		int	ret;
-
 		if (rereadPlaylist_notify) {
 			rereadPlaylist_notify = 0;
-			printf("%s: SIGHUP signal received, will reread playlist after this file.\n",
-			       __progname);
+			if (!pezConfig->fileNameIsProgram)
+				printf("%s: SIGHUP signal received, will reread playlist after this file.\n",
+				       __progname);
 		}
 		if (skipTrack) {
 			skipTrack = 0;
-			retval = 2;
+			ret = 2;
 			break;
 		}
 
-		ret = shout_send(shout, buff, read);
-		if (ret != SHOUTERR_SUCCESS) {
+		shout_sync(shout);
+
+		if (shout_send(shout, buff, read) != SHOUTERR_SUCCESS) {
 			printf("%s: shout_send(): %s\n", __progname,
 			       shout_get_error(shout));
 			while (1) {
@@ -591,13 +591,11 @@
 				if (shout_open(shout) == SHOUTERR_SUCCESS) {
 					printf("%s: Reconnect to server successful.\n",
 					       __progname);
-					ret = shout_send(shout, buff, read);
-					if (ret != SHOUTERR_SUCCESS)
-						printf("%s: shout_send(): %s\n",
-						       __progname,
-						       shout_get_error(shout));
-					else
+					if (shout_send(shout, buff, read) == SHOUTERR_SUCCESS)
 						break;
+					printf("%s: shout_send(): %s\n",
+					       __progname,
+					       shout_get_error(shout));
 				} else {
 					printf("%s: Reconnect failed. Waiting 5 seconds ...\n",
 					       __progname);
@@ -610,8 +608,6 @@
 			}
 		}
 
-		shout_sync(shout);
-
 		total += read;
 		if (qFlag && vFlag) {
 #ifdef HAVE_GETTIMEOFDAY
@@ -620,10 +616,17 @@
 			unsigned int	hrs, mins, secs;
 #endif /* HAVE_GETTIMEOFDAY */
 
-			if (!isStdin && playlistMode)
-				printf("  [%4lu/%-4lu]",
-				       playlist_get_position(playlist),
-				       playlist_get_num_items(playlist));
+			if (!isStdin && playlistMode) {
+				if (pezConfig->fileNameIsProgram) {
+					char *tmp = xstrdup(pezConfig->fileName);
+					printf("  [%s]",
+					       basename(tmp));
+					xfree(tmp);
+				} else
+					printf("  [%4lu/%-4lu]",
+					       playlist_get_position(playlist),
+					       playlist_get_num_items(playlist));
+			}
 
 #ifdef HAVE_GETTIMEOFDAY
 			oldTime = (double)timeStamp.tv_sec
@@ -656,13 +659,13 @@
 	if (ferror(filepstream)) {
 		if (errno == EINTR) {
 			clearerr(filepstream);
-			retval = 1;
+			ret = 1;
 		} else
 			printf("%s: streamFile(): Error while reading '%s': %s\n",
 			       __progname, fileName, strerror(errno));
 	}
 
-	return (retval);
+	return (ret);
 }
 
 int
@@ -732,17 +735,23 @@
 	const char	*song;
 	char		 lastSong[PATH_MAX + 1];
 
-	/*
-	 * XXX: This preserves traditional behavior, however, rereading the
-	 *      playlist after each walkthrough seems a bit more logical.
-	 */
 	if (playlist == NULL) {
-		if ((playlist = playlist_read(fileName)) == NULL)
-			return (0);
+		if (pezConfig->fileNameIsProgram) {
+			if ((playlist = playlist_program(fileName)) == NULL)
+				return (0);
+		} else {
+			if ((playlist = playlist_read(fileName)) == NULL)
+				return (0);
+		}
 	} else
+		/*
+		 * XXX: This preserves traditional behavior, however,
+		 *      rereading the playlist after each walkthrough seems a
+		 *      bit more logical.
+		 */
 		playlist_rewind(playlist);
 
-	if (pezConfig->shuffle)
+	if (!pezConfig->fileNameIsProgram && pezConfig->shuffle)
 		playlist_shuffle(playlist);
 
 	while ((song = playlist_get_next(playlist)) != NULL) {
@@ -751,6 +760,8 @@
 			return (0);
 		if (rereadPlaylist) {
 			rereadPlaylist = rereadPlaylist_notify = 0;
+			if (pezConfig->fileNameIsProgram)
+				continue;
 			printf("%s: Rereading playlist\n", __progname);
 			if (!playlist_reread(&playlist))
 				return (0);
@@ -1095,13 +1106,18 @@
 		tmpFileName = xstrdup(pezConfig->fileName);
 		for (p = tmpFileName; *p != '\0'; p++)
 			*p = tolower((int)*p);
-		if (strrcmp(tmpFileName, ".m3u") == 0 ||
+		if (pezConfig->fileNameIsProgram ||
+		    strrcmp(tmpFileName, ".m3u") == 0 ||
 		    strrcmp(tmpFileName, ".txt") == 0)
 			playlistMode = 1;
 		else
 			playlistMode = 0;
 		xfree(tmpFileName);
 
+		if (vFlag && pezConfig->fileNameIsProgram)
+			printf("%s: Using program '%s' to get filenames for streaming\n",
+			       __progname, pezConfig->fileName);
+
 		ret = 1;
 		do {
 			if (playlistMode)
@@ -1114,6 +1130,9 @@
 		printf("%s: Connection to http://%s:%d%s failed: %s\n", __progname,
 		       host, port, mount, shout_get_error(shout));
 
+	if (vFlag)
+		printf("%s: Exiting ...\n", __progname);
+
 	shout_close(shout);
 
 	playlist_free(playlist);

Modified: trunk/ezstream/src/playlist.c
===================================================================
--- trunk/ezstream/src/playlist.c	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/src/playlist.c	2007-02-28 21:26:16 UTC (rev 12591)
@@ -21,6 +21,9 @@
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
@@ -33,6 +36,9 @@
 
 #ifdef WIN32
 # define snprintf	_snprintf
+# define popen		_popen
+# define pclose 	_pclose
+# define stat		_stat
 #endif
 
 #ifndef SIZE_T_MAX
@@ -47,21 +53,24 @@
 extern char    *__progname;
 
 struct playlist {
-	char	       *filename;
-	char	      **list;
-	size_t		size;
-	size_t		num;
-	size_t		index;
+	char	 *filename;
+	char	**list;
+	size_t	  size;
+	size_t	  num;
+	size_t	  index;
+	int	  program;
+	char	 *prog_track;
 };
 
 playlist_t *	playlist_create(const char *);
 int		playlist_add(playlist_t *, const char *);
 unsigned int	playlist_random(void);
+const char *	playlist_run_program(playlist_t *);
 
 playlist_t *
 playlist_create(const char *filename)
 {
-	playlist_t *pl;
+	playlist_t	*pl;
 
 	pl = xcalloc(1, sizeof(playlist_t));
 	pl->filename = xstrdup(filename);
@@ -145,16 +154,15 @@
 playlist_t *
 playlist_read(const char *filename)
 {
-	playlist_t     *pl;
-	unsigned long	line;
-	FILE	       *filep;
-	char		buf[PATH_MAX + 1];
+	playlist_t	*pl;
+	unsigned long	 line;
+	FILE		*filep;
+	char		 buf[PATH_MAX + 1];
 
 	pl = playlist_create(filename);
 
 	if ((filep = fopen(filename, "r")) == NULL) {
-		printf("%s: playlist_read(): %s: %s\n", __progname, filename,
-		       strerror(errno));
+		printf("%s: %s: %s\n", __progname, filename, strerror(errno));
 		playlist_free(pl);
 		return (NULL);
 	}
@@ -213,6 +221,42 @@
 	return (pl);
 }
 
+playlist_t *
+playlist_program(const char *filename)
+{
+	playlist_t	*pl;
+#ifdef HAVE_STAT
+	struct stat	 st;
+#else
+	FILE		*filep;
+#endif
+
+	pl = playlist_create(filename);
+	pl->program = 1;
+
+#ifdef HAVE_STAT
+	if (stat(filename, &st) == -1) {
+		printf("%s: %s: %s\n", __progname, filename, strerror(errno));
+		playlist_free(pl);
+		return (NULL);
+	}
+	if (!(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+		printf("%s: %s: Not an executable program\n", __progname, filename);
+		playlist_free(pl);
+		return (NULL);
+	}
+#else
+	if ((filep = fopen(filename, "r")) == NULL) {
+		printf("%s: %s: %s\n", __progname, filename, strerror(errno));
+		playlist_free(pl);
+		return (NULL);
+	}
+	fclose(filep);
+#endif /* HAVE_STAT */
+
+	return (pl);
+}
+
 void
 playlist_free(playlist_t *pl)
 {
@@ -239,6 +283,9 @@
 			pl->list = NULL;
 		}
 
+		if (pl->prog_track != NULL)
+			xfree(pl->prog_track);
+
 		xfree(pl);
 		pl = NULL;
 	}
@@ -253,6 +300,9 @@
 		exit(1);
 	}
 
+	if (pl->program)
+		return (playlist_run_program(pl));
+
 	return ((const char *)pl->list[pl->index++]);
 }
 
@@ -265,6 +315,9 @@
 		exit(1);
 	}
 
+	if (pl->program)
+		return (NULL);
+
 	return ((const char *)pl->list[pl->index]);
 }
 
@@ -277,6 +330,9 @@
 		exit(1);
 	}
 
+	if (pl->program)
+		return;
+
 	if (pl->list[pl->index] != NULL)
 		pl->index++;
 }
@@ -290,6 +346,9 @@
 		exit(1);
 	}
 
+	if (pl->program)
+		return (0);
+
 	return ((unsigned long)pl->num);
 }
 
@@ -302,6 +361,9 @@
 		exit(1);
 	}
 
+	if (pl->program)
+		return (0);
+
 	return ((unsigned long)pl->index);
 }
 
@@ -314,7 +376,7 @@
 		exit(1);
 	}
 
-	if (index > pl->num - 1)
+	if (pl->program || index > pl->num - 1)
 		return (0);
 
 	pl->index = (size_t)index;
@@ -333,7 +395,7 @@
 		exit(1);
 	}
 
-	if (pl->num == 0)
+	if (pl->program || pl->num == 0)
 		return (0);
 
 	for (i = 0; i < pl->num; i++) {
@@ -355,6 +417,9 @@
 		exit(1);
 	}
 
+	if (pl->program)
+		return;
+
 	pl->index = 0;
 }
 
@@ -371,6 +436,9 @@
 
 	pl = *plist;
 
+	if (pl->program)
+		return (0);
+
 	if ((new_pl = playlist_read(pl->filename)) == NULL)
 		return (0);
 
@@ -386,8 +454,8 @@
 void
 playlist_shuffle(playlist_t *pl)
 {
-	unsigned long	d, i, range;
-	char	       *temp;
+	unsigned long	 d, i, range;
+	char		*temp;
 
 	if (pl == NULL) {
 		printf("%s: playlist_shuffle(): Internal error: NULL argument\n",
@@ -395,7 +463,7 @@
 		exit(1);
 	}
 
-	if (pl->num < 2)
+	if (pl->program || pl->num < 2)
 		return;
 
 	for (i = 0; i < pl->num; i++) {
@@ -420,3 +488,66 @@
 		pl->list[i] = temp;
 	}
 }
+
+const char *
+playlist_run_program(playlist_t *pl)
+{
+	FILE	*filep;
+	char	 buf[PATH_MAX + 1];
+
+	if (pl == NULL) {
+		printf("%s: playlist_run_program(): Internal error: NULL argument\n",
+		       __progname);
+		exit(1);
+	}
+
+	if (!pl->program)
+		return (NULL);
+
+	fflush(NULL);
+	errno = 0;
+	if ((filep = popen(pl->filename, "r")) == NULL) {
+		printf("%s: playlist_run_program(): Error while executing '%s'",
+		       __progname, pl->filename);
+		/* popen() does not set errno reliably ... */
+		if (errno)
+			printf(": %s\n", strerror(errno));
+		else
+			printf("\n");
+		return (NULL);
+	}
+
+	if (fgets(buf, sizeof(buf), filep) == NULL) {
+		if (ferror(filep))
+			printf("%s: Error while reading output from program '%s': %s\n",
+			       __progname, pl->filename, strerror(errno));
+		pclose(filep);
+		printf("%s: FATAL: External program '%s' not (or no longer) usable.\n",
+		       __progname, pl->filename);
+		exit(1);
+	}
+
+	pclose(filep);
+
+	if (strlen(buf) == sizeof(buf) - 1) {
+		printf("%s: Output from program '%s' too long\n", __progname,
+		       pl->filename);
+		return (NULL);
+	}
+
+	if (buf[0] != '\0' && buf[strlen(buf) - 1] == '\n')
+		buf[strlen(buf) - 1] = '\0';
+	if (buf[0] != '\0' && buf[strlen(buf) - 1] == '\r')
+		buf[strlen(buf) - 1] = '\0';
+	if (buf[0] == '\0') {
+		printf("%s: Empty line received from program '%s'\n",
+		       __progname, pl->filename);
+		return (NULL);
+	}
+
+	if (pl->prog_track != NULL)
+		xfree(pl->prog_track);
+	pl->prog_track = xstrdup(buf);
+
+	return ((const char *)pl->prog_track);
+}

Modified: trunk/ezstream/src/playlist.h
===================================================================
--- trunk/ezstream/src/playlist.h	2007-02-28 15:35:07 UTC (rev 12590)
+++ trunk/ezstream/src/playlist.h	2007-02-28 21:26:16 UTC (rev 12591)
@@ -37,6 +37,13 @@
 playlist_t *	playlist_read(const char * /* filename */);
 
 /*
+ * For each call to playlist_get_next(), the specified program is run. This
+ * program is supposed to print one line to standard output, containing the
+ * path and filename of a media file.
+ */
+playlist_t *	playlist_program(const char * /* program name */);
+
+/*
  * Free all memory used by a playlist handler that was created with
  * playlist_read().
  */
@@ -49,6 +56,11 @@
 const char *	playlist_get_next(playlist_t *);
 
 /*
+ * The functions below work on playlist handlers obtained with playlist_read()
+ * and are no-ops (i.e. return failure) for playlists from playlist_program().
+ */
+
+/*
  * Get the next item in the playlist without moving on to the following entry.
  * Returns a NUL-terminated string of the next playlist entry, or NULL if the
  * currently playing song is the last one in the list.



More information about the commits mailing list