Blame src/control_generic.c

Packit c32a2d
/*
Packit c32a2d
	control_generic.c: control interface for frontends and real console warriors
Packit c32a2d
Packit c32a2d
	copyright 1997-99,2004-8 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 Andreas Neuhaus and Michael Hipp
Packit c32a2d
	reworked by Thomas Orgis - it was the entry point for eventually becoming maintainer...
Packit c32a2d
*/
Packit c32a2d
Packit c32a2d
#include "config.h"
Packit c32a2d
/* _BSD_SOURCE needed for setlinebuf, erm, but that's deprecated
Packit c32a2d
   so trying _DEFAULT_SOURCE */
Packit c32a2d
#ifndef _DEFAULT_SOURCE
Packit c32a2d
#define _DEFAULT_SOURCE
Packit c32a2d
#endif
Packit c32a2d
/* Defining that after _DEFAULT_SOURCE seems fine and is still
Packit c32a2d
   needed for older glibc. I guess I need a configure check
Packit c32a2d
   about setlinebuf()/setvbuf() if I really care about old
Packit c32a2d
   systems. */
Packit c32a2d
#ifndef _BSD_SOURCE
Packit c32a2d
#define _BSD_SOURCE
Packit c32a2d
#endif
Packit c32a2d
#include "compat.h"
Packit c32a2d
Packit c32a2d
#include "mpg123app.h"
Packit c32a2d
#include "out123.h"
Packit c32a2d
#include <stdarg.h>
Packit c32a2d
#include <ctype.h>
Packit c32a2d
#if !defined (WIN32) || defined (__CYGWIN__)
Packit c32a2d
#include <sys/wait.h>
Packit c32a2d
#include <sys/socket.h>
Packit c32a2d
#endif
Packit c32a2d
#include <errno.h>
Packit c32a2d
#include <string.h>
Packit c32a2d
Packit c32a2d
#include "common.h"
Packit c32a2d
#include "genre.h"
Packit c32a2d
#include "playlist.h"
Packit c32a2d
#include "audio.h"
Packit c32a2d
#define MODE_STOPPED 0
Packit c32a2d
#define MODE_PLAYING 1
Packit c32a2d
#define MODE_PAUSED 2
Packit c32a2d
Packit c32a2d
extern out123_handle *ao;
Packit c32a2d
Packit c32a2d
#ifdef FIFO
Packit c32a2d
#include <sys/stat.h>
Packit c32a2d
int control_file = STDIN_FILENO;
Packit c32a2d
#else
Packit c32a2d
#define control_file STDIN_FILENO
Packit c32a2d
#ifdef WANT_WIN32_FIFO
Packit c32a2d
#error Control interface does not work on win32 stdin
Packit c32a2d
#endif /* WANT_WIN32_FIFO */
Packit c32a2d
#endif
Packit c32a2d
FILE *outstream;
Packit c32a2d
static int mode = MODE_STOPPED;
Packit c32a2d
static int init = 0;
Packit c32a2d
Packit c32a2d
#include "debug.h"
Packit c32a2d
Packit c32a2d
void generic_sendmsg (const char *fmt, ...)
Packit c32a2d
{
Packit c32a2d
	va_list ap;
Packit c32a2d
	fprintf(outstream, "@");
Packit c32a2d
	va_start(ap, fmt);
Packit c32a2d
	vfprintf(outstream, fmt, ap);
Packit c32a2d
	va_end(ap);
Packit c32a2d
	fprintf(outstream, "\n");
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* Split up a number of lines separated by \n, \r, both or just zero byte
Packit c32a2d
   and print out each line with specified prefix. */
Packit c32a2d
static void generic_send_lines(const char* fmt, mpg123_string *inlines)
Packit c32a2d
{
Packit c32a2d
	size_t i;
Packit c32a2d
	int hadcr = 0, hadlf = 0;
Packit c32a2d
	char *lines = NULL;
Packit c32a2d
	char *line  = NULL;
Packit c32a2d
	size_t len = 0;
Packit c32a2d
Packit c32a2d
	if(inlines != NULL && inlines->fill)
Packit c32a2d
	{
Packit c32a2d
		lines = inlines->p;
Packit c32a2d
		len   = inlines->fill;
Packit c32a2d
	}
Packit c32a2d
	else return;
Packit c32a2d
Packit c32a2d
	line = lines;
Packit c32a2d
	for(i=0; i
Packit c32a2d
	{
Packit c32a2d
		if(lines[i] == '\n' || lines[i] == '\r' || lines[i] == 0)
Packit c32a2d
		{
Packit c32a2d
			char save = lines[i]; /* saving, changing, restoring a byte in the data */
Packit c32a2d
			if(save == '\n') ++hadlf;
Packit c32a2d
			if(save == '\r') ++hadcr;
Packit c32a2d
			if((hadcr || hadlf) && hadlf % 2 == 0 && hadcr % 2 == 0) line = "";
Packit c32a2d
Packit c32a2d
			if(line)
Packit c32a2d
			{
Packit c32a2d
				lines[i] = 0;
Packit c32a2d
				generic_sendmsg(fmt, line);
Packit c32a2d
				line = NULL;
Packit c32a2d
				lines[i] = save;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
		else
Packit c32a2d
		{
Packit c32a2d
			hadlf = hadcr = 0;
Packit c32a2d
			if(line == NULL) line = lines+i;
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void generic_sendstat (mpg123_handle *fr)
Packit c32a2d
{
Packit c32a2d
	off_t current_frame, frames_left;
Packit c32a2d
	double current_seconds, seconds_left;
Packit c32a2d
	if(!mpg123_position(fr, 0, out123_buffered(ao), &current_frame, &frames_left, &current_seconds, &seconds_left))
Packit c32a2d
	generic_sendmsg("F %"OFF_P" %"OFF_P" %3.2f %3.2f", (off_p)current_frame, (off_p)frames_left, current_seconds, seconds_left);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void generic_sendv1(mpg123_id3v1 *v1, const char *prefix)
Packit c32a2d
{
Packit c32a2d
	int i;
Packit c32a2d
	char info[125] = "";
Packit c32a2d
	memcpy(info,    v1->title,   30);
Packit c32a2d
	memcpy(info+30, v1->artist,  30);
Packit c32a2d
	memcpy(info+60, v1->album,   30);
Packit c32a2d
	memcpy(info+90, v1->year,     4);
Packit c32a2d
	memcpy(info+94, v1->comment, 30);
Packit c32a2d
Packit c32a2d
	for(i=0;i<124; ++i) if(info[i] == 0) info[i] = ' ';
Packit c32a2d
	info[i] = 0;
Packit c32a2d
	generic_sendmsg("%s ID3:%s%s", prefix, info, (v1->genre<=genre_count) ? genre_table[v1->genre] : "Unknown");
Packit c32a2d
	generic_sendmsg("%s ID3.genre:%i", prefix, v1->genre);
Packit c32a2d
	if(v1->comment[28] == 0 && v1->comment[29] != 0)
Packit c32a2d
	generic_sendmsg("%s ID3.track:%i", prefix, (unsigned char)v1->comment[29]);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void generic_sendinfoid3(mpg123_handle *mh)
Packit c32a2d
{
Packit c32a2d
	mpg123_id3v1 *v1;
Packit c32a2d
	mpg123_id3v2 *v2;
Packit c32a2d
	if(MPG123_OK != mpg123_id3(mh, &v1, &v2))
Packit c32a2d
	{
Packit c32a2d
		error1("Cannot get ID3 data: %s", mpg123_strerror(mh));
Packit c32a2d
		return;
Packit c32a2d
	}
Packit c32a2d
	if(v1 != NULL)
Packit c32a2d
	{
Packit c32a2d
		generic_sendv1(v1, "I");
Packit c32a2d
	}
Packit c32a2d
	if(v2 != NULL)
Packit c32a2d
	{
Packit c32a2d
		generic_send_lines("I ID3v2.title:%s",   v2->title);
Packit c32a2d
		generic_send_lines("I ID3v2.artist:%s",  v2->artist);
Packit c32a2d
		generic_send_lines("I ID3v2.album:%s",   v2->album);
Packit c32a2d
		generic_send_lines("I ID3v2.year:%s",    v2->year);
Packit c32a2d
		generic_send_lines("I ID3v2.comment:%s", v2->comment);
Packit c32a2d
		generic_send_lines("I ID3v2.genre:%s",   v2->genre);
Packit c32a2d
	}
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void generic_sendalltag(mpg123_handle *mh)
Packit c32a2d
{
Packit c32a2d
	mpg123_id3v1 *v1;
Packit c32a2d
	mpg123_id3v2 *v2;
Packit c32a2d
	generic_sendmsg("T {");
Packit c32a2d
	if(MPG123_OK != mpg123_id3(mh, &v1, &v2))
Packit c32a2d
	{
Packit c32a2d
		error1("Cannot get ID3 data: %s", mpg123_strerror(mh));
Packit c32a2d
		v2 = NULL;
Packit c32a2d
		v1 = NULL;
Packit c32a2d
	}
Packit c32a2d
	if(v1 != NULL) generic_sendv1(v1, "T");
Packit c32a2d
Packit c32a2d
	if(v2 != NULL)
Packit c32a2d
	{
Packit c32a2d
		size_t i;
Packit c32a2d
		for(i=0; i<v2->texts; ++i)
Packit c32a2d
		{
Packit c32a2d
			char id[5];
Packit c32a2d
			memcpy(id, v2->text[i].id, 4);
Packit c32a2d
			id[4] = 0;
Packit c32a2d
			generic_sendmsg("T ID3v2.%s:", id);
Packit c32a2d
			generic_send_lines("T =%s", &v2->text[i].text);
Packit c32a2d
		}
Packit c32a2d
		for(i=0; i<v2->extras; ++i)
Packit c32a2d
		{
Packit c32a2d
			char id[5];
Packit c32a2d
			memcpy(id, v2->extra[i].id, 4);
Packit c32a2d
			id[4] = 0;
Packit c32a2d
			generic_sendmsg("T ID3v2.%s desc(%s):",
Packit c32a2d
			        id,
Packit c32a2d
			        v2->extra[i].description.fill ? v2->extra[i].description.p : "" );
Packit c32a2d
			generic_send_lines("T =%s", &v2->extra[i].text);
Packit c32a2d
		}
Packit c32a2d
		for(i=0; i<v2->comments; ++i)
Packit c32a2d
		{
Packit c32a2d
			char id[5];
Packit c32a2d
			char lang[4];
Packit c32a2d
			memcpy(id, v2->comment_list[i].id, 4);
Packit c32a2d
			id[4] = 0;
Packit c32a2d
			memcpy(lang, v2->comment_list[i].lang, 3);
Packit c32a2d
			lang[3] = 0;
Packit c32a2d
			generic_sendmsg("T ID3v2.%s lang(%s) desc(%s):",
Packit c32a2d
			                id, lang,
Packit c32a2d
			                v2->comment_list[i].description.fill ? v2->comment_list[i].description.p : "");
Packit c32a2d
			generic_send_lines("T =%s", &v2->comment_list[i].text);
Packit c32a2d
		}
Packit c32a2d
	}
Packit c32a2d
	generic_sendmsg("T }");
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
void generic_sendinfo (char *filename)
Packit c32a2d
{
Packit c32a2d
	char *s, *t;
Packit c32a2d
	s = strrchr(filename, '/');
Packit c32a2d
	if (!s)
Packit c32a2d
		s = filename;
Packit c32a2d
	else
Packit c32a2d
		s++;
Packit c32a2d
	t = strrchr(s, '.');
Packit c32a2d
	if (t)
Packit c32a2d
		*t = 0;
Packit c32a2d
	generic_sendmsg("I %s", s);
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void generic_load(mpg123_handle *fr, char *arg, int state)
Packit c32a2d
{
Packit c32a2d
	out123_drop(ao);
Packit c32a2d
	if(mode != MODE_STOPPED)
Packit c32a2d
	{
Packit c32a2d
		close_track();
Packit c32a2d
		mode = MODE_STOPPED;
Packit c32a2d
	}
Packit c32a2d
	if(!open_track(arg))
Packit c32a2d
	{
Packit c32a2d
		generic_sendmsg("E Error opening stream: %s", arg);
Packit c32a2d
		generic_sendmsg("P 0");
Packit c32a2d
		return;
Packit c32a2d
	}
Packit c32a2d
	mpg123_seek(fr, 0, SEEK_SET); /* This finds ID3v2 at beginning. */
Packit c32a2d
	if(mpg123_meta_check(fr) & MPG123_NEW_ID3)
Packit c32a2d
	{
Packit c32a2d
		generic_sendinfoid3(fr);
Packit c32a2d
	}
Packit c32a2d
	else generic_sendinfo(arg);
Packit c32a2d
Packit c32a2d
	if(htd.icy_name.fill) generic_sendmsg("I ICY-NAME: %s", htd.icy_name.p);
Packit c32a2d
	if(htd.icy_url.fill)  generic_sendmsg("I ICY-URL: %s", htd.icy_url.p);
Packit c32a2d
Packit c32a2d
	mode = state;
Packit c32a2d
	init = 1;
Packit c32a2d
	generic_sendmsg(mode == MODE_PAUSED ? "P 1" : "P 2");
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
static void generic_loadlist(mpg123_handle *fr, char *arg)
Packit c32a2d
{
Packit c32a2d
	/* arguments are two: first the index to play, then the URL */
Packit c32a2d
	long entry;
Packit c32a2d
	long i = 0;
Packit c32a2d
	char *file = NULL;
Packit c32a2d
	char *thefile = NULL;
Packit c32a2d
Packit c32a2d
	/* I feel retarted with string parsing outside Perl. */
Packit c32a2d
	while(*arg && isspace(*arg)) ++arg;
Packit c32a2d
	entry = atol(arg);
Packit c32a2d
	while(*arg && !isspace(*arg)) ++arg;
Packit c32a2d
	while(*arg && isspace(*arg)) ++arg;
Packit c32a2d
	if(!*arg)
Packit c32a2d
	{
Packit c32a2d
		generic_sendmsg("E empty list name");
Packit c32a2d
		return;
Packit c32a2d
	}
Packit c32a2d
Packit c32a2d
	/* Now got the plain playlist path in arg. On to evil manupulation of mpg123's playlist code. */
Packit c32a2d
	param.listname = arg;
Packit c32a2d
	param.listentry = 0; /* The playlist shall not filter. */
Packit c32a2d
	param.loop = 1;
Packit c32a2d
	param.shuffle = 0;
Packit c32a2d
	prepare_playlist(0, NULL);
Packit c32a2d
	while((file = get_next_file()))
Packit c32a2d
	{
Packit c32a2d
		++i;
Packit c32a2d
		/* semantics: 0 brings you to the last track */
Packit c32a2d
		if(entry == 0 || entry == i) thefile = file;
Packit c32a2d
Packit c32a2d
		generic_sendmsg("I LISTENTRY %li: %s", i, file);
Packit c32a2d
	}
Packit c32a2d
	if(!i) generic_sendmsg("I LIST EMPTY");
Packit c32a2d
Packit c32a2d
	/* If we have something to play, play it. */
Packit c32a2d
	if(thefile) generic_load(fr, thefile, MODE_PLAYING);
Packit c32a2d
Packit c32a2d
	free_playlist(); /* Free memory after it is not needed anymore. */
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
int control_generic (mpg123_handle *fr)
Packit c32a2d
{
Packit c32a2d
	struct timeval tv;
Packit c32a2d
	fd_set fds;
Packit c32a2d
	int n;
Packit c32a2d
Packit c32a2d
	/* ThOr */
Packit c32a2d
	char alive = 1;
Packit c32a2d
	char silent = 0;
Packit c32a2d
Packit c32a2d
	/* responses to stderr for frontends needing audio data from stdout */
Packit c32a2d
	if (param.remote_err)
Packit c32a2d
 		outstream = stderr;
Packit c32a2d
 	else
Packit c32a2d
 		outstream = stdout;
Packit c32a2d
 		
Packit c32a2d
#ifndef WIN32
Packit c32a2d
 	setlinebuf(outstream);
Packit c32a2d
#else /* perhaps just use setvbuf as it's C89 */
Packit c32a2d
	/*
Packit c32a2d
	fprintf(outstream, "You are on Win32 and want to use the control interface... tough luck: We need a replacement for select on STDIN first.\n");
Packit c32a2d
	return 0;
Packit c32a2d
	setvbuf(outstream, (char*)NULL, _IOLBF, 0);
Packit c32a2d
	*/
Packit c32a2d
#endif
Packit c32a2d
	/* the command behaviour is different, so is the ID */
Packit c32a2d
	/* now also with version for command availability */
Packit c32a2d
	fprintf(outstream, "@R MPG123 (ThOr) v8\n");
Packit c32a2d
#ifdef FIFO
Packit c32a2d
	if(param.fifo)
Packit c32a2d
	{
Packit c32a2d
		if(param.fifo[0] == 0)
Packit c32a2d
		{
Packit c32a2d
			error("You wanted an empty FIFO name??");
Packit c32a2d
			return 1;
Packit c32a2d
		}
Packit c32a2d
#ifndef WANT_WIN32_FIFO
Packit c32a2d
		unlink(param.fifo);
Packit c32a2d
		if(mkfifo(param.fifo, 0666) == -1)
Packit c32a2d
		{
Packit c32a2d
			error2("Failed to create FIFO at %s (%s)", param.fifo, strerror(errno));
Packit c32a2d
			return 1;
Packit c32a2d
		}
Packit c32a2d
		debug("going to open named pipe ... blocking until someone gives command");
Packit c32a2d
#endif /* WANT_WIN32_FIFO */
Packit c32a2d
#ifdef WANT_WIN32_FIFO
Packit c32a2d
		control_file = win32_fifo_mkfifo(param.fifo);
Packit c32a2d
#else
Packit c32a2d
		control_file = open(param.fifo,O_RDONLY);
Packit c32a2d
#endif /* WANT_WIN32_FIFO */
Packit c32a2d
		debug("opened");
Packit c32a2d
	}
Packit c32a2d
#endif
Packit c32a2d
Packit c32a2d
	while (alive)
Packit c32a2d
	{
Packit c32a2d
		tv.tv_sec = 0;
Packit c32a2d
		tv.tv_usec = 0;
Packit c32a2d
		FD_ZERO(&fds);
Packit c32a2d
		FD_SET(control_file, &fds);
Packit c32a2d
		/* play frame if no command needs to be processed */
Packit c32a2d
		if (mode == MODE_PLAYING) {
Packit c32a2d
#ifdef WANT_WIN32_FIFO
Packit c32a2d
			n = win32_fifo_read_peek(&tv;;
Packit c32a2d
#else
Packit c32a2d
			n = select(32, &fds, NULL, NULL, &tv;;
Packit c32a2d
#endif
Packit c32a2d
			if (n == 0) {
Packit c32a2d
				if (!play_frame())
Packit c32a2d
				{
Packit c32a2d
					out123_pause(ao);
Packit c32a2d
					/* When the track ended, user may want to keep it open (to seek back),
Packit c32a2d
					   so there is a decision between stopping and pausing at the end. */
Packit c32a2d
					if(param.keep_open)
Packit c32a2d
					{
Packit c32a2d
						mode = MODE_PAUSED;
Packit c32a2d
						/* Hm, buffer should be stopped already, shouldn't it? */
Packit c32a2d
						generic_sendmsg("P 1");
Packit c32a2d
					}
Packit c32a2d
					else
Packit c32a2d
					{
Packit c32a2d
						mode = MODE_STOPPED;
Packit c32a2d
						close_track();
Packit c32a2d
						generic_sendmsg("P 0");
Packit c32a2d
					}
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
				if (init) {
Packit c32a2d
					print_remote_header(fr);
Packit c32a2d
					init = 0;
Packit c32a2d
				}
Packit c32a2d
				if(silent == 0)
Packit c32a2d
				{
Packit c32a2d
					generic_sendstat(fr);
Packit c32a2d
					if(mpg123_meta_check(fr) & MPG123_NEW_ICY)
Packit c32a2d
					{
Packit c32a2d
						char *meta;
Packit c32a2d
						if(mpg123_icy(fr, &meta) == MPG123_OK)
Packit c32a2d
						generic_sendmsg("I ICY-META: %s", meta != NULL ? meta : "<nil>");
Packit c32a2d
					}
Packit c32a2d
				}
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
		else {
Packit c32a2d
			/* wait for command */
Packit c32a2d
			while (1) {
Packit c32a2d
#ifdef WANT_WIN32_FIFO
Packit c32a2d
				n = win32_fifo_read_peek(NULL);
Packit c32a2d
#else
Packit c32a2d
				n = select(32, &fds, NULL, NULL, NULL);
Packit c32a2d
#endif
Packit c32a2d
				if (n > 0)
Packit c32a2d
					break;
Packit c32a2d
			}
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		/*  on error */
Packit c32a2d
		if (n < 0) {
Packit c32a2d
			fprintf(stderr, "Error waiting for command: %s\n", strerror(errno));
Packit c32a2d
			return 1;
Packit c32a2d
		}
Packit c32a2d
Packit c32a2d
		/* read & process commands */
Packit c32a2d
		if (n > 0)
Packit c32a2d
		{
Packit c32a2d
			short int len = 1; /* length of buffer */
Packit c32a2d
			char *cmd, *arg; /* variables for parsing, */
Packit c32a2d
			char *comstr = NULL; /* gcc thinks that this could be used uninitialited... */ 
Packit c32a2d
			char buf[REMOTE_BUFFER_SIZE];
Packit c32a2d
			short int counter;
Packit c32a2d
			char *next_comstr = buf; /* have it initialized for first command */
Packit c32a2d
Packit c32a2d
			/* read as much as possible, maybe multiple commands */
Packit c32a2d
			/* When there is nothing to read (EOF) or even an error, it is the end */
Packit c32a2d
#ifdef WANT_WIN32_FIFO
Packit c32a2d
			len = win32_fifo_read(buf,REMOTE_BUFFER_SIZE);
Packit c32a2d
#else
Packit c32a2d
			len = read(control_file, buf, REMOTE_BUFFER_SIZE);
Packit c32a2d
#endif
Packit c32a2d
			if(len < 1)
Packit c32a2d
			{
Packit c32a2d
#ifdef FIFO
Packit c32a2d
				if(len == 0 && param.fifo)
Packit c32a2d
				{
Packit c32a2d
					debug("fifo ended... reopening");
Packit c32a2d
#ifdef WANT_WIN32_FIFO
Packit c32a2d
					win32_fifo_mkfifo(param.fifo);
Packit c32a2d
#else
Packit c32a2d
					close(control_file);
Packit c32a2d
					control_file = open(param.fifo,O_RDONLY|O_NONBLOCK);
Packit c32a2d
#endif
Packit c32a2d
					if(control_file < 0){ error1("open of fifo failed... %s", strerror(errno)); break; }
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
#endif
Packit c32a2d
				if(len < 0) error1("command read error: %s", strerror(errno));
Packit c32a2d
				break;
Packit c32a2d
			}
Packit c32a2d
Packit c32a2d
			debug1("read %i bytes of commands", len);
Packit c32a2d
			/* one command on a line - separation by \n -> C strings in a row */
Packit c32a2d
			for(counter = 0; counter < len; ++counter)
Packit c32a2d
			{
Packit c32a2d
				/* line end is command end */
Packit c32a2d
				if( (buf[counter] == '\n') || (buf[counter] == '\r') )
Packit c32a2d
				{
Packit c32a2d
					debug1("line end at counter=%i", counter);
Packit c32a2d
					buf[counter] = 0; /* now it's a properly ending C string */
Packit c32a2d
					comstr = next_comstr;
Packit c32a2d
Packit c32a2d
					/* skip the additional line ender of \r\n or \n\r */
Packit c32a2d
					if( (counter < (len - 1)) && ((buf[counter+1] == '\n') || (buf[counter+1] == '\r')) ) buf[++counter] = 0;
Packit c32a2d
Packit c32a2d
					/* next "real" char is first of next command */
Packit c32a2d
					next_comstr = buf + counter+1;
Packit c32a2d
Packit c32a2d
					/* directly process the command now */
Packit c32a2d
					debug1("interpreting command: %s", comstr);
Packit c32a2d
				if(strlen(comstr) == 0) continue;
Packit c32a2d
Packit c32a2d
				/* PAUSE */
Packit c32a2d
				if (!strcasecmp(comstr, "P") || !strcasecmp(comstr, "PAUSE")) {
Packit c32a2d
					if(mode != MODE_STOPPED)
Packit c32a2d
					{	
Packit c32a2d
						if (mode == MODE_PLAYING) {
Packit c32a2d
							mode = MODE_PAUSED;
Packit c32a2d
							out123_pause(ao);
Packit c32a2d
							generic_sendmsg("P 1");
Packit c32a2d
						} else {
Packit c32a2d
							mode = MODE_PLAYING;
Packit c32a2d
							out123_continue(ao);
Packit c32a2d
							generic_sendmsg("P 2");
Packit c32a2d
						}
Packit c32a2d
					} else generic_sendmsg("P 0");
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				/* STOP */
Packit c32a2d
				if (!strcasecmp(comstr, "S") || !strcasecmp(comstr, "STOP")) {
Packit c32a2d
					if (mode != MODE_STOPPED) {
Packit c32a2d
						/* Do we want to drop here? */
Packit c32a2d
						out123_drop(ao);
Packit c32a2d
						out123_pause(ao);
Packit c32a2d
						close_track();
Packit c32a2d
						mode = MODE_STOPPED;
Packit c32a2d
						generic_sendmsg("P 0");
Packit c32a2d
					} else generic_sendmsg("P 0");
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				/* SILENCE */
Packit c32a2d
				if(!strcasecmp(comstr, "SILENCE")) {
Packit c32a2d
					silent = 1;
Packit c32a2d
					generic_sendmsg("silence");
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				if(!strcasecmp(comstr, "T") || !strcasecmp(comstr, "TAG")) {
Packit c32a2d
					generic_sendalltag(fr);
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				if(!strcasecmp(comstr, "SCAN"))
Packit c32a2d
				{
Packit c32a2d
					if(mode != MODE_STOPPED)
Packit c32a2d
					{
Packit c32a2d
						if(mpg123_scan(fr) == MPG123_OK)
Packit c32a2d
						generic_sendmsg("SCAN done");
Packit c32a2d
						else
Packit c32a2d
						generic_sendmsg("E %s", mpg123_strerror(fr));
Packit c32a2d
					}
Packit c32a2d
					else generic_sendmsg("E No track loaded!");
Packit c32a2d
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				if(!strcasecmp(comstr, "SAMPLE"))
Packit c32a2d
				{
Packit c32a2d
					off_t pos = mpg123_tell(fr);
Packit c32a2d
					off_t len = mpg123_length(fr);
Packit c32a2d
					/* I need to have portable printf specifiers that do not truncate the type... more autoconf... */
Packit c32a2d
					if(len < 0) generic_sendmsg("E %s", mpg123_strerror(fr));
Packit c32a2d
					else generic_sendmsg("SAMPLE %li %li", (long)pos, (long)len);
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				if(!strcasecmp(comstr, "FORMAT"))
Packit c32a2d
				{
Packit c32a2d
					long rate;
Packit c32a2d
					int ch;
Packit c32a2d
					int ret = mpg123_getformat2(fr, &rate, &ch, NULL, 0);
Packit c32a2d
					/* I need to have portable printf specifiers that do not truncate the type... more autoconf... */
Packit c32a2d
					if(ret < 0) generic_sendmsg("E %s", mpg123_strerror(fr));
Packit c32a2d
					else generic_sendmsg("FORMAT %li %i", rate, ch);
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				if(!strcasecmp(comstr, "SHOWEQ"))
Packit c32a2d
				{
Packit c32a2d
					int i;
Packit c32a2d
					generic_sendmsg("SHOWEQ {");
Packit c32a2d
					for(i=0; i<32; ++i)
Packit c32a2d
					{
Packit c32a2d
						generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_LEFT, i, mpg123_geteq(fr, MPG123_LEFT, i));
Packit c32a2d
						generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_RIGHT, i, mpg123_geteq(fr, MPG123_RIGHT, i));
Packit c32a2d
					}
Packit c32a2d
					generic_sendmsg("SHOWEQ }");
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				if(!strcasecmp(comstr, "STATE"))
Packit c32a2d
				{
Packit c32a2d
					long val;
Packit c32a2d
					generic_sendmsg("STATE {");
Packit c32a2d
					/* Get some state information bits and display them. */
Packit c32a2d
					if(mpg123_getstate(fr, MPG123_ACCURATE, &val, NULL) == MPG123_OK)
Packit c32a2d
					generic_sendmsg("STATE accurate %li", val);
Packit c32a2d
Packit c32a2d
					generic_sendmsg("STATE }");
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				/* QUIT */
Packit c32a2d
				if (!strcasecmp(comstr, "Q") || !strcasecmp(comstr, "QUIT")){
Packit c32a2d
					alive = FALSE; continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				/* some HELP */
Packit c32a2d
				if (!strcasecmp(comstr, "H") || !strcasecmp(comstr, "HELP")) {
Packit c32a2d
					generic_sendmsg("H {");
Packit c32a2d
					generic_sendmsg("H HELP/H: command listing (LONG/SHORT forms), command case insensitve");
Packit c32a2d
					generic_sendmsg("H LOAD/L <trackname>: load and start playing resource <trackname>");
Packit c32a2d
					generic_sendmsg("H LOADPAUSED/LP <trackname>: load but do not start playing resource <trackname>");
Packit c32a2d
					generic_sendmsg("H LOADLIST/LL <entry> <url>: load a playlist from given <url>, and display its entries, optionally load and play one of these specificed by the integer <entry> (<0: just list, 0: play last track, >0:play track with that position in list)");
Packit c32a2d
					generic_sendmsg("H PAUSE/P: pause playback");
Packit c32a2d
					generic_sendmsg("H STOP/S: stop playback (closes file)");
Packit c32a2d
					generic_sendmsg("H JUMP/J <frame>|<+offset>|<-offset>|<[+|-]seconds>s: jump to mpeg frame <frame> or change position by offset, same in seconds if number followed by \"s\"");
Packit c32a2d
					generic_sendmsg("H VOLUME/V <percent>: set volume in % (0..100...); float value");
Packit c32a2d
					generic_sendmsg("H RVA off|(mix|radio)|(album|audiophile): set rva mode");
Packit c32a2d
					generic_sendmsg("H EQ/E <channel> <band> <value>: set equalizer value for frequency band 0 to 31 on channel %i (left) or %i (right) or %i (both)", MPG123_LEFT, MPG123_RIGHT, MPG123_LR);
Packit c32a2d
					generic_sendmsg("H EQFILE <filename>: load EQ settings from a file");
Packit c32a2d
					generic_sendmsg("H SHOWEQ: show all equalizer settings (as <channel> <band> <value> lines in a SHOWEQ block (like TAG))");
Packit c32a2d
					generic_sendmsg("H SEEK/K <sample>|<+offset>|<-offset>: jump to output sample position <samples> or change position by offset");
Packit c32a2d
					generic_sendmsg("H SCAN: scan through the file, building seek index");
Packit c32a2d
					generic_sendmsg("H SAMPLE: print out the sample position and total number of samples");
Packit c32a2d
					generic_sendmsg("H FORMAT: print out sampling rate in Hz and channel count");
Packit c32a2d
					generic_sendmsg("H SEQ <bass> <mid> <treble>: simple eq setting...");
Packit c32a2d
					generic_sendmsg("H PITCH <[+|-]value>: adjust playback speed (+0.01 is 1 %% faster)");
Packit c32a2d
					generic_sendmsg("H SILENCE: be silent during playback (meaning silence in text form)");
Packit c32a2d
					generic_sendmsg("H STATE: Print auxiliary state info in several lines (just try it to see what info is there).");
Packit c32a2d
					generic_sendmsg("H TAG/T: Print all available (ID3) tag info, for ID3v2 that gives output of all collected text fields, using the ID3v2.3/4 4-character names. NOTE: ID3v2 data will be deleted on non-forward seeks.");
Packit c32a2d
					generic_sendmsg("H    The output is multiple lines, begin marked by \"@T {\", end by \"@T }\".");
Packit c32a2d
					generic_sendmsg("H    ID3v1 data is like in the @I info lines (see below), just with \"@T\" in front.");
Packit c32a2d
					generic_sendmsg("H    An ID3v2 data field is introduced via ([ ... ] means optional):");
Packit c32a2d
					generic_sendmsg("H     @T ID3v2.<NAME>[ [lang(<LANG>)] desc(<description>)]:");
Packit c32a2d
					generic_sendmsg("H    The lines of data follow with \"=\" prefixed:");
Packit c32a2d
					generic_sendmsg("H     @T =<one line of content in UTF-8 encoding>");
Packit c32a2d
					generic_sendmsg("H meaning of the @S stream info:");
Packit c32a2d
					generic_sendmsg("H %s", remote_header_help);
Packit c32a2d
					generic_sendmsg("H The @I lines after loading a track give some ID3 info, the format:");
Packit c32a2d
					generic_sendmsg("H      @I ID3:artist  album  year  comment genretext");
Packit c32a2d
					generic_sendmsg("H     where artist,album and comment are exactly 30 characters each, year is 4 characters, genre text unspecified.");
Packit c32a2d
					generic_sendmsg("H     You will encounter \"@I ID3.genre:<number>\" and \"@I ID3.track:<number>\".");
Packit c32a2d
					generic_sendmsg("H     Then, there is an excerpt of ID3v2 info in the structure");
Packit c32a2d
					generic_sendmsg("H      @I ID3v2.title:Blabla bla Bla");
Packit c32a2d
					generic_sendmsg("H     for every line of the \"title\" data field. Likewise for other fields (author, album, etc).");
Packit c32a2d
					generic_sendmsg("H }");
Packit c32a2d
					continue;
Packit c32a2d
				}
Packit c32a2d
Packit c32a2d
				/* commands with arguments */
Packit c32a2d
				cmd = NULL;
Packit c32a2d
				arg = NULL;
Packit c32a2d
				cmd = strtok(comstr," \t"); /* get the main command */
Packit c32a2d
				arg = strtok(NULL,""); /* get the args */
Packit c32a2d
Packit c32a2d
				if (cmd && strlen(cmd) && arg && strlen(arg))
Packit c32a2d
				{
Packit c32a2d
#ifndef NO_EQUALIZER
Packit c32a2d
					/* Simple EQ: SEQ <BASS> <MID> <TREBLE>  */
Packit c32a2d
					if (!strcasecmp(cmd, "SEQ")) {
Packit c32a2d
						double b,m,t;
Packit c32a2d
						int cn;
Packit c32a2d
						if(sscanf(arg, "%lf %lf %lf", &b, &m, &t) == 3)
Packit c32a2d
						{
Packit c32a2d
							/* Consider adding mpg123_seq()... but also, on could define a nicer courve for that. */
Packit c32a2d
							if ((t >= 0) && (t <= 3))
Packit c32a2d
							for(cn=0; cn < 1; ++cn)	mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, b);
Packit c32a2d
Packit c32a2d
							if ((m >= 0) && (m <= 3))
Packit c32a2d
							for(cn=1; cn < 2; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, m);
Packit c32a2d
Packit c32a2d
							if ((b >= 0) && (b <= 3))
Packit c32a2d
							for(cn=2; cn < 32; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, t);
Packit c32a2d
Packit c32a2d
							generic_sendmsg("bass: %f mid: %f treble: %f", b, m, t);
Packit c32a2d
						}
Packit c32a2d
						else generic_sendmsg("E invalid arguments for SEQ: %s", arg);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
Packit c32a2d
					/* Equalizer control :) (JMG) */
Packit c32a2d
					if (!strcasecmp(cmd, "E") || !strcasecmp(cmd, "EQ")) {
Packit c32a2d
						double e; /* ThOr: equalizer is of type real... whatever that is */
Packit c32a2d
						int c, v;
Packit c32a2d
						/*generic_sendmsg("%s",updown);*/
Packit c32a2d
						if(sscanf(arg, "%i %i %lf", &c, &v, &e) == 3)
Packit c32a2d
						{
Packit c32a2d
							if(mpg123_eq(fr, c, v, e) == MPG123_OK)
Packit c32a2d
							generic_sendmsg("%i : %i : %f", c, v, e);
Packit c32a2d
							else
Packit c32a2d
							generic_sendmsg("E failed to set eq: %s", mpg123_strerror(fr));
Packit c32a2d
						}
Packit c32a2d
						else generic_sendmsg("E invalid arguments for EQ: %s", arg);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
Packit c32a2d
					if(!strcasecmp(cmd, "EQFILE"))
Packit c32a2d
					{
Packit c32a2d
						equalfile = arg;
Packit c32a2d
						if(load_equalizer(fr) == 0)
Packit c32a2d
						generic_sendmsg("EQFILE done");
Packit c32a2d
						else
Packit c32a2d
						generic_sendmsg("E failed to parse given eq file");
Packit c32a2d
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
#endif
Packit c32a2d
					/* SEEK to a sample offset */
Packit c32a2d
					if(!strcasecmp(cmd, "K") || !strcasecmp(cmd, "SEEK"))
Packit c32a2d
					{
Packit c32a2d
						off_t soff;
Packit c32a2d
						off_t oldpos;
Packit c32a2d
						off_t newpos;
Packit c32a2d
						char *spos = arg;
Packit c32a2d
						int whence = SEEK_SET;
Packit c32a2d
						if(mode == MODE_STOPPED)
Packit c32a2d
						{
Packit c32a2d
							generic_sendmsg("E No track loaded!");
Packit c32a2d
							continue;
Packit c32a2d
						}
Packit c32a2d
						oldpos = mpg123_tell(fr);
Packit c32a2d
Packit c32a2d
						soff = (off_t) atobigint(spos);
Packit c32a2d
						if(spos[0] == '-' || spos[0] == '+') whence = SEEK_CUR;
Packit c32a2d
						if(0 > (soff = mpg123_seek(fr, soff, whence)))
Packit c32a2d
						{
Packit c32a2d
							generic_sendmsg("E Error while seeking: %s", mpg123_strerror(fr));
Packit c32a2d
							mpg123_seek(fr, 0, SEEK_SET);
Packit c32a2d
						}
Packit c32a2d
						out123_drop(ao);
Packit c32a2d
Packit c32a2d
						newpos = mpg123_tell(fr);
Packit c32a2d
						if(newpos <= oldpos) mpg123_meta_free(fr);
Packit c32a2d
Packit c32a2d
						generic_sendmsg("K %"OFF_P, (off_p)newpos);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
					/* JUMP */
Packit c32a2d
					if (!strcasecmp(cmd, "J") || !strcasecmp(cmd, "JUMP")) {
Packit c32a2d
						char *spos;
Packit c32a2d
						off_t offset;
Packit c32a2d
						off_t oldpos;
Packit c32a2d
						double secs;
Packit c32a2d
Packit c32a2d
						spos = arg;
Packit c32a2d
						if(mode == MODE_STOPPED)
Packit c32a2d
						{
Packit c32a2d
							generic_sendmsg("E No track loaded!");
Packit c32a2d
							continue;
Packit c32a2d
						}
Packit c32a2d
						oldpos = framenum;
Packit c32a2d
Packit c32a2d
						if(spos[strlen(spos)-1] == 's' && sscanf(arg, "%lf", &secs) == 1) offset = mpg123_timeframe(fr, secs);
Packit c32a2d
						else offset = atol(spos);
Packit c32a2d
						/* totally replaced that stuff - it never fully worked
Packit c32a2d
						   a bit usure about why +pos -> spos+1 earlier... */
Packit c32a2d
						if (spos[0] == '-' || spos[0] == '+') offset += framenum;
Packit c32a2d
Packit c32a2d
						if(0 > (framenum = mpg123_seek_frame(fr, offset, SEEK_SET)))
Packit c32a2d
						{
Packit c32a2d
							generic_sendmsg("E Error while seeking");
Packit c32a2d
							mpg123_seek_frame(fr, 0, SEEK_SET);
Packit c32a2d
						}
Packit c32a2d
						out123_drop(ao);
Packit c32a2d
Packit c32a2d
						if(framenum <= oldpos) mpg123_meta_free(fr);
Packit c32a2d
						generic_sendmsg("J %d", framenum);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
Packit c32a2d
					/* VOLUME in percent */
Packit c32a2d
					if(!strcasecmp(cmd, "V") || !strcasecmp(cmd, "VOLUME"))
Packit c32a2d
					{
Packit c32a2d
						double v;
Packit c32a2d
						mpg123_volume(fr, atof(arg)/100);
Packit c32a2d
						mpg123_getvolume(fr, &v, NULL, NULL); /* Necessary? */
Packit c32a2d
						generic_sendmsg("V %f%%", v * 100);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
Packit c32a2d
					/* PITCH (playback speed) in percent */
Packit c32a2d
					if(!strcasecmp(cmd, "PITCH"))
Packit c32a2d
					{
Packit c32a2d
						double p;
Packit c32a2d
						if(sscanf(arg, "%lf", &p) == 1)
Packit c32a2d
						{
Packit c32a2d
							set_pitch(fr, ao, p);
Packit c32a2d
							generic_sendmsg("PITCH %f", param.pitch);
Packit c32a2d
						}
Packit c32a2d
						else generic_sendmsg("E invalid arguments for PITCH: %s", arg);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
Packit c32a2d
					/* RVA mode */
Packit c32a2d
					if(!strcasecmp(cmd, "RVA"))
Packit c32a2d
					{
Packit c32a2d
						if(!strcasecmp(arg, "off")) param.rva = MPG123_RVA_OFF;
Packit c32a2d
						else if(!strcasecmp(arg, "mix") || !strcasecmp(arg, "radio")) param.rva = MPG123_RVA_MIX;
Packit c32a2d
						else if(!strcasecmp(arg, "album") || !strcasecmp(arg, "audiophile")) param.rva = MPG123_RVA_ALBUM;
Packit c32a2d
						mpg123_volume_change(fr, 0.);
Packit c32a2d
						generic_sendmsg("RVA %s", rva_name[param.rva]);
Packit c32a2d
						continue;
Packit c32a2d
					}
Packit c32a2d
Packit c32a2d
					/* LOAD - actually play */
Packit c32a2d
					if (!strcasecmp(cmd, "L") || !strcasecmp(cmd, "LOAD")){ generic_load(fr, arg, MODE_PLAYING); continue; }
Packit c32a2d
Packit c32a2d
					if (!strcasecmp(cmd, "LL") || !strcasecmp(cmd, "LOADLIST")){ generic_loadlist(fr, arg); continue; }
Packit c32a2d
Packit c32a2d
					/* LOADPAUSED */
Packit c32a2d
					if (!strcasecmp(cmd, "LP") || !strcasecmp(cmd, "LOADPAUSED")){ generic_load(fr, arg, MODE_PAUSED); continue; }
Packit c32a2d
Packit c32a2d
					/* no command matched */
Packit c32a2d
					generic_sendmsg("E Unknown command: %s", cmd); /* unknown command */
Packit c32a2d
				} /* end commands with arguments */
Packit c32a2d
				else generic_sendmsg("E Unknown command or no arguments: %s", comstr); /* unknown command */
Packit c32a2d
Packit c32a2d
				} /* end of single command processing */
Packit c32a2d
			} /* end of scanning the command buffer */
Packit c32a2d
Packit c32a2d
			/*
Packit c32a2d
			   when last command had no \n... should I discard it?
Packit c32a2d
			   Ideally, I should remember the part and wait for next
Packit c32a2d
				 read() to get the rest up to a \n. But that can go
Packit c32a2d
				 to infinity. Too long commands too quickly are just
Packit c32a2d
				 bad. Cannot/Won't change that. So, discard the unfinished
Packit c32a2d
				 command and have fingers crossed that the rest of this
Packit c32a2d
				 unfinished one qualifies as "unknown". 
Packit c32a2d
			*/
Packit c32a2d
			if(buf[len-1] != 0)
Packit c32a2d
			{
Packit c32a2d
				char lasti = buf[len-1];
Packit c32a2d
				buf[len-1] = 0;
Packit c32a2d
				generic_sendmsg("E Unfinished command: %s%c", comstr, lasti);
Packit c32a2d
			}
Packit c32a2d
		} /* end command reading & processing */
Packit c32a2d
	} /* end main (alive) loop */
Packit c32a2d
	debug("going to end");
Packit c32a2d
	/* quit gracefully */
Packit c32a2d
	debug("closing control");
Packit c32a2d
#ifdef FIFO
Packit c32a2d
#if WANT_WIN32_FIFO
Packit c32a2d
	win32_fifo_close();
Packit c32a2d
#else
Packit c32a2d
	close(control_file); /* be it FIFO or STDIN */
Packit c32a2d
	if(param.fifo) unlink(param.fifo);
Packit c32a2d
#endif /* WANT_WIN32_FIFO */
Packit c32a2d
#endif
Packit c32a2d
	debug("control_generic returning");
Packit c32a2d
	return 0;
Packit c32a2d
}
Packit c32a2d
Packit c32a2d
/* EOF */