author | Kalle Wallin <kaw@linux.se> | |
Fri, 19 Mar 2004 13:26:18 +0000 (13:26 +0000) | ||
committer | Kalle Wallin <kaw@linux.se> | |
Fri, 19 Mar 2004 13:26:18 +0000 (13:26 +0000) |
38 files changed:
AUTHORS | [new file with mode: 0644] | patch | blob |
ChangeLog | [new file with mode: 0644] | patch | blob |
Makefile.am | [new file with mode: 0644] | patch | blob |
NEWS | [new file with mode: 0644] | patch | blob |
README | [new file with mode: 0644] | patch | blob |
TODO | [new file with mode: 0644] | patch | blob |
bootstrap.sh | [new file with mode: 0755] | patch | blob |
cleanup.sh | [new file with mode: 0755] | patch | blob |
command.c | [new file with mode: 0644] | patch | blob |
command.h | [new file with mode: 0644] | patch | blob |
configure.ac | [new file with mode: 0644] | patch | blob |
doc/ncmpc.1 | [new file with mode: 0644] | patch | blob |
libmpdclient.c | [new file with mode: 0644] | patch | blob |
libmpdclient.h | [new file with mode: 0644] | patch | blob |
m4/CVS/Entries | [new file with mode: 0644] | patch | blob |
m4/CVS/Repository | [new file with mode: 0644] | patch | blob |
m4/CVS/Root | [new file with mode: 0644] | patch | blob |
m4/glib-2.0.m4 | [new file with mode: 0644] | patch | blob |
main.c | [new file with mode: 0644] | patch | blob |
mpc.c | [new file with mode: 0644] | patch | blob |
mpc.h | [new file with mode: 0644] | patch | blob |
options.c | [new file with mode: 0644] | patch | blob |
options.h | [new file with mode: 0644] | patch | blob |
out | [new file with mode: 0644] | patch | blob |
screen.c | [new file with mode: 0644] | patch | blob |
screen.h | [new file with mode: 0644] | patch | blob |
screen_file.c | [new file with mode: 0644] | patch | blob |
screen_file.h | [new file with mode: 0644] | patch | blob |
screen_help.c | [new file with mode: 0644] | patch | blob |
screen_help.h | [new file with mode: 0644] | patch | blob |
screen_play.c | [new file with mode: 0644] | patch | blob |
screen_play.h | [new file with mode: 0644] | patch | blob |
screen_search.c | [new file with mode: 0644] | patch | blob |
screen_search.h | [new file with mode: 0644] | patch | blob |
screen_utils.c | [new file with mode: 0644] | patch | blob |
screen_utils.h | [new file with mode: 0644] | patch | blob |
support.c | [new file with mode: 0644] | patch | blob |
support.h | [new file with mode: 0644] | patch | blob |
diff --git a/AUTHORS b/AUTHORS
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Kalle Wallin <kaw@linux.se>
diff --git a/ChangeLog b/ChangeLog
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,12 @@
+0.1.2 * Fixed a bug introduced in version 0.1.1. The full path to files
+ was printed in the browse screen (changed back to basename).
+
+0.1.1 * Added support for utf-8 (MPD >= 0.10.0)
+
+0.1.0 * First published version of mpc-ncurses
+
+
+
+
+
+
diff --git a/Makefile.am b/Makefile.am
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,23 @@
+#
+# $Id: Makefile.am,v 1.5 2004/03/16 23:49:14 kalle Exp $
+#
+
+bin_PROGRAMS = mpc-ncurses
+man_MANS = doc/ncmpc.1
+pkgdata_DATA =
+docdir = $(prefix)/share/doc/$(PACKAGE)
+doc_DATA = AUTHORS README ChangeLog
+EXTRA_DIST = COPYING $(pkgdata_DATA) $(man_MANS) $(doc_DATA)
+
+mpc_ncurses_headers = libmpdclient.h mpc.h options.h command.h screen.h \
+ screen_utils.h screen_play.h screen_file.h screen_search.h \
+ screen_help.h support.h
+
+mpc_ncurses_SOURCES = libmpdclient.c main.c mpc.c options.c command.c \
+ screen.c screen_utils.c screen_play.c screen_file.c \
+ screen_search.c screen_help.c \
+ support.c $(mpc_ncurses_headers)
+
+
+
+
diff --git a/README b/README
--- /dev/null
+++ b/README
@@ -0,0 +1,29 @@
+mpc-ncurses is a ncurses client for MPD, the Music Player Daemon.
+The client connects to a MPD running on a machine on the local network.
+By default, mpc-ncurses connects to localhost:2100. This can be
+changed either at compile-time, or by exporting the MPD_HOST and
+MPD_PORT environment variables, or by the command line options --host
+and --port.
+
+ $ mpc-ncurses --host=musicserver --port=44000
+
+
+Note!
+If your are connecting to a MPD server with a version newer than 0.9.x
+and you dont use utf-8 as your charset set the CHARSET enviroment variable.
+
+ $ export CHARSET=ISO_8859-1
+
+
+
+Read more about MPD on http://www.musicpd.org
+Read more about mpc-ncurses on http://hem.bredband.net/kaw/apps/mpc-ncurses
+
+
+
+
+
+
+
+
+
diff --git a/TODO b/TODO
--- /dev/null
+++ b/TODO
@@ -0,0 +1,15 @@
+
+client functions
+----------------------------------------------------------------------------
+ * search screen
+ * search function in playlist (like in less '/')
+ * load/save playlists
+
+ncurses
+----------------------------------------------------------------------------
+ * improve list drawing (faster)
+
+other
+----------------------------------------------------------------------------
+
+
diff --git a/bootstrap.sh b/bootstrap.sh
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,54 @@
+#! /bin/sh
+# Check Autoconf version
+if [ -x `which autoconf` ]; then
+ AC_VER=`autoconf --version | head -1 | sed 's/^[^0-9]*//'`
+ AC_VER_MAJOR=`echo $AC_VER | cut -f1 -d'.'`
+ AC_VER_MINOR=`echo $AC_VER | cut -f2 -d'.' | sed 's/[^0-9]*$//'`
+
+ if [ "$AC_VER_MAJOR" -lt "2" ]; then
+ echo "Autoconf 2.13 or greater needed to build configure."
+ exit 1
+ fi
+
+ if [ "$AC_VER_MINOR" -lt "13" ]; then
+ echo "Autoconf 2.13 or greater needed to build configure."
+ exit 1
+ fi
+
+ if [ "$AC_VER_MINOR" -lt "50" ]; then
+ if [ ! -e configure.in ]; then
+ ln -s configure.ac configure.in
+ fi
+ echo "If you see some warnings about cross-compiling, don't worry; this is normal."
+ else
+ echo "rm -f configure.in ?"
+ fi
+else
+ echo Autoconf not found. AlsaPlayer CVS requires autoconf to bootstrap itself.
+ exit 1
+fi
+
+run_cmd() {
+ echo running $* ...
+ if ! $*; then
+ echo failed!
+ exit 1
+ fi
+}
+
+# Check if /usr/local/share/aclocal exists
+if [ -d /usr/local/share/aclocal ]; then
+ ACLOCAL_INCLUDE="$ACLOCAL_INCLUDE -I /usr/local/share/aclocal"
+fi
+
+if [ -d m4 ] ; then
+ run_cmd cat m4/*.m4 > acinclude.m4
+fi
+run_cmd aclocal $ACLOCAL_INCLUDE
+run_cmd autoheader
+run_cmd libtoolize --automake
+run_cmd automake --add-missing
+run_cmd autoconf
+echo
+echo "Now run './configure'"
+echo
diff --git a/cleanup.sh b/cleanup.sh
--- /dev/null
+++ b/cleanup.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+rm -f *~ *.o Makefile configure Makefile.in
+rm -f *.guess *.log *.m4 config.status
+rm -f stamp-h*
+rm -rf *.cache
+rm -f depcomp
+rm -f config.h*
+rm -f config.sub
+rm -f libtool
+rm -f ltmain.sh
+rm -f missing
+rm -f mkinstalldirs
+rm -f install-sh
diff --git a/command.c b/command.c
--- /dev/null
+++ b/command.c
@@ -0,0 +1,222 @@
+/*
+ * $Id: command.c,v 1.9 2004/03/17 14:59:32 kalle Exp $
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#include "config.h"
+#include "command.h"
+
+#undef DEBUG_KEYS
+
+#ifdef DEBUG_KEYS
+#define DK(x) x
+#else
+#define DK(x)
+#endif
+
+#define BS KEY_BACKSPACE
+#define DEL KEY_DC
+#define UP KEY_UP
+#define DWN KEY_DOWN
+#define LEFT KEY_LEFT
+#define RGHT KEY_RIGHT
+#define HOME KEY_HOME
+#define END KEY_END
+#define PGDN KEY_NPAGE
+#define PGUP KEY_PPAGE
+#define TAB 0x09
+#define STAB 0x5A
+#define ESC 0x1B
+#define F1 KEY_F(1)
+#define F2 KEY_F(2)
+#define F3 KEY_F(3)
+#define F4 KEY_F(4)
+#define F5 KEY_F(5)
+#define F6 KEY_F(6)
+
+static command_definition_t cmds[] =
+{
+ { { 13, 0, 0 }, CMD_PLAY, "Play/Enter directory" },
+ { { 'p', 0, 0 }, CMD_PAUSE, "Pause" },
+ { { BS, ESC, 0 }, CMD_STOP, "Stop" },
+ { { 'n', 0, 0 }, CMD_TRACK_NEXT, "Next song" },
+ { { 'b', 0, 0 }, CMD_TRACK_PREVIOUS, "Previous song" },
+
+ { { '+', RGHT, 0 }, CMD_VOLUME_UP, "Increase volume" },
+ { { '-', LEFT, 0 }, CMD_VOLUME_DOWN, "Decrease volume" },
+
+ { { ' ', 0, 0 }, CMD_SELECT, "Select/deselect song in playlist" },
+ { { DEL, 0, 0 }, CMD_DELETE, "Delete song from playlist" },
+ { { 's', 0, 0 }, CMD_SHUFFLE, "Shuffle playlist" },
+ { { 'c', 0, 0 }, CMD_CLEAR, "Clear playlist" },
+ { { 'r', 0, 0 }, CMD_REPEAT, "Toggle repeat mode" },
+ { { 'z', 0, 0 }, CMD_RANDOM, "Toggle random mode" },
+
+ { { UP, 0, 0 }, CMD_LIST_PREVIOUS, "Move: Up" },
+ { { DWN, 0, 0 }, CMD_LIST_NEXT, "Move: Down" },
+ { { HOME, 0, 0 }, CMD_LIST_FIRST, "Move: Home" },
+ { { END, 0, 0 }, CMD_LIST_LAST, "Move: End" },
+ { { PGUP, 0, 0 }, CMD_LIST_PREVIOUS_PAGE, "Move: Page Up" },
+ { { PGDN, 0, 0 }, CMD_LIST_NEXT_PAGE, "Move: Page Down" },
+
+ { { TAB, 0, 0 }, CMD_SCREEN_NEXT, "Next screen" },
+ { { STAB, 0, 0 }, CMD_SCREEN_PREVIOUS, "Previous screen" },
+ { { F1, '1', 'h' }, CMD_SCREEN_HELP, "Help screen" },
+ { { F2, '2', 0 }, CMD_SCREEN_PLAY, "Playlist screen" },
+ { { F3, '3', 0 }, CMD_SCREEN_FILE, "Browse screen" },
+ // { { F4, '4', 0 }, CMD_SCREEN_SEARCH, "Search screen" },
+
+ { { 'q', 0, 0 }, CMD_QUIT, "Quit " PACKAGE },
+
+ { { -1, -1, -1 }, CMD_NONE, NULL }
+};
+
+char *
+key2str(int key)
+{
+ static char buf[2];
+
+ buf[0] = 0;
+ switch(key)
+ {
+ case ' ':
+ return "Space";
+ case 13:
+ return "Enter";
+ case BS:
+ return "Backspace";
+ case DEL:
+ return "Delete";
+ case UP:
+ return "Up";
+ case DWN:
+ return "Down";
+ case LEFT:
+ return "Left";
+ case RGHT:
+ return "Right";
+ case HOME:
+ return "Home";
+ case END:
+ return "End";
+ case PGDN:
+ return "PageDown";
+ case PGUP:
+ return "PageUp";
+ case TAB:
+ return "Tab";
+ case STAB:
+ return "Shift+Tab";
+ case ESC:
+ return "Esc";
+ case F1:
+ return "F1";
+ case F2:
+ return "F2";
+ case F3:
+ return "F3";
+ case F4:
+ return "F4";
+ case F5:
+ return "F5";
+ case F6:
+ return "F6";
+ default:
+ snprintf(buf, 2, "%c", key);
+ }
+ return buf;
+}
+
+void
+command_dump_keys(void)
+{
+ int i;
+
+ i=0;
+ while( cmds[i].description )
+ {
+ if( cmds[i].command != CMD_NONE )
+ {
+ int j;
+ char keystr[80];
+
+ strcpy(keystr, key2str(cmds[i].keys[0]));
+ j=1;
+ while( j<3 && cmds[i].keys[j]>0 )
+ {
+ strcat(keystr, " ");
+ strcat(keystr, key2str(cmds[i].keys[j]));
+ j++;
+ }
+ printf(" %20s : %s\n", keystr, cmds[i].description);
+
+ }
+ i++;
+ }
+}
+
+char *
+command_get_keys(command_t command)
+{
+ int i;
+
+ i=0;
+ while( cmds[i].description )
+ {
+ if( cmds[i].command == command )
+ {
+ int j;
+ static char keystr[80];
+
+ strcpy(keystr, key2str(cmds[i].keys[0]));
+ j=1;
+ while( j<3 && cmds[i].keys[j]>0 )
+ {
+ strcat(keystr, " ");
+ strcat(keystr, key2str(cmds[i].keys[j]));
+ j++;
+ }
+ return keystr;
+ }
+ i++;
+ }
+ return NULL;
+}
+
+command_t
+get_keyboard_command(void)
+{
+ int i;
+ int key;
+
+ key = wgetch(stdscr);
+
+ if( key==ERR )
+ return CMD_NONE;
+
+ DK(fprintf(stderr, "key = 0x%02X\t", key));
+
+ if( isalpha(key) )
+ key=tolower(key);
+
+ i=0;
+ while( cmds[i].description )
+ {
+ if( cmds[i].keys[0] == key ||
+ cmds[i].keys[1] == key ||
+ cmds[i].keys[2] == key )
+ {
+ DK(fprintf(stderr, "Match - %s\n", cmds[i].description));
+ return cmds[i].command;
+ }
+ i++;
+ }
+ DK(fprintf(stderr, "NO MATCH\n"));
+ return CMD_NONE;
+}
diff --git a/command.h b/command.h
--- /dev/null
+++ b/command.h
@@ -0,0 +1,44 @@
+
+typedef enum
+{
+ CMD_NONE = 0,
+ CMD_PLAY,
+ CMD_SELECT,
+ CMD_PAUSE,
+ CMD_STOP,
+ CMD_TRACK_NEXT,
+ CMD_TRACK_PREVIOUS,
+ CMD_SHUFFLE,
+ CMD_RANDOM,
+ CMD_CLEAR,
+ CMD_DELETE,
+ CMD_REPEAT,
+ CMD_VOLUME_UP,
+ CMD_VOLUME_DOWN,
+ CMD_LIST_PREVIOUS,
+ CMD_LIST_NEXT,
+ CMD_LIST_FIRST,
+ CMD_LIST_LAST,
+ CMD_LIST_NEXT_PAGE,
+ CMD_LIST_PREVIOUS_PAGE,
+ CMD_SCREEN_PREVIOUS,
+ CMD_SCREEN_NEXT,
+ CMD_SCREEN_PLAY,
+ CMD_SCREEN_FILE,
+ CMD_SCREEN_SEARCH,
+ CMD_SCREEN_HELP,
+ CMD_QUIT
+} command_t;
+
+typedef struct
+{
+ int keys[3];
+ command_t command;
+ char *description;
+} command_definition_t;
+
+
+void command_dump_keys(void);
+char *command_get_keys(command_t command);
+
+command_t get_keyboard_command(void);
diff --git a/configure.ac b/configure.ac
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,100 @@
+dnl
+dnl $Id: configure.ac,v 1.10 2004/03/18 09:33:43 kalle Exp $
+dnl
+
+AC_INIT(main.c)
+AM_INIT_AUTOMAKE(mpc-ncurses, 0.1.2)
+
+dnl Check for programs
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+dnl =======================================================
+dnl initialize variables
+dnl =======================================================
+
+set -- $CFLAGS
+CFLAGS="-Wall $CFLAGS"
+
+
+dnl
+dnl Check for libaries
+dnl
+
+dnl ncurses
+AC_CHECK_LIB(ncurses, initscr,, [AC_MSG_ERROR(ncurses library is required)])
+
+dnl glib-2.0
+AM_PATH_GLIB_2_0(, , [AC_MSG_ERROR(glib-2.x is required)], glib)
+
+dnl popt
+AC_CHECK_LIB(popt,
+ poptGetArg,
+ LIBS="$LIBS -lpopt",
+ AC_MSG_ERROR(Missing popt command line parsing library))
+
+dnl
+dnl Check for types
+dnl
+AC_CHECK_TYPE(socklen_t,
+ AC_DEFINE(HAVE_SOCKLEN_T, 1, socklen_t defined in sys/socket.h),
+)
+
+dnl
+dnl Check for headers
+dnl
+AC_CHECK_HEADER(libgen.h,
+ AC_DEFINE(HAVE_LIBGEN_H, 1, glibc - libgen.h),
+ ,)
+
+dnl
+dnl X11
+dnl
+dnl AC_PATH_XTRA
+
+dnl Debugging
+AC_ARG_ENABLE(debug,
+ [ --enable-debug Enable debugging [default=no]],
+ ,
+ enable_debug=no)
+
+if test "$enable_debug" = yes; then
+ CFLAGS="$CFLAGS -g -DDEBUG"
+fi
+
+dnl Default host
+AC_ARG_WITH(default-host,
+ [ --with-default-host=HOST Default host (localhost)],
+ DEFAULT_HOST="$withval",
+ DEFAULT_HOST="localhost")
+
+dnl Default port
+AC_ARG_WITH(default-port,
+ [ --with-default-port=PORT Default port (2100)],
+ DEFAULT_PORT="$withval",
+ DEFAULT_PORT="2100")
+
+
+CFLAGS="$CFLAGS $GLIB_CFLAGS"
+LIBS="$LIBS $GLIB_LIBS -lncurses"
+
+
+dnl Autoheader
+AC_DEFINE_UNQUOTED(DEFAULT_PORT, $DEFAULT_PORT, Default MPD port)
+AC_DEFINE_UNQUOTED(DEFAULT_PORT_STR, "$DEFAULT_PORT", Default MPD port)
+AC_DEFINE_UNQUOTED(DEFAULT_HOST, "$DEFAULT_HOST", Default MPD host)
+
+
+AM_CONFIG_HEADER(config.h)
+
+AC_OUTPUT(Makefile)
+
+echo "
+Configuration:
+ Install path: ${prefix}
+ Enable debugging: ${enable_debug}
+ Default MPD host: ${DEFAULT_HOST}
+ Default MPD port: ${DEFAULT_PORT}
+"
+echo
diff --git a/doc/ncmpc.1 b/doc/ncmpc.1
--- /dev/null
+++ b/doc/ncmpc.1
@@ -0,0 +1,46 @@
+.TH "mpc-ncurses" "1" "" "Kalle Wallin" ""
+.SH "NAME"
+ncmpc \- ncurses MPD client.
+.SH "SYNOPSIS"
+.B ncmpc
+[options]
+.SH "DESCRIPTION"
+ncmpc is a client for MPD, the Music Player Daemon.
+ncmpc connects to a MPD running on a machine on the local
+network.
+
+By default, ncmpc connects to localhost:2100.
+This can be changed either at compile\-time, or by exporting the
+MPD_HOST and MPD_PORT environment variables, or by the command line
+options \-\-host and \-\-port.
+
+$ ncmpc \-\-host=musicserver \-\-port=44000
+
+Read more about MPD on http://www.musicpd.org
+
+.SH "OPTIONS"
+.TP
+.B \-?, \-\-help
+Display help.
+.TP
+.B \-V, \-\-version
+Display version information.
+.TP
+.B \-k, \-\-keys
+Display key bindings.
+.TP
+.B \-h, \-\-host=HOSTNAME
+Specify host.
+.TP
+.B \-p, \-\-port=PORT
+Connect to server on PORT.
+.TP
+Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options.
+.SH "KEYS"
+Since ncmpc is under development key bindings may change.
+View current ncmpc key bindings in the help screen or by running:
+
+$ ncmpc \-\-keys
+
+.SH "SEE ALSO"
+mpc(1), MPD(1)
diff --git a/libmpdclient.c b/libmpdclient.c
--- /dev/null
+++ b/libmpdclient.c
@@ -0,0 +1,1076 @@
+/* libmpdclient
+ * (c)2002 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "libmpdclient.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <sys/param.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#ifndef HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+#endif
+
+#ifndef MPD_NO_IPV6
+#ifdef AF_INET6
+#define MPD_HAVE_IPV6
+#endif
+#endif
+
+#ifdef MPD_HAVE_IPV6
+int mpd_ipv6Supported() {
+ int s;
+ s = socket(AF_INET6,SOCK_STREAM,0);
+ if(s == -1) return 0;
+ close(s);
+ return 1;
+}
+#endif
+
+
+char * mpd_sanitizeArg(const char * arg) {
+ size_t i;
+ int count=0;
+ char * ret;
+
+ for(i=0;i<strlen(arg);i++) {
+ if(arg[i]=='"' || arg[i]=='\\') count++;
+ }
+
+ ret = malloc(strlen(arg)+count+1);
+
+ count = 0;
+ for(i=0;i<strlen(arg)+1;i++) {
+ if(arg[i]=='"' || arg[i]=='\\') {
+ ret[i+count] = '\\';
+ count++;
+ }
+ ret[i+count] = arg[i];
+ }
+
+ return ret;
+}
+
+mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
+{
+ mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
+
+ ret->name = strdup(name);
+ ret->value = strdup(value);
+
+ return ret;
+}
+
+void mpd_freeReturnElement(mpd_ReturnElement * re) {
+ free(re->name);
+ free(re->value);
+ free(re);
+}
+
+void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
+ connection->timeout.tv_sec = (int)timeout;
+ connection->timeout.tv_usec = (int)((timeout -
+ connection->timeout.tv_sec)/1e+6+0.5);
+}
+
+mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
+ int err;
+ struct hostent * he;
+ struct sockaddr * dest;
+ socklen_t destlen;
+ struct sockaddr_in sin;
+ char * rt;
+ char * output;
+ mpd_Connection * connection = malloc(sizeof(mpd_Connection));
+ struct timeval tv;
+ fd_set fds;
+#ifdef MPD_HAVE_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+ strcpy(connection->buffer,"");
+ connection->buflen = 0;
+ connection->bufstart = 0;
+ strcpy(connection->errorStr,"");
+ connection->error = 0;
+ connection->doneProcessing = 0;
+ connection->commandList = 0;
+ connection->returnElement = NULL;
+
+ if(!(he=gethostbyname(host))) {
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "host \"%s\" not found",host);
+ connection->error = MPD_ERROR_UNKHOST;
+ return connection;
+ }
+
+ memset(&sin,0,sizeof(struct sockaddr_in));
+ /*dest.sin_family = he->h_addrtype;*/
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+#ifdef MPD_HAVE_IPV6
+ memset(&sin6,0,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(port);
+#endif
+ switch(he->h_addrtype) {
+ case AF_INET:
+ memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
+ he->h_length);
+ dest = (struct sockaddr *)&sin;
+ destlen = sizeof(struct sockaddr_in);
+ break;
+#ifdef MPD_HAVE_IPV6
+ case AF_INET6:
+ if(!mpd_ipv6Supported()) {
+ strcpy(connection->errorStr,"no IPv6 suuport but a "
+ "IPv6 address found\n");
+ connection->error = MPD_ERROR_SYSTEM;
+ return connection;
+ }
+ memcpy((char *)&sin6.sin6_addr.s6_addr,(char *)he->h_addr,
+ he->h_length);
+ dest = (struct sockaddr *)&sin6;
+ destlen = sizeof(struct sockaddr_in6);
+ break;
+#endif
+ default:
+ strcpy(connection->errorStr,"address type is not IPv4 or "
+ "IPv6\n");
+ connection->error = MPD_ERROR_SYSTEM;
+ return connection;
+ break;
+ }
+
+ if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
+ strcpy(connection->errorStr,"problems creating socket");
+ connection->error = MPD_ERROR_SYSTEM;
+ return connection;
+ }
+
+ /* connect stuff */
+ {
+#ifdef SO_RCVTIMEO
+ struct timeval rcvoldto;
+ struct timeval sndoldto;
+ socklen_t oldlen = sizeof(struct timeval);
+
+ mpd_setConnectionTimeout(connection,timeout);
+
+ tv.tv_sec = connection->timeout.tv_sec;
+ tv.tv_usec = connection->timeout.tv_usec;
+
+ if(getsockopt(connection->sock,SOL_SOCKET,SO_RCVTIMEO,&rcvoldto,
+ &oldlen)<0 ||
+ getsockopt(connection->sock,SOL_SOCKET,
+ SO_SNDTIMEO,&sndoldto,&oldlen)<0)
+ {
+ strcpy(connection->errorStr,"problems getting socket "
+ "timeout\n");
+ connection->error = MPD_ERROR_SYSTEM;
+ return connection;
+ }
+ if(setsockopt(connection->sock,SOL_SOCKET,SO_RCVTIMEO,&tv,
+ sizeof(struct timeval))<0 ||
+ setsockopt(connection->sock,SOL_SOCKET,
+ SO_SNDTIMEO,&tv,
+ sizeof(struct timeval))<0)
+ {
+ strcpy(connection->errorStr,"problems setting socket "
+ "timeout\n");
+ connection->error = MPD_ERROR_SYSTEM;
+ return connection;
+ }
+#endif
+ if(connect(connection->sock,dest,destlen)<0) {
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "problems connecting to \"%s\" on port"
+ " %i",host,port);
+ connection->error = MPD_ERROR_CONNPORT;
+ return connection;
+ }
+#ifdef SO_RCVTIMEO
+ if(setsockopt(connection->sock,SOL_SOCKET,SO_SNDTIMEO,&rcvoldto,
+ sizeof(struct timeval))<0 ||
+ setsockopt(connection->sock,SOL_SOCKET,
+ SO_SNDTIMEO,&sndoldto,
+ sizeof(struct timeval))<0)
+ {
+ strcpy(connection->errorStr,"problems setting socket "
+ "timeout\n");
+ connection->error = MPD_ERROR_SYSTEM;
+ return connection;
+ }
+#endif
+ }
+
+ while(!(rt = strstr(connection->buffer,"\n"))) {
+ tv.tv_sec = connection->timeout.tv_sec;
+ tv.tv_usec = connection->timeout.tv_usec;
+ FD_ZERO(&fds);
+ FD_SET(connection->sock,&fds);
+ if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
+ int readed;
+ readed = recv(connection->sock,
+ &(connection->buffer[connection->buflen]),
+ MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
+ if(readed<=0) {
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "problems getting a response from"
+ " \"%s\" on port %i",host,port);
+ connection->error = MPD_ERROR_NORESPONSE;
+ return connection;
+ }
+ connection->buflen+=readed;
+ connection->buffer[connection->buflen] = '\0';
+ tv.tv_sec = connection->timeout.tv_sec;
+ tv.tv_usec = connection->timeout.tv_usec;
+ }
+ else if(err<0 && errno==EINTR) continue;
+ else {
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "timeout in attempting to get a response from"
+ " \"%s\" on port %i",host,port);
+ connection->error = MPD_ERROR_NORESPONSE;
+ return connection;
+ }
+ }
+
+ *rt = '\0';
+ output = strdup(connection->buffer);
+ strcpy(connection->buffer,rt+1);
+ connection->buflen = strlen(connection->buffer);
+
+ if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
+ free(output);
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "mpd not running on port %i on host \"%s\"",
+ port,host);
+ connection->error = MPD_ERROR_NOTMPD;
+ return connection;
+ }
+
+ {
+ char * test;
+ char * version[3];
+ char * tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
+ char * search = ".";
+ int i;
+
+ for(i=0;i<3;i++) {
+ char * tok;
+ if(i==3) search = " ";
+ version[i] = strtok_r(tmp,search,&tok);
+ if(!version[i]) {
+ free(output);
+ snprintf(connection->errorStr,
+ MPD_BUFFER_MAX_LENGTH,
+ "error parsing version number at "
+ "\"%s\"",
+ &output[strlen(MPD_WELCOME_MESSAGE)]);
+ connection->error = MPD_ERROR_NOTMPD;
+ return connection;
+ }
+ connection->version[i] = strtol(version[i],&test,10);
+ if(version[i]==test || *test!='\0') {
+ free(output);
+ snprintf(connection->errorStr,
+ MPD_BUFFER_MAX_LENGTH,
+ "error parsing version number at "
+ "\"%s\"",
+ &output[strlen(MPD_WELCOME_MESSAGE)]);
+ connection->error = MPD_ERROR_NOTMPD;
+ return connection;
+ }
+ tmp = NULL;
+ }
+ }
+
+ free(output);
+
+ connection->doneProcessing = 1;
+
+ return connection;
+}
+
+void mpd_clearError(mpd_Connection * connection) {
+ connection->error = 0;
+ connection->errorStr[0] = '\0';
+}
+
+void mpd_closeConnection(mpd_Connection * connection) {
+ close(connection->sock);
+ if(connection->returnElement) free(connection->returnElement);
+ free(connection);
+}
+
+void mpd_executeCommand(mpd_Connection * connection, char * command) {
+ int ret;
+ struct timeval tv;
+ fd_set fds;
+ char * commandPtr = command;
+ int commandLen = strlen(command);
+
+ if(!connection->doneProcessing && !connection->commandList) {
+ strcpy(connection->errorStr,"not done processing current command");
+ connection->error = 1;
+ return;
+ }
+
+ mpd_clearError(connection);
+
+ FD_ZERO(&fds);
+ FD_SET(connection->sock,&fds);
+ tv.tv_sec = connection->timeout.tv_sec;
+ tv.tv_usec = connection->timeout.tv_usec;
+
+ while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
+ (ret==-1 && errno==EINTR)) {
+ ret = send(connection->sock,commandPtr,commandLen,
+ MSG_DONTWAIT);
+ if(ret<=0)
+ {
+ if(ret==EAGAIN || ret==EINTR) continue;
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "problems giving command \"%s\"",command);
+ connection->error = MPD_ERROR_SENDING;
+ return;
+ }
+ else {
+ commandPtr+=ret;
+ commandLen-=ret;
+ }
+
+ if(commandLen<=0) break;
+ }
+
+ if(commandLen>0) {
+ perror("");
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "timeout sending command \"%s\"",command);
+ connection->error = MPD_ERROR_TIMEOUT;
+ return;
+ }
+
+ if(!connection->commandList) connection->doneProcessing = 0;
+}
+
+void mpd_getNextReturnElement(mpd_Connection * connection) {
+ char * output = NULL;
+ char * rt = NULL;
+ char * name;
+ char * value;
+ fd_set fds;
+ struct timeval tv;
+ char * tok;
+ int readed;
+ char * bufferCheck;
+ int err;
+
+ if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
+ connection->returnElement = NULL;
+
+ if(connection->doneProcessing) {
+ strcpy(connection->errorStr,"already done processing current command");
+ connection->error = 1;
+ return;
+ }
+
+ bufferCheck = connection->buffer+connection->bufstart;
+ while(connection->bufstart>=connection->buflen ||
+ !(rt = strstr(bufferCheck,"\n"))) {
+ if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
+ memmove(connection->buffer,
+ connection->buffer+
+ connection->bufstart,
+ connection->buflen-
+ connection->bufstart+1);
+ bufferCheck-=connection->bufstart;
+ connection->buflen-=connection->bufstart;
+ connection->bufstart = 0;
+ }
+ if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
+ strcpy(connection->errorStr,"buffer overrun");
+ connection->error = MPD_ERROR_BUFFEROVERRUN;
+ connection->doneProcessing = 1;
+ return;
+ }
+ bufferCheck+=connection->buflen-connection->bufstart;
+ tv.tv_sec = connection->timeout.tv_sec;
+ tv.tv_usec = connection->timeout.tv_usec;
+ FD_ZERO(&fds);
+ FD_SET(connection->sock,&fds);
+ if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
+ readed = recv(connection->sock,
+ connection->buffer+connection->buflen,
+ MPD_BUFFER_MAX_LENGTH-connection->buflen,
+ MSG_DONTWAIT);
+ if(readed<0 && (errno==EAGAIN || errno==EINTR)) {
+ continue;
+ }
+ if(readed<=0) {
+ strcpy(connection->errorStr,"connection"
+ " closed");
+ connection->error = MPD_ERROR_CONNCLOSED;
+ connection->doneProcessing = 1;
+ return;
+ }
+ connection->buflen+=readed;
+ connection->buffer[connection->buflen] = '\0';
+ }
+ else if(err<0 && errno==EINTR) continue;
+ else {
+ strcpy(connection->errorStr,"connection timeout");
+ connection->error = MPD_ERROR_TIMEOUT;
+ connection->doneProcessing = 1;
+ return;
+ }
+ }
+
+ *rt = '\0';
+ output = connection->buffer+connection->bufstart;
+ connection->bufstart = rt - connection->buffer + 1;
+
+ if(strcmp(output,"OK")==0) {
+ connection->doneProcessing = 1;
+ return;
+ }
+ if(strncmp(output,"ACK",strlen("ACK"))==0) {
+ strcpy(connection->errorStr,output);
+ connection->error = MPD_ERROR_ACK;
+ connection->doneProcessing = 1;
+ return;
+ }
+
+ name = strtok_r(output,":",&tok);
+ if(name && (value = strtok_r(NULL,"",&tok)) && value[0]==' ') {
+ connection->returnElement = mpd_newReturnElement(name,&(value[1]));
+ }
+ else {
+ if(!name || !value) {
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "error parsing: %s",output);
+ }
+ else {
+ snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH,
+ "error parsing: %s:%s",name,value);
+ }
+ connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0';
+ connection->error = 1;
+ }
+}
+
+void mpd_finishCommand(mpd_Connection * connection) {
+ while(!connection->doneProcessing) mpd_getNextReturnElement(connection);
+}
+
+
+mpd_Status * mpd_getStatus(mpd_Connection * connection) {
+ mpd_Status * status;
+
+ mpd_executeCommand(connection,"status\n");
+
+ if(connection->error) return NULL;
+
+ status = malloc(sizeof(mpd_Status));
+ status->volume = -1;
+ status->repeat = 0;
+ status->random = 0;
+ status->playlist = -1;
+ status->playlistLength = -1;
+ status->state = -1;
+ status->song = 0;
+ status->elapsedTime = 0;
+ status->totalTime = 0;
+ status->bitRate = 0;
+ status->error = NULL;
+
+ mpd_getNextReturnElement(connection);
+ if(connection->error) {
+ free(status);
+ return NULL;
+ }
+ while(connection->returnElement) {
+ mpd_ReturnElement * re = connection->returnElement;
+ if(strcmp(re->name,"volume")==0) {
+ status->volume = atoi(re->value);
+ }
+ else if(strcmp(re->name,"repeat")==0) {
+ status->repeat = atoi(re->value);
+ }
+ else if(strcmp(re->name,"random")==0) {
+ status->random = atoi(re->value);
+ }
+ else if(strcmp(re->name,"playlist")==0) {
+ status->playlist = strtol(re->value,NULL,10);
+ }
+ else if(strcmp(re->name,"playlistlength")==0) {
+ status->playlistLength = atoi(re->value);
+ }
+ else if(strcmp(re->name,"bitrate")==0) {
+ status->bitRate = atoi(re->value);
+ }
+ else if(strcmp(re->name,"state")==0) {
+ if(strcmp(re->value,"play")==0) {
+ status->state = MPD_STATUS_STATE_PLAY;
+ }
+ else if(strcmp(re->value,"stop")==0) {
+ status->state = MPD_STATUS_STATE_STOP;
+ }
+ else if(strcmp(re->value,"pause")==0) {
+ status->state = MPD_STATUS_STATE_PAUSE;
+ }
+ else {
+ status->state = MPD_STATUS_STATE_UNKNOWN;
+ }
+ }
+ else if(strcmp(re->name,"song")==0) {
+ status->song = atoi(re->value);
+ }
+ else if(strcmp(re->name,"time")==0) {
+ char * tok;
+ char * copy;
+ copy = strdup(re->value);
+ status->elapsedTime = atoi(strtok_r(copy,":",&tok));
+ status->totalTime = atoi(strtok_r(NULL,"",&tok));
+ free(copy);
+ }
+ else if(strcmp(re->name,"error")==0) {
+ status->error = strdup(re->value);
+ }
+
+ mpd_getNextReturnElement(connection);
+ if(connection->error) {
+ free(status);
+ return NULL;
+ }
+ }
+
+ if(connection->error) {
+ free(status);
+ return NULL;
+ }
+ else if(status->state<0) {
+ strcpy(connection->errorStr,"state not found");
+ connection->error = 1;
+ free(status);
+ return NULL;
+ }
+
+ return status;
+}
+
+void mpd_freeStatus(mpd_Status * status) {
+ if(status->error) free(status->error);
+ free(status);
+}
+
+void mpd_initSong(mpd_Song * song) {
+ song->file = NULL;
+ song->artist = NULL;
+ song->album = NULL;
+ song->track = NULL;
+ song->title = NULL;
+ song->time = MPD_SONG_NO_TIME;
+}
+
+void mpd_finishSong(mpd_Song * song) {
+ if(song->file) free(song->file);
+ if(song->artist) free(song->artist);
+ if(song->album) free(song->album);
+ if(song->title) free(song->title);
+ if(song->track) free(song->track);
+}
+
+mpd_Song * mpd_newSong() {
+ mpd_Song * ret = malloc(sizeof(mpd_Song));
+
+ mpd_initSong(ret);
+
+ return ret;
+}
+
+void mpd_freeSong(mpd_Song * song) {
+ mpd_finishSong(song);
+ free(song);
+}
+
+mpd_Song * mpd_songDup(mpd_Song * song) {
+ mpd_Song * ret = mpd_newSong();
+
+ if(song->file) ret->file = strdup(song->file);
+ if(song->artist) ret->artist = strdup(song->artist);
+ if(song->album) ret->album = strdup(song->album);
+ if(song->title) ret->title = strdup(song->title);
+ if(song->track) ret->track = strdup(song->track);
+ ret->time = song->time;
+
+ return ret;
+}
+
+void mpd_initDirectory(mpd_Directory * directory) {
+ directory->path = NULL;
+}
+
+void mpd_finishDirectory(mpd_Directory * directory) {
+ if(directory->path) free(directory->path);
+}
+
+mpd_Directory * mpd_newDirectory () {
+ mpd_Directory * directory = malloc(sizeof(mpd_Directory));;
+
+ mpd_initDirectory(directory);
+
+ return directory;
+}
+
+void mpd_freeDirectory(mpd_Directory * directory) {
+ mpd_finishDirectory(directory);
+
+ free(directory);
+}
+
+mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
+ mpd_Directory * ret = mpd_newDirectory();
+
+ if(directory->path) ret->path = strdup(directory->path);
+
+ return ret;
+}
+
+void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
+ playlist->path = NULL;
+}
+
+void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
+ if(playlist->path) free(playlist->path);
+}
+
+mpd_PlaylistFile * mpd_newPlaylistFile() {
+ mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile));
+
+ mpd_initPlaylistFile(playlist);
+
+ return playlist;
+}
+
+void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
+ mpd_finishPlaylistFile(playlist);
+ free(playlist);
+}
+
+mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
+ mpd_PlaylistFile * ret = mpd_newPlaylistFile();
+
+ if(playlist->path) ret->path = strdup(playlist->path);
+
+ return ret;
+}
+
+void mpd_initInfoEntity(mpd_InfoEntity * entity) {
+ entity->info.directory = NULL;
+}
+
+void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
+ if(entity->info.directory) {
+ if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
+ mpd_freeDirectory(entity->info.directory);
+ }
+ else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
+ mpd_freeSong(entity->info.song);
+ }
+ else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
+ mpd_freePlaylistFile(entity->info.playlistFile);
+ }
+ }
+}
+
+mpd_InfoEntity * mpd_newInfoEntity() {
+ mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity));
+
+ mpd_initInfoEntity(entity);
+
+ return entity;
+}
+
+void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
+ mpd_finishInfoEntity(entity);
+ free(entity);
+}
+
+void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
+ mpd_executeCommand(connection,command);
+}
+
+mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
+ mpd_InfoEntity * entity = NULL;
+
+ if(connection->doneProcessing) return NULL;
+
+ if(!connection->returnElement) mpd_getNextReturnElement(connection);
+
+ if(connection->returnElement) {
+ if(strcmp(connection->returnElement->name,"file")==0) {
+ entity = mpd_newInfoEntity();
+ entity->type = MPD_INFO_ENTITY_TYPE_SONG;
+ entity->info.song = mpd_newSong();
+ entity->info.song->file =
+ strdup(connection->returnElement->value);
+ }
+ else if(strcmp(connection->returnElement->name,
+ "directory")==0) {
+ entity = mpd_newInfoEntity();
+ entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
+ entity->info.directory = mpd_newDirectory();
+ entity->info.directory->path =
+ strdup(connection->returnElement->value);
+ }
+ else if(strcmp(connection->returnElement->name,"playlist")==0) {
+ entity = mpd_newInfoEntity();
+ entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
+ entity->info.playlistFile = mpd_newPlaylistFile();
+ entity->info.playlistFile->path =
+ strdup(connection->returnElement->value);
+ }
+ else {
+ connection->error = 1;
+ strcpy(connection->errorStr,"problem parsing song info");
+ return NULL;
+ }
+ }
+ else return NULL;
+
+ mpd_getNextReturnElement(connection);
+ while(connection->returnElement) {
+ mpd_ReturnElement * re = connection->returnElement;
+
+ if(strcmp(re->name,"file")==0) return entity;
+ else if(strcmp(re->name,"directory")==0) return entity;
+ else if(strcmp(re->name,"playlist")==0) return entity;
+
+ if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
+ strlen(re->value)) {
+ if(!entity->info.song->artist &&
+ strcmp(re->name,"Artist")==0) {
+ entity->info.song->artist = strdup(re->value);
+ }
+ else if(!entity->info.song->album &&
+ strcmp(re->name,"Album")==0) {
+ entity->info.song->album = strdup(re->value);
+ }
+ else if(!entity->info.song->title &&
+ strcmp(re->name,"Title")==0) {
+ entity->info.song->title = strdup(re->value);
+ }
+ else if(!entity->info.song->track &&
+ strcmp(re->name,"Track")==0) {
+ entity->info.song->track = strdup(re->value);
+ }
+ else if(entity->info.song->time==MPD_SONG_NO_TIME &&
+ strcmp(re->name,"Time")==0) {
+ entity->info.song->time = atoi(re->value);
+ }
+ }
+ else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
+ }
+ else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
+ }
+
+ mpd_getNextReturnElement(connection);
+ }
+
+ return entity;
+}
+
+char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
+ const char * name)
+{
+ if(connection->doneProcessing) return NULL;
+
+ mpd_getNextReturnElement(connection);
+ while(connection->returnElement) {
+ mpd_ReturnElement * re = connection->returnElement;
+
+ if(strcmp(re->name,name)==0) return strdup(re->value);
+ mpd_getNextReturnElement(connection);
+ }
+
+ return NULL;
+}
+
+char * mpd_getNextArtist(mpd_Connection * connection) {
+ return mpd_getNextReturnElementNamed(connection,"Artist");
+}
+
+char * mpd_getNextAlbum(mpd_Connection * connection) {
+ return mpd_getNextReturnElementNamed(connection,"Album");
+}
+
+void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songNum) {
+ char * string = malloc(strlen("playlistinfo")+25);
+ sprintf(string,"playlistinfo \"%i\"\n",songNum);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
+ char * sDir = mpd_sanitizeArg(dir);
+ char * string = malloc(strlen("listall")+strlen(sDir)+5);
+ sprintf(string,"listall \"%s\"\n",sDir);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+ free(sDir);
+}
+
+void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
+ char * sDir = mpd_sanitizeArg(dir);
+ char * string = malloc(strlen("listallinfo")+strlen(sDir)+5);
+ sprintf(string,"listallinfo \"%s\"\n",sDir);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+ free(sDir);
+}
+
+void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
+ char * sDir = mpd_sanitizeArg(dir);
+ char * string = malloc(strlen("lsinfo")+strlen(sDir)+5);
+ sprintf(string,"lsinfo \"%s\"\n",sDir);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+ free(sDir);
+}
+
+void mpd_sendSearchCommand(mpd_Connection * connection, int table,
+ const char * str)
+{
+ char st[10];
+ char * string;
+ char * sanitStr = mpd_sanitizeArg(str);
+ if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
+ else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
+ else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
+ else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename");
+ else {
+ connection->error = 1;
+ strcpy(connection->errorStr,"unknown table for search");
+ return;
+ }
+ string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6);
+ sprintf(string,"search %s \"%s\"\n",st,sanitStr);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+ free(sanitStr);
+}
+
+void mpd_sendFindCommand(mpd_Connection * connection, int table,
+ const char * str)
+{
+ char st[10];
+ char * string;
+ char * sanitStr = mpd_sanitizeArg(str);
+ if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
+ else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
+ else if(table == MPD_TABLE_TITLE) strcpy(st,"title");
+ else {
+ connection->error = 1;
+ strcpy(connection->errorStr,"unknown table for find");
+ return;
+ }
+ string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6);
+ sprintf(string,"find %s \"%s\"\n",st,sanitStr);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+ free(sanitStr);
+}
+
+void mpd_sendListCommand(mpd_Connection * connection, int table,
+ const char * arg1)
+{
+ char st[10];
+ char * string;
+ if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
+ else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
+ else {
+ connection->error = 1;
+ strcpy(connection->errorStr,"unknown table for list");
+ return;
+ }
+ if(arg1) {
+ char * sanitArg1 = mpd_sanitizeArg(arg1);
+ string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6);
+ sprintf(string,"list %s \"%s\"\n",st,sanitArg1);
+ free(sanitArg1);
+ }
+ else {
+ string = malloc(strlen("list")+strlen(st)+3);
+ sprintf(string,"list %s\n",st);
+ }
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
+ char * sFile = mpd_sanitizeArg(file);
+ char * string = malloc(strlen("add")+strlen(sFile)+5);
+ sprintf(string,"add \"%s\"\n",sFile);
+ mpd_executeCommand(connection,string);
+ free(string);
+ free(sFile);
+}
+
+void mpd_sendDeleteCommand(mpd_Connection * connection, int songNum) {
+ char * string = malloc(strlen("delete")+25);
+ sprintf(string,"delete \"%i\"\n",songNum);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
+ char * sName = mpd_sanitizeArg(name);
+ char * string = malloc(strlen("save")+strlen(sName)+5);
+ sprintf(string,"save \"%s\"\n",sName);
+ mpd_executeCommand(connection,string);
+ free(string);
+ free(sName);
+}
+
+void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
+ char * sName = mpd_sanitizeArg(name);
+ char * string = malloc(strlen("load")+strlen(sName)+5);
+ sprintf(string,"load \"%s\"\n",sName);
+ mpd_executeCommand(connection,string);
+ free(string);
+ free(sName);
+}
+
+void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
+ char * sName = mpd_sanitizeArg(name);
+ char * string = malloc(strlen("rm")+strlen(sName)+5);
+ sprintf(string,"rm \"%s\"\n",sName);
+ mpd_executeCommand(connection,string);
+ free(string);
+ free(sName);
+}
+
+void mpd_sendShuffleCommand(mpd_Connection * connection) {
+ mpd_executeCommand(connection,"shuffle\n");
+}
+
+void mpd_sendClearCommand(mpd_Connection * connection) {
+ mpd_executeCommand(connection,"clear\n");
+}
+
+void mpd_sendPlayCommand(mpd_Connection * connection, int songNum) {
+ char * string = malloc(strlen("play")+25);
+ sprintf(string,"play \"%i\"\n",songNum);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendStopCommand(mpd_Connection * connection) {
+ mpd_executeCommand(connection,"stop\n");
+}
+
+void mpd_sendPauseCommand(mpd_Connection * connection) {
+ mpd_executeCommand(connection,"pause\n");
+}
+
+void mpd_sendNextCommand(mpd_Connection * connection) {
+ mpd_executeCommand(connection,"next\n");
+}
+
+void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
+ char * string = malloc(strlen("move")+25);
+ sprintf(string,"move \"%i\" \"%i\"\n",from,to);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
+ char * string = malloc(strlen("swap")+25);
+ sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
+ char * string = malloc(strlen("seek")+25);
+ sprintf(string,"seek \"%i\" \"%i\"\n",song,time);
+ mpd_sendInfoCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendUpdateCommand(mpd_Connection * connection) {
+ mpd_executeCommand(connection,"update\n");
+}
+
+void mpd_sendPrevCommand(mpd_Connection * connection) {
+ mpd_executeCommand(connection,"previous\n");
+}
+
+void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
+ char * string = malloc(strlen("repeat")+25);
+ sprintf(string,"repeat \"%i\"\n",repeatMode);
+ mpd_executeCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
+ char * string = malloc(strlen("random")+25);
+ sprintf(string,"random \"%i\"\n",randomMode);
+ mpd_executeCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
+ char * string = malloc(strlen("volume")+25);
+ sprintf(string,"volume \"%i\"\n",volumeChange);
+ mpd_executeCommand(connection,string);
+ free(string);
+}
+
+void mpd_sendCommandListBegin(mpd_Connection * connection) {
+ if(connection->commandList) {
+ strcpy(connection->errorStr,"already in command list mode");
+ connection->error = 1;
+ return;
+ }
+ connection->commandList = 1;
+ mpd_executeCommand(connection,"command_list_begin\n");
+}
+
+void mpd_sendCommandListEnd(mpd_Connection * connection) {
+ if(!connection->commandList) {
+ strcpy(connection->errorStr,"not in command list mode");
+ connection->error = 1;
+ return;
+ }
+ connection->commandList = 0;
+ mpd_executeCommand(connection,"command_list_end\n");
+}
diff --git a/libmpdclient.h b/libmpdclient.h
--- /dev/null
+++ b/libmpdclient.h
@@ -0,0 +1,369 @@
+/* libmpdclient
+ * (c)2002 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#ifndef LIBMPDCLIENT_H
+#define LIBMPDCLIENT_H
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#define MPD_BUFFER_MAX_LENGTH 50000
+#define MPD_WELCOME_MESSAGE "OK MPD "
+
+#define MPD_ERROR_TIMEOUT 10 /* timeout trying to talk to mpd */
+#define MPD_ERROR_SYSTEM 11 /* system error */
+#define MPD_ERROR_UNKHOST 12 /* unknown host */
+#define MPD_ERROR_CONNPORT 13 /* problems connecting to port on host */
+#define MPD_ERROR_NOTMPD 14 /* mpd not running on port at host */
+#define MPD_ERROR_NORESPONSE 15 /* no response on attempting to connect */
+#define MPD_ERROR_SENDING 16 /* error sending command */
+#define MPD_ERROR_CONNCLOSED 17 /* connection closed by mpd */
+#define MPD_ERROR_ACK 18 /* ACK returned! */
+#define MPD_ERROR_BUFFEROVERRUN 19 /* Buffer was overrun! */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* internal stuff don't touch this struct */
+typedef struct _mpd_ReturnElement {
+ char * name;
+ char * value;
+} mpd_ReturnElement;
+
+/* mpd_Connection
+ * holds info about connection to mpd
+ * use error, and errorStr to detect errors
+ */
+typedef struct _mpd_Connection {
+ /* use this to check the version of mpd */
+ int version[3];
+ /* IMPORTANT, you want to get the error messages from here */
+ char errorStr[MPD_BUFFER_MAX_LENGTH+1];
+ /* this will be set to 1 if there is an error, 0 if not */
+ int error;
+ /* DON'T TOUCH any of the rest of this stuff */
+ int sock;
+ char buffer[MPD_BUFFER_MAX_LENGTH+1];
+ int buflen;
+ int bufstart;
+ int doneProcessing;
+ int commandList;
+ mpd_ReturnElement * returnElement;
+ struct timeval timeout;
+} mpd_Connection;
+
+/* mpd_newConnection
+ * use this to open a new connection
+ * you should use mpd_closeConnection, when your done with the connection,
+ * even if an error has occurred
+ * _timeout_ is the connection timeout period in seconds
+ */
+mpd_Connection * mpd_newConnection(const char * host, int port, float timeout);
+
+void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout);
+
+/* mpd_closeConnection
+ * use this to close a connection and free'ing subsequent memory
+ */
+void mpd_closeConnection(mpd_Connection * connection);
+
+/* mpd_clearError
+ * clears error
+ */
+void mpd_clearError(mpd_Connection * connection);
+
+/* STATUS STUFF */
+
+/* use these with status.state to determine what state the player is in */
+#define MPD_STATUS_STATE_UNKNOWN 0
+#define MPD_STATUS_STATE_STOP 1
+#define MPD_STATUS_STATE_PLAY 2
+#define MPD_STATUS_STATE_PAUSE 3
+
+/* us this with status.volume to determine if mpd has volume support */
+#define MPD_STATUS_NO_VOLUME -1
+
+/* mpd_Status
+ * holds info return from status command
+ */
+typedef struct mpd_Status {
+ /* 0-100, or MPD_STATUS_NO_VOLUME when there is no volume support */
+ int volume;
+ /* 1 if repeat is on, 0 otherwise */
+ int repeat;
+ /* 1 if random is on, 0 otherwise */
+ int random;
+ /* playlist length */
+ int playlistLength;
+ /* playlist, use this to determine when the playlist has changed */
+ long long playlist;
+ /* use with MPD_STATUS_STATE_* to determine state of player */
+ int state;
+ /* if in PLAY or PAUSE state, this is the number of the currently
+ * playing song in the playlist, beginning with 0
+ */
+ int song;
+ /* time in seconds that have elapsed in the currently playing/paused
+ * song
+ */
+ int elapsedTime;
+ /* length in seconds of the currently playing/paused song */
+ int totalTime;
+ /* current bit rate in kbs */
+ int bitRate;
+ /* error */
+ char * error;
+} mpd_Status;
+
+/* mpd_getStatus
+ * returns status info, be sure to free it with mpd_freeStatus()
+ */
+mpd_Status * mpd_getStatus(mpd_Connection * connection);
+
+/* mpd_freeStatus
+ * free's status info malloc'd and returned by mpd_getStatus
+ */
+void mpd_freeStatus(mpd_Status * status);
+
+/* SONG STUFF */
+
+#define MPD_SONG_NO_TIME -1
+
+/* mpd_Song
+ * for storing song info returned by mpd
+ */
+typedef struct _mpd_Song {
+ /* filename of song */
+ char * file;
+ /* artist, maybe NULL if there is no tag */
+ char * artist;
+ /* title, maybe NULL if there is no tag */
+ char * title;
+ /* album, maybe NULL if there is no tag */
+ char * album;
+ /* track, maybe NULL if there is no tag */
+ char * track;
+ /* length of song in seconds, check that it is not MPD_SONG_NO_TIME */
+ int time;
+} mpd_Song;
+
+/* mpd_newSong
+ * use to allocate memory for a new mpd_Song
+ * file, artist, etc all initialized to NULL
+ * if your going to assign values to file, artist, etc
+ * be sure to malloc or strdup the memory
+ * use mpd_freeSong to free the memory for the mpd_Song, it will also
+ * free memory for file, artist, etc, so don't do it yourself
+ */
+mpd_Song * mpd_newSong();
+
+/* mpd_freeSong
+ * use to free memory allocated by mpd_newSong
+ * also it will free memory pointed to by file, artist, etc, so be careful
+ */
+void mpd_freeSong(mpd_Song * song);
+
+/* mpd_songDup
+ * works like strDup, but for a mpd_Song
+ */
+mpd_Song * mpd_songDup(mpd_Song * song);
+
+/* DIRECTORY STUFF */
+
+/* mpd_Directory
+ * used to store info fro directory (right now that just the path)
+ */
+typedef struct _mpd_Directory {
+ char * path;
+} mpd_Directory;
+
+/* mpd_newDirectory
+ * allocates memory for a new directory
+ * use mpd_freeDirectory to free this memory
+ */
+mpd_Directory * mpd_newDirectory ();
+
+/* mpd_freeDirectory
+ * used to free memory allocated with mpd_newDirectory, and it frees
+ * path of mpd_Directory, so be careful
+ */
+void mpd_freeDirectory(mpd_Directory * directory);
+
+/* mpd_directoryDup
+ * works like strdup, but for mpd_Directory
+ */
+mpd_Directory * mpd_directoryDup(mpd_Directory * directory);
+
+/* PLAYLISTFILE STUFF */
+
+/* mpd_PlaylistFile
+ * stores info about playlist file returned by lsinfo
+ */
+typedef struct _mpd_PlaylistFile {
+ char * path;
+} mpd_PlaylistFile;
+
+/* mpd_newPlaylistFile
+ * allocates memory for new mpd_PlaylistFile, path is set to NULL
+ * free this memory with mpd_freePlaylistFile
+ */
+mpd_PlaylistFile * mpd_newPlaylistFile();
+
+/* mpd_freePlaylist
+ * free memory allocated for freePlaylistFile, will also free
+ * path, so be careful
+ */
+void mpd_freePlaylistFile(mpd_PlaylistFile * playlist);
+
+/* mpd_playlistFileDup
+ * works like strdup, but for mpd_PlaylistFile
+ */
+mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist);
+
+/* INFO ENTITY STUFF */
+
+/* the type of entity returned from one of the commands that generates info
+ * use in conjunction with mpd_InfoEntity.type
+ */
+#define MPD_INFO_ENTITY_TYPE_DIRECTORY 0
+#define MPD_INFO_ENTITY_TYPE_SONG 1
+#define MPD_INFO_ENTITY_TYPE_PLAYLISTFILE 2
+
+/* mpd_InfoEntity
+ * stores info on stuff returned info commands
+ */
+typedef struct mpd_InfoEntity {
+ /* the type of entity, use with MPD_INFO_ENTITY_TYPE_* to determine
+ * what this entity is (song, directory, etc...)
+ */
+ int type;
+ /* the actual data you want, mpd_Song, mpd_Directory, etc */
+ union {
+ mpd_Directory * directory;
+ mpd_Song * song;
+ mpd_PlaylistFile * playlistFile;
+ } info;
+} mpd_InfoEntity;
+
+mpd_InfoEntity * mpd_newInfoEntity();
+
+void mpd_freeInfoEntity(mpd_InfoEntity * entity);
+
+/* INFO COMMANDS AND STUFF */
+
+/* use this function to loop over after calling Info/Listall functions */
+mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection);
+
+/* songNum of -1, means to display the whole list */
+void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songNum);
+
+void mpd_sendListallCommand(mpd_Connection * connection, const char * dir);
+
+void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir);
+
+void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir);
+
+#define MPD_TABLE_ARTIST 0
+#define MPD_TABLE_ALBUM 1
+#define MPD_TABLE_TITLE 2
+#define MPD_TABLE_FILENAME 3
+
+void mpd_sendSearchCommand(mpd_Connection * connection, int table,
+ const char * str);
+
+void mpd_sendFindCommand(mpd_Connection * connection, int table,
+ const char * str);
+
+/* LIST TAG COMMANDS */
+
+/* use this function fetch next artist entry, be sure to free the returned
+ * string. NULL means there are no more. Best used with sendListArtists
+ */
+char * mpd_getNextArtist(mpd_Connection * connection);
+
+char * mpd_getNextAlbum(mpd_Connection * connection);
+
+/* list artist or albums by artist, arg1 should be set to the artist if
+ * listing albums by a artist, otherwise NULL for listing all artists or albums
+ */
+void mpd_sendListCommand(mpd_Connection * connection, int table,
+ const char * arg1);
+
+void mpd_sendListAlbumsCommand(mpd_Connection * connection,
+ const char * artist);
+
+/* SIMPLE COMMANDS */
+
+void mpd_sendAddCommand(mpd_Connection * connection, const char * file);
+
+void mpd_sendDeleteCommand(mpd_Connection * connection, int songNum);
+
+void mpd_sendSaveCommand(mpd_Connection * connection, const char * name);
+
+void mpd_sendLoadCommand(mpd_Connection * connection, const char * name);
+
+void mpd_sendRmCommand(mpd_Connection * connection, const char * name);
+
+void mpd_sendShuffleCommand(mpd_Connection * connection);
+
+void mpd_sendClearCommand(mpd_Connection * connection);
+
+/* use this to start playing at the beginning, useful when in random mode */
+#define MPD_PLAY_AT_BEGINNING -1
+
+void mpd_sendPlayCommand(mpd_Connection * connection, int songNum);
+
+void mpd_sendStopCommand(mpd_Connection * connection);
+
+void mpd_sendPauseCommand(mpd_Connection * connection);
+
+void mpd_sendNextCommand(mpd_Connection * connection);
+
+void mpd_sendPrevCommand(mpd_Connection * connection);
+
+void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to);
+
+void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2);
+
+void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time);
+
+void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode);
+
+void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode);
+
+void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange);
+
+void mpd_sendUpdateCommand(mpd_Connection * connection);
+
+/* after executing a command, when your done with it to get its status
+ * (you want to check connection->error for an error)
+ */
+void mpd_finishCommand(mpd_Connection * connection);
+
+/* command list stuff, use this to do things like add files very quickly */
+void mpd_sendCommandListBegin(mpd_Connection * connection);
+
+void mpd_sendCommandListEnd(mpd_Connection * connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/m4/CVS/Entries b/m4/CVS/Entries
--- /dev/null
+++ b/m4/CVS/Entries
@@ -0,0 +1,2 @@
+/glib-2.0.m4/1.1/Tue Sep 9 20:11:05 2003//
+D
diff --git a/m4/CVS/Repository b/m4/CVS/Repository
--- /dev/null
+++ b/m4/CVS/Repository
@@ -0,0 +1 @@
+apps/mpc-curses/m4
diff --git a/m4/CVS/Root b/m4/CVS/Root
--- /dev/null
+++ b/m4/CVS/Root
@@ -0,0 +1 @@
+/usr/global/var/cvs/dev
diff --git a/m4/glib-2.0.m4 b/m4/glib-2.0.m4
--- /dev/null
+++ b/m4/glib-2.0.m4
@@ -0,0 +1,212 @@
+# Configure paths for GLIB
+# Owen Taylor 1997-2001
+
+dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
+dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject or
+dnl gthread is specified in MODULES, pass to pkg-config
+dnl
+AC_DEFUN(AM_PATH_GLIB_2_0,
+[dnl
+dnl Get the cflags and libraries from pkg-config
+dnl
+AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run a test GLIB program],
+ , enable_glibtest=yes)
+
+ pkg_config_args=glib-2.0
+ for module in . $4
+ do
+ case "$module" in
+ gmodule)
+ pkg_config_args="$pkg_config_args gmodule-2.0"
+ ;;
+ gobject)
+ pkg_config_args="$pkg_config_args gobject-2.0"
+ ;;
+ gthread)
+ pkg_config_args="$pkg_config_args gthread-2.0"
+ ;;
+ esac
+ done
+
+ AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+
+ no_glib=""
+
+ if test x$PKG_CONFIG != xno ; then
+ if $PKG_CONFIG --atleast-pkgconfig-version 0.7 ; then
+ :
+ else
+ echo *** pkg-config too old; version 0.7 or better required.
+ no_glib=yes
+ PKG_CONFIG=no
+ fi
+ else
+ no_glib=yes
+ fi
+
+ min_glib_version=ifelse([$1], ,2.0.0,$1)
+ AC_MSG_CHECKING(for GLIB - version >= $min_glib_version)
+
+ if test x$PKG_CONFIG != xno ; then
+ ## don't try to run the test against uninstalled libtool libs
+ if $PKG_CONFIG --uninstalled $pkg_config_args; then
+ echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH"
+ enable_glibtest=no
+ fi
+
+ if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then
+ :
+ else
+ no_glib=yes
+ fi
+ fi
+
+ if test x"$no_glib" = x ; then
+ GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
+ GOBJECT_QUERY=`$PKG_CONFIG --variable=gobject_query glib-2.0`
+ GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
+
+ GLIB_CFLAGS=`$PKG_CONFIG --cflags $pkg_config_args`
+ GLIB_LIBS=`$PKG_CONFIG --libs $pkg_config_args`
+ glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.0 | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_glibtest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $GLIB_CFLAGS"
+ LIBS="$GLIB_LIBS $LIBS"
+dnl
+dnl Now check if the installed GLIB is sufficiently new. (Also sanity
+dnl checks the results of pkg-config to some extent)
+dnl
+ rm -f conf.glibtest
+ AC_TRY_RUN([
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main ()
+{
+ int major, minor, micro;
+ char *tmp_version;
+
+ system ("touch conf.glibtest");
+
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = g_strdup("$min_glib_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) {
+ printf("%s, bad version string\n", "$min_glib_version");
+ exit(1);
+ }
+
+ if ((glib_major_version != $glib_config_major_version) ||
+ (glib_minor_version != $glib_config_minor_version) ||
+ (glib_micro_version != $glib_config_micro_version))
+ {
+ printf("\n*** 'pkg-config --modversion glib-2.0' returned %d.%d.%d, but GLIB (%d.%d.%d)\n",
+ $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
+ glib_major_version, glib_minor_version, glib_micro_version);
+ printf ("*** was found! If pkg-config was correct, then it is best\n");
+ printf ("*** to remove the old version of GLib. You may also be able to fix the error\n");
+ printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+ printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+ printf("*** required on your system.\n");
+ printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
+ printf("*** to point to the correct configuration files\n");
+ }
+ else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
+ (glib_minor_version != GLIB_MINOR_VERSION) ||
+ (glib_micro_version != GLIB_MICRO_VERSION))
+ {
+ printf("*** GLIB header files (version %d.%d.%d) do not match\n",
+ GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
+ printf("*** library (version %d.%d.%d)\n",
+ glib_major_version, glib_minor_version, glib_micro_version);
+ }
+ else
+ {
+ if ((glib_major_version > major) ||
+ ((glib_major_version == major) && (glib_minor_version > minor)) ||
+ ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** An old version of GLIB (%d.%d.%d) was found.\n",
+ glib_major_version, glib_minor_version, glib_micro_version);
+ printf("*** You need a version of GLIB newer than %d.%d.%d. The latest version of\n",
+ major, minor, micro);
+ printf("*** GLIB is always available from ftp://ftp.gtk.org.\n");
+ printf("***\n");
+ printf("*** If you have already installed a sufficiently new version, this error\n");
+ printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
+ printf("*** being found. The easiest way to fix this is to remove the old version\n");
+ printf("*** of GLIB, but you can also set the PKG_CONFIG environment to point to the\n");
+ printf("*** correct copy of pkg-config. (In this case, you will have to\n");
+ printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+ printf("*** so that the correct libraries are found at run-time))\n");
+ }
+ }
+ return 1;
+}
+],, no_glib=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_glib" = x ; then
+ AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version))
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$PKG_CONFIG" = "no" ; then
+ echo "*** A new enough version of pkg-config was not found."
+ echo "*** See http://www.freedesktop.org/software/pkgconfig/"
+ else
+ if test -f conf.glibtest ; then
+ :
+ else
+ echo "*** Could not run GLIB test program, checking why..."
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $GLIB_CFLAGS"
+ LIBS="$LIBS $GLIB_LIBS"
+ AC_TRY_LINK([
+#include <glib.h>
+#include <stdio.h>
+], [ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding GLIB or finding the wrong"
+ echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means GLIB is incorrectly installed."])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ GLIB_CFLAGS=""
+ GLIB_LIBS=""
+ GLIB_GENMARSHAL=""
+ GOBJECT_QUERY=""
+ GLIB_MKENUMS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(GLIB_CFLAGS)
+ AC_SUBST(GLIB_LIBS)
+ AC_SUBST(GLIB_GENMARSHAL)
+ AC_SUBST(GOBJECT_QUERY)
+ AC_SUBST(GLIB_MKENUMS)
+ rm -f conf.glibtest
+])
diff --git a/main.c b/main.c
--- /dev/null
+++ b/main.c
@@ -0,0 +1,122 @@
+/*
+ * $Id: main.c,v 1.5 2004/03/16 14:34:49 kalle Exp $
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <glib.h>
+
+#include "config.h"
+#include "libmpdclient.h"
+#include "mpc.h"
+#include "options.h"
+#include "command.h"
+#include "screen.h"
+
+
+static mpd_client_t *mpc = NULL;
+
+void
+exit_and_cleanup(void)
+{
+ screen_exit();
+ if( mpc )
+ {
+ if( mpc_error(mpc) )
+ fprintf(stderr,"Error: %s\n", mpc_error_str(mpc));
+ mpc_close(mpc);
+ }
+}
+
+void
+catch_sigint( int sig )
+{
+ printf( "\nExiting...\n");
+ exit(EXIT_SUCCESS);
+}
+
+int
+main(int argc, char *argv[])
+{
+ options_t *options;
+ struct sigaction act;
+ int counter;
+
+ /* parse command line options */
+ options_init();
+ options = options_parse(argc, argv);
+
+ /* setup signal behavior - SIGINT */
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = 0;
+ act.sa_handler = catch_sigint;
+ if( sigaction( SIGINT, &act, NULL )<0 )
+ {
+ perror("signal");
+ exit(EXIT_FAILURE);
+ }
+ /* setup signal behavior - SIGWINCH */
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = 0;
+ act.sa_handler = screen_resized;
+ if( sigaction( SIGWINCH, &act, NULL )<0 )
+ {
+ perror("sigaction()");
+ exit(EXIT_FAILURE);
+ }
+ /* setup signal behavior - SIGTERM */
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = 0;
+ act.sa_handler = catch_sigint;
+ if( sigaction( SIGTERM, &act, NULL )<0 )
+ {
+ perror("sigaction()");
+ exit(EXIT_FAILURE);
+ }
+
+ /* set xterm title */
+ printf("%c]0;%s%c", '\033', PACKAGE " v" VERSION, '\007');
+
+ /* install exit function */
+ atexit(exit_and_cleanup);
+
+ /* connect to our music player daemon */
+ mpc = mpc_connect(options->host, options->port);
+ if( mpc_error(mpc) )
+ exit(EXIT_FAILURE);
+
+ screen_init();
+#if 0
+ mpc_update(mpc);
+ mpc_update_filelist(mpc);
+ screen_paint(mpc);
+ // sleep(1);
+#endif
+
+ counter=0;
+ while( !mpc_error(mpc) )
+ {
+ command_t cmd;
+
+ if( !counter )
+ {
+ mpc_update(mpc);
+ mpd_finishCommand(mpc->connection);
+ counter=10;
+ }
+ else
+ counter--;
+
+ screen_update(mpc);
+
+ if( (cmd=get_keyboard_command()) != CMD_NONE )
+ {
+ screen_cmd(mpc, cmd);
+ counter=0;
+ }
+ }
+
+ exit(EXIT_FAILURE);
+}
diff --git a/mpc.c b/mpc.c
--- /dev/null
+++ b/mpc.c
@@ -0,0 +1,329 @@
+/*
+ * $Id: mpc.c,v 1.5 2004/03/17 23:19:21 kalle Exp $
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <glib.h>
+
+#include "config.h"
+#include "support.h"
+#include "libmpdclient.h"
+#include "mpc.h"
+#include "options.h"
+
+void
+mpc_update_song(mpd_client_t *c)
+{
+ mpd_InfoEntity *entity;
+
+ if( c->song )
+ {
+ mpd_freeSong(c->song);
+ c->song = NULL;
+ }
+
+ mpd_sendPlaylistInfoCommand(c->connection, c->status->song);
+ while( (entity=mpd_getNextInfoEntity(c->connection)) )
+ {
+ mpd_Song *song = entity->info.song;
+
+ if(c->connection->error)
+ {
+ fprintf(stderr,"error: %s\n",c->connection->errorStr);
+ exit(EXIT_FAILURE);
+ }
+ if(entity->type!=MPD_INFO_ENTITY_TYPE_SONG) {
+ mpd_freeInfoEntity(entity);
+ fprintf(stderr,
+ "error: type != MPD_INFO_ENTITY_TYPE_SONG [%d]\n",
+ entity->type);
+ exit(EXIT_FAILURE);
+ }
+ c->song = mpd_songDup(song);
+ mpd_freeInfoEntity(entity);
+ }
+}
+
+int
+mpc_close(mpd_client_t *c)
+{
+ if( c->connection )
+ mpd_closeConnection(c->connection);
+ if( c->cwd )
+ free( c->cwd );
+
+ return 0;
+}
+
+mpd_client_t *
+mpc_connect(char *host, int port)
+{
+ mpd_Connection *connection;
+ mpd_client_t *c;
+
+ connection = mpd_newConnection(host, port, 10);
+ if( connection==NULL )
+ {
+ fprintf(stderr, "mpd_newConnection to %s:%d failed!\n", host, port);
+ exit(EXIT_FAILURE);
+ }
+
+ c = malloc(sizeof(mpd_client_t));
+ memset(c, 0, sizeof(mpd_client_t));
+ c->connection = connection;
+ c->cwd = strdup("");
+
+ return c;
+}
+
+int
+mpc_error(mpd_client_t *c)
+{
+ if( c == NULL || c->connection == NULL )
+ return 1;
+ if( c->connection->error )
+ return 1;
+
+ return 0;
+}
+
+char *
+mpc_error_str(mpd_client_t *c)
+{
+ if( c == NULL || c->connection == NULL )
+ return "Not connected";
+
+ if( c->connection && c->connection->errorStr )
+ return c->connection->errorStr;
+
+ return NULL;
+}
+
+
+
+int
+mpc_free_playlist(mpd_client_t *c)
+{
+ GList *list;
+
+ if( c==NULL || c->playlist==NULL )
+ return -1;
+
+ list=g_list_first(c->playlist);
+
+ while( list!=NULL )
+ {
+ mpd_Song *song = (mpd_Song *) list->data;
+
+ mpd_freeSong(song);
+ list=list->next;
+ }
+ g_list_free(c->playlist);
+ c->playlist=NULL;
+ c->playlist_length=0;
+
+ return 0;
+}
+
+int
+mpc_update_playlist(mpd_client_t *c)
+{
+ mpd_InfoEntity *entity;
+
+ // fprintf(stderr, "mpc_update_playlist(): status->playlist = %d\n", c->status->playlist);
+
+ if( c->playlist )
+ mpc_free_playlist(c);
+
+ c->playlist_length=0;
+ mpd_sendPlaylistInfoCommand(c->connection,-1);
+ while( (entity=mpd_getNextInfoEntity(c->connection)) )
+ {
+ if(entity->type==MPD_INFO_ENTITY_TYPE_SONG)
+ {
+ mpd_Song *song = mpd_songDup(entity->info.song);
+
+ c->playlist = g_list_append(c->playlist, (gpointer) song);
+ c->playlist_length++;
+ }
+ mpd_freeInfoEntity(entity);
+ }
+ c->playlist_id = c->status->playlist;
+ c->playlist_updated = 1;
+ c->song_id = -1;
+
+ return 0;
+}
+
+int
+mpc_playlist_get_song_index(mpd_client_t *c, char *filename)
+{
+ GList *list = c->playlist;
+ int i=0;
+
+ while( list )
+ {
+ mpd_Song *song = (mpd_Song *) list->data;
+ if( strcmp(song->file, filename ) == 0 )
+ return i;
+ list=list->next;
+ i++;
+ }
+ return -1;
+}
+
+mpd_Song *
+mpc_playlist_get_song(mpd_client_t *c, int n)
+{
+ return (mpd_Song *) g_list_nth_data(c->playlist, n);
+}
+
+char *
+mpc_get_song_name(mpd_Song *song)
+{
+ if( song->title )
+ {
+ if( song->artist )
+ {
+ static char buf[512];
+
+ snprintf(buf, 512, "%s - %s", song->artist, song->title);
+ return utf8(buf);
+ }
+ else
+ {
+ return utf8(song->title);
+ }
+ }
+ return utf8(basename(song->file));
+}
+
+int
+mpc_update(mpd_client_t *c)
+{
+ if( c->status )
+ {
+ mpd_freeStatus(c->status);
+ }
+
+ c->status = mpd_getStatus(c->connection);
+
+ // if( c->playlist == NULL || c->playlist_id!=c->status->playlist )
+ if( c->playlist_id!=c->status->playlist )
+ mpc_update_playlist(c);
+
+ // if( c->song == NULL || c->status->song != c->song_id )
+ if( c->status->song != c->song_id )
+ {
+ c->song = mpc_playlist_get_song(c, c->status->song);
+ c->song_id = c->status->song;
+ c->song_updated = 1;
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+int
+mpc_free_filelist(mpd_client_t *c)
+{
+ GList *list;
+
+ if( c==NULL || c->filelist==NULL )
+ return -1;
+
+ list=g_list_first(c->filelist);
+
+ while( list!=NULL )
+ {
+ filelist_entry_t *entry = list->data;
+
+ if( entry->entity )
+ mpd_freeInfoEntity(entry->entity);
+ free(entry);
+ list=list->next;
+ }
+ g_list_free(c->filelist);
+ c->filelist=NULL;
+ c->filelist_length=0;
+
+ return 0;
+}
+
+
+
+int
+mpc_update_filelist(mpd_client_t *c)
+{
+ mpd_InfoEntity *entity;
+
+ if( c->filelist )
+ mpc_free_filelist(c);
+
+ c->filelist_length=0;
+
+ // mpd_sendListallCommand(conn,"");
+ mpd_sendLsInfoCommand(c->connection, c->cwd);
+
+ if( c->cwd && c->cwd[0] )
+ {
+ /* add a dummy entry for ./.. */
+ filelist_entry_t *entry = malloc(sizeof(filelist_entry_t));
+ memset(entry, 0, sizeof(filelist_entry_t));
+ entry->entity = NULL;
+ c->filelist = g_list_append(c->filelist, (gpointer) entry);
+ c->filelist_length++;
+ }
+
+ while( (entity=mpd_getNextInfoEntity(c->connection)) )
+ {
+ filelist_entry_t *entry = malloc(sizeof(filelist_entry_t));
+
+ memset(entry, 0, sizeof(filelist_entry_t));
+ entry->entity = entity;
+ c->filelist = g_list_append(c->filelist, (gpointer) entry);
+ c->filelist_length++;
+ }
+
+ c->filelist_updated = 1;
+
+ mpd_finishCommand(c->connection);
+
+ mpc_filelist_set_selected(c);
+
+ return 0;
+}
+
+int
+mpc_filelist_set_selected(mpd_client_t *c)
+{
+ GList *list = c->filelist;
+
+ while( list )
+ {
+ filelist_entry_t *entry = list->data;
+ mpd_InfoEntity *entity = entry->entity ;
+
+ if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
+ {
+ mpd_Song *song = entity->info.song;
+
+ if( mpc_playlist_get_song_index(c, song->file) >= 0 )
+ entry->selected = 1;
+ else
+ entry->selected = 0;
+ }
+
+ list=list->next;
+ }
+ return 0;
+}
diff --git a/mpc.h b/mpc.h
--- /dev/null
+++ b/mpc.h
@@ -0,0 +1,45 @@
+typedef struct
+{
+ char selected;
+ mpd_InfoEntity *entity;
+} filelist_entry_t;
+
+typedef struct
+{
+ mpd_Connection *connection;
+ mpd_Status *status;
+
+ mpd_Song *song;
+ int song_id;
+ int song_updated;
+
+ GList *playlist;
+ int playlist_length;
+ long long playlist_id;
+ int playlist_updated;
+
+ char *cwd;
+ GList *filelist;
+ int filelist_length;
+ int filelist_updated;
+
+} mpd_client_t;
+
+
+int mpc_close(mpd_client_t *c);
+
+mpd_client_t *mpc_connect(char *host, int port);
+
+int mpc_update(mpd_client_t *c);
+int mpc_update_playlist(mpd_client_t *c);
+
+int mpc_update_filelist(mpd_client_t *c);
+int mpc_filelist_set_selected(mpd_client_t *c);
+int mpc_set_cwd(mpd_client_t *c, char *dir);
+
+mpd_Song *mpc_playlist_get_song(mpd_client_t *c, int n);
+char *mpc_get_song_name(mpd_Song *song);
+int mpc_playlist_get_song_index(mpd_client_t *c, char *filename);
+
+int mpc_error(mpd_client_t *c);
+char *mpc_error_str(mpd_client_t *c);
diff --git a/options.c b/options.c
--- /dev/null
+++ b/options.c
@@ -0,0 +1,107 @@
+/*
+ * $Id: options.c,v 1.7 2004/03/17 11:34:36 kalle Exp $
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <popt.h>
+
+#include "config.h"
+#include "options.h"
+#include "command.h"
+
+static options_t options;
+
+static struct poptOption optionsTable[] = {
+#ifdef DEBUG
+ { "debug", 'D', 0, 0, 'D', "Enable debug output." },
+#endif
+ { "version", 'V', 0, 0, 'V', "Display version information." },
+ { "keys", 'k', 0, 0, 'k', "Display key bindings." },
+ { "port", 'p', POPT_ARG_INT, &options.port, 0,
+ "Connect to server on port [" DEFAULT_PORT_STR "].", "PORT" },
+ { "host", 'h', POPT_ARG_STRING, &options.host, 0,
+ "Connect to server [" DEFAULT_HOST "].", "HOSTNAME" },
+ POPT_AUTOHELP
+ { NULL, 0, 0, NULL, 0 }
+};
+
+static void
+usage(poptContext optCon, int exitcode, char *error, char *addl)
+{
+ poptPrintUsage(optCon, stderr, 0);
+ if (error)
+ fprintf(stderr, "%s: %s0", error, addl);
+ exit(exitcode);
+}
+
+options_t *
+options_parse( int argc, char **argv )
+{
+ int c;
+ poptContext optCon; /* context for parsing command-line options */
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+ while ((c = poptGetNextOpt(optCon)) >= 0)
+ {
+ switch (c)
+ {
+#ifdef DEBUG
+ case 'D':
+ options.debug = 1;
+ break;
+#endif
+ case 'V':
+ printf("Version " VERSION "\n");
+ exit(EXIT_SUCCESS);
+ case 'k':
+ command_dump_keys();
+ exit(EXIT_SUCCESS);
+ default:
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ poptStrerror(c));
+ poptFreeContext(optCon);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ if (c < -1)
+ {
+ /* an error occurred during option processing */
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
+ poptStrerror(c));
+ poptFreeContext(optCon);
+ exit(EXIT_FAILURE);
+ }
+
+ poptFreeContext(optCon);
+ return &options;
+}
+
+void
+options_init( void )
+{
+ char *value;
+
+ memset(&options, 0, sizeof(options_t));
+ if( (value=getenv(MPD_HOST_ENV)) )
+ options.host = strdup(value);
+ else
+ options.host = strdup(DEFAULT_HOST);
+ if( (value=getenv(MPD_PORT_ENV)) )
+ options.port = atoi(value);
+ else
+ options.port = DEFAULT_PORT;
+}
+
+
+options_t *
+options_get(void)
+{
+ return &options;
+}
diff --git a/options.h b/options.h
--- /dev/null
+++ b/options.h
@@ -0,0 +1,16 @@
+
+#define MPD_HOST_ENV "MPD_HOST"
+#define MPD_PORT_ENV "MPD_PORT"
+
+
+typedef struct
+{
+ char *host;
+ int port;
+
+} options_t;
+
+void options_init(void);
+options_t *options_parse(int argc, char **argv);
+options_t *get_options(void);
+
diff --git a/out b/out
--- /dev/null
+++ b/out
@@ -0,0 +1,4 @@
+charset: ANSI_X3.4-1968
+\e[1;30r\e[0;10m\e[4l\e[39;49m\e[?25l\e[?1c\e[39;49m\e[0;10m\e[H\e[J\e[0;10;1mMusic Player Client - Playlist\e[1;69H\e[0;10mVolume 80%\r\e[2d\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[H\e[J\e[0;10;1mMusic Player Client - Playlist\e[1;69H\e[0;10mVolume 80%\r\e[2d\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[29;1H\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[30;1H\e[0;10;1mStopped! \e[0;10m\e[H\e[J\e[0;10;1mMusic Player Client - Playlist\e[1;69H\e[0;10mVolume 80%\r\e[2d\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[29;1H\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[30;1H\e[0;10;1mStopped! \e[0;10m
+Exiting...
+\e[30;1H\e[?25h\e[?0c\r
\ No newline at end of file
diff --git a/screen.c b/screen.c
--- /dev/null
+++ b/screen.c
@@ -0,0 +1,520 @@
+/*
+ * $Id: screen.c,v 1.10 2004/03/17 14:50:12 kalle Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#include "libmpdclient.h"
+#include "mpc.h"
+#include "command.h"
+#include "screen.h"
+#include "screen_play.h"
+#include "screen_file.h"
+#include "screen_help.h"
+#include "screen_search.h"
+
+#define STATUS_MESSAGE_TIMEOUT 3
+
+static screen_t *screen = NULL;
+
+static void
+switch_screen_mode(screen_mode_t new_mode, mpd_client_t *c)
+{
+ if( new_mode == screen->mode )
+ return;
+
+ switch(screen->mode)
+ {
+ case SCREEN_PLAY_WINDOW:
+ play_close(screen, c);
+ break;
+ case SCREEN_FILE_WINDOW:
+ file_close(screen, c);
+ break;
+ case SCREEN_SEARCH_WINDOW:
+ search_close(screen, c);
+ break;
+ case SCREEN_HELP_WINDOW:
+ help_close(screen, c);
+ break;
+ }
+
+ screen->mode = new_mode;
+ screen->painted = 0;
+
+ switch(screen->mode)
+ {
+ case SCREEN_PLAY_WINDOW:
+ play_open(screen, c);
+ break;
+ case SCREEN_FILE_WINDOW:
+ file_open(screen, c);
+ break;
+ case SCREEN_SEARCH_WINDOW:
+ search_open(screen, c);
+ break;
+ case SCREEN_HELP_WINDOW:
+ help_open(screen, c);
+ break;
+ }
+}
+
+static void
+paint_top_window(char *header, int volume, int clear)
+{
+ static int prev_volume = -1;
+ WINDOW *w = screen->top_window.w;
+
+ if(clear)
+ {
+ wclear(w);
+ }
+
+ if(prev_volume!=volume || clear)
+ {
+ char buf[12];
+
+ wattron(w, A_BOLD);
+ mvwaddstr(w, 0, 0, header );
+ wattroff(w, A_BOLD);
+ if( volume==MPD_STATUS_NO_VOLUME )
+ {
+ snprintf(buf, 12, "Volume n/a ");
+ }
+ else
+ {
+ snprintf(buf, 12, "Volume %3d%%", volume);
+ }
+ mvwaddstr(w, 0, screen->top_window.cols-12, buf);
+
+ mvwhline(w, 1, 0, ACS_HLINE, screen->top_window.cols);
+
+ wrefresh(w);
+ }
+}
+
+static void
+paint_progress_window(mpd_client_t *c)
+{
+ double p;
+ int width;
+
+ if( c->status==NULL || !IS_PLAYING(c->status->state) )
+ {
+ mvwhline(screen->progress_window.w, 0, 0, ACS_HLINE,
+ screen->progress_window.cols);
+ wrefresh(screen->progress_window.w);
+ return;
+ }
+
+ p = ((double) c->status->elapsedTime) / ((double) c->status->totalTime);
+
+ width = (int) (p * (double) screen->progress_window.cols);
+
+ mvwhline(screen->progress_window.w,
+ 0, 0,
+ ACS_HLINE,
+ screen->progress_window.cols);
+
+ whline(screen->progress_window.w, '=', width-1);
+
+ mvwaddch(screen->progress_window.w, 0, width-1, 'O');
+ wrefresh(screen->progress_window.w);
+}
+
+static void
+paint_status_window(mpd_client_t *c)
+{
+ WINDOW *w = screen->status_window.w;
+ mpd_Status *status = c->status;
+ mpd_Song *song = c->song;
+ int x = 0;
+
+ if( time(NULL) - screen->status_timestamp <= STATUS_MESSAGE_TIMEOUT )
+ return;
+
+ wmove(w, 0, 0);
+ wclrtoeol(w);
+
+ switch(status->state)
+ {
+ case MPD_STATUS_STATE_STOP:
+ wattron(w, A_BOLD);
+ waddstr(w, "Stopped! ");
+ wattroff(w, A_BOLD);
+ break;
+ case MPD_STATUS_STATE_PLAY:
+ waddstr(w, "Playing:");
+ break;
+ case MPD_STATUS_STATE_PAUSE:
+ wattron(w, A_BOLD);
+ waddstr(w, "Paused:");
+ wattroff(w, A_BOLD);
+ break;
+ default:
+ waddstr(w, "Warning: Music Player Daemon in unknown state!");
+ break;
+ }
+ x += 10;
+
+ if( IS_PLAYING(status->state) && song )
+ {
+ mvwaddstr(w, 0, x, mpc_get_song_name(song));
+ }
+
+
+ /* time */
+ if( IS_PLAYING(status->state) )
+ {
+ x = screen->status_window.cols - strlen(screen->buf);
+
+ if( c->status->repeat )
+ mvwaddstr(w, 0, x-3, "<R>");
+ else
+ mvwaddstr(w, 0, x-3, " ");
+
+ snprintf(screen->buf, screen->buf_size,
+ " [%i:%02i/%i:%02i] ",
+ status->elapsedTime/60, status->elapsedTime%60,
+ status->totalTime/60, status->totalTime%60 );
+ mvwaddstr(w, 0, x, screen->buf);
+
+ }
+
+
+ wrefresh(w);
+}
+
+
+
+int
+screen_exit(void)
+{
+ endwin();
+ if( screen )
+ {
+ screen->playlist = list_window_free(screen->playlist);
+ screen->filelist = list_window_free(screen->filelist);
+ screen->helplist = list_window_free(screen->helplist);
+ free(screen->buf);
+ free(screen);
+ screen = NULL;
+ }
+ return 0;
+}
+
+void
+screen_resized(int sig)
+{
+ screen_exit();
+ if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
+ {
+ fprintf(stderr, "Error: Screen to small!\n");
+ exit(EXIT_FAILURE);
+ }
+ screen_init();
+}
+
+void
+screen_status_message(mpd_client_t *c, char *msg)
+{
+ WINDOW *w = screen->status_window.w;
+
+ wmove(w, 0, 0);
+ wclrtoeol(w);
+ wattron(w, A_BOLD);
+ waddstr(w, msg);
+ wattroff(w, A_BOLD);
+ wrefresh(w);
+ screen->status_timestamp = time(NULL);
+}
+
+int
+screen_init(void)
+{
+ /* initialize the curses library */
+ initscr();
+ start_color();
+ use_default_colors();
+ /* tell curses not to do NL->CR/NL on output */
+ nonl();
+ /* take input chars one at a time, no wait for \n */
+ cbreak();
+ /* don't echo input */
+ noecho();
+ /* set cursor invisible */
+ curs_set(0);
+ /* return from getch() without blocking */
+ // nodelay(stdscr, TRUE);
+ keypad(stdscr, TRUE);
+ timeout(100); /*void wtimeout(WINDOW *win, int delay);*/
+
+
+ if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
+ {
+ fprintf(stderr, "Error: Screen to small!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ screen = malloc(sizeof(screen_t));
+ memset(screen, 0, sizeof(screen_t));
+ screen->mode = SCREEN_PLAY_WINDOW;
+ screen->cols = COLS;
+ screen->rows = LINES;
+ screen->buf = malloc(screen->cols);
+ screen->buf_size = screen->cols;
+ screen->painted = 0;
+
+ /* create top window */
+ screen->top_window.rows = 2;
+ screen->top_window.cols = screen->cols;
+ screen->top_window.w = newwin(screen->top_window.rows,
+ screen->top_window.cols,
+ 0, 0);
+ leaveok(screen->top_window.w, TRUE);
+ keypad(screen->top_window.w, TRUE);
+
+ /* create main window */
+ screen->main_window.rows = screen->rows-4;
+ screen->main_window.cols = screen->cols;
+ screen->main_window.w = newwin(screen->main_window.rows,
+ screen->main_window.cols,
+ 2,
+ 0);
+ screen->playlist = list_window_init( screen->main_window.w,
+ screen->main_window.cols,
+ screen->main_window.rows );
+ screen->filelist = list_window_init( screen->main_window.w,
+ screen->main_window.cols,
+ screen->main_window.rows );
+ screen->helplist = list_window_init( screen->main_window.w,
+ screen->main_window.cols,
+ screen->main_window.rows );
+ leaveok(screen->main_window.w, TRUE);
+ keypad(screen->main_window.w, TRUE);
+
+ /* create progress window */
+ screen->progress_window.rows = 1;
+ screen->progress_window.cols = screen->cols;
+ screen->progress_window.w = newwin(screen->progress_window.rows,
+ screen->progress_window.cols,
+ screen->rows-2,
+ 0);
+ leaveok(screen->progress_window.w, TRUE);
+
+ /* create status window */
+ screen->status_window.rows = 1;
+ screen->status_window.cols = screen->cols;
+ screen->status_window.w = newwin(screen->status_window.rows,
+ screen->status_window.cols,
+ screen->rows-1,
+ 0);
+ leaveok(screen->status_window.w, FALSE);
+ keypad(screen->status_window.w, TRUE);
+
+ return 0;
+}
+
+void
+screen_paint(mpd_client_t *c)
+{
+ switch(screen->mode)
+ {
+ case SCREEN_PLAY_WINDOW:
+ paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
+ play_paint(screen, c);
+ break;
+ case SCREEN_FILE_WINDOW:
+ paint_top_window(file_get_header(c), c->status->volume, 1);
+ file_paint(screen, c);
+ break;
+ case SCREEN_SEARCH_WINDOW:
+ paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 1);
+ search_paint(screen, c);
+ break;
+ case SCREEN_HELP_WINDOW:
+ paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1);
+ help_paint(screen, c);
+ break;
+ }
+
+ paint_progress_window(c);
+ paint_status_window(c);
+ screen->painted = 1;
+}
+
+void
+screen_update(mpd_client_t *c)
+{
+ if( !screen->painted )
+ return screen_paint(c);
+
+ switch(screen->mode)
+ {
+ case SCREEN_PLAY_WINDOW:
+ paint_top_window(TOP_HEADER_PLAY, c->status->volume, 0);
+ play_update(screen, c);
+ break;
+ case SCREEN_FILE_WINDOW:
+ paint_top_window(file_get_header(c), c->status->volume, 0);
+ file_update(screen, c);
+ break;
+ case SCREEN_SEARCH_WINDOW:
+ paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 0);
+ search_update(screen, c);
+ break;
+ case SCREEN_HELP_WINDOW:
+ paint_top_window(TOP_HEADER_HELP, c->status->volume, 0);
+ help_update(screen, c);
+ break;
+ }
+ paint_progress_window(c);
+ paint_status_window(c);
+}
+
+void
+screen_cmd(mpd_client_t *c, command_t cmd)
+{
+ int n;
+ char buf[256];
+ screen_mode_t new_mode = screen->mode;
+
+ switch(screen->mode)
+ {
+ case SCREEN_PLAY_WINDOW:
+ if( play_cmd(screen, c, cmd) )
+ return;
+ break;
+ case SCREEN_FILE_WINDOW:
+ if( file_cmd(screen, c, cmd) )
+ return;
+ break;
+ case SCREEN_SEARCH_WINDOW:
+ if( search_cmd(screen, c, cmd) )
+ return;
+ break;
+ case SCREEN_HELP_WINDOW:
+ if( help_cmd(screen, c, cmd) )
+ return;
+ break;
+ }
+
+ switch(cmd)
+ {
+ case CMD_PLAY:
+ mpd_sendPlayCommand(c->connection, screen->playlist->selected);
+ mpd_finishCommand(c->connection);
+ break;
+ case CMD_PAUSE:
+ mpd_sendPauseCommand(c->connection);
+ mpd_finishCommand(c->connection);
+ break;
+ case CMD_STOP:
+ mpd_sendStopCommand(c->connection);
+ mpd_finishCommand(c->connection);
+ break;
+ case CMD_TRACK_NEXT:
+ if( IS_PLAYING(c->status->state) )
+ {
+ mpd_sendNextCommand(c->connection);
+ mpd_finishCommand(c->connection);
+ }
+ break;
+ case CMD_TRACK_PREVIOUS:
+ if( IS_PLAYING(c->status->state) )
+ {
+ mpd_sendPrevCommand(c->connection);
+ mpd_finishCommand(c->connection);
+ }
+ break;
+ case CMD_SHUFFLE:
+ mpd_sendShuffleCommand(c->connection);
+ mpd_finishCommand(c->connection);
+ screen_status_message(c, "Shuffled playlist!");
+ break;
+ case CMD_CLEAR:
+ mpd_sendClearCommand(c->connection);
+ mpd_finishCommand(c->connection);
+ file_clear_highlights(c);
+ screen_status_message(c, "Cleared playlist!");
+ break;
+ case CMD_REPEAT:
+ n = !c->status->repeat;
+ mpd_sendRepeatCommand(c->connection, n);
+ mpd_finishCommand(c->connection);
+ snprintf(buf, 256, "Repeat is %s", n ? "On" : "Off");
+ screen_status_message(c, buf);
+ break;
+ case CMD_RANDOM:
+ n = !c->status->random;
+ mpd_sendRandomCommand(c->connection, n);
+ mpd_finishCommand(c->connection);
+ snprintf(buf, 256, "Random is %s", n ? "On" : "Off");
+ screen_status_message(c, buf);
+ break;
+ case CMD_VOLUME_UP:
+ mpd_sendVolumeCommand(c->connection, 1);
+ mpd_finishCommand(c->connection);
+ if( c->status->volume!=MPD_STATUS_NO_VOLUME )
+ {
+ snprintf(buf, 256, "Volume %d%%", c->status->volume+1);
+ screen_status_message(c, buf);
+ }
+ break;
+ case CMD_VOLUME_DOWN:
+ mpd_sendVolumeCommand(c->connection, -1);
+ mpd_finishCommand(c->connection);
+ if( c->status->volume!=MPD_STATUS_NO_VOLUME )
+ {
+ snprintf(buf, 256, "Volume %d%%", c->status->volume-1);
+ screen_status_message(c, buf);
+ }
+ break;
+ case CMD_SCREEN_PREVIOUS:
+ if( screen->mode > SCREEN_PLAY_WINDOW )
+ new_mode = screen->mode - 1;
+ else
+ new_mode = SCREEN_HELP_WINDOW-1;
+ switch_screen_mode(new_mode, c);
+ break;
+ case CMD_SCREEN_NEXT:
+ new_mode = screen->mode + 1;
+ if( new_mode >= SCREEN_HELP_WINDOW )
+ new_mode = SCREEN_PLAY_WINDOW;
+ switch_screen_mode(new_mode, c);
+ break;
+ case CMD_SCREEN_PLAY:
+ switch_screen_mode(SCREEN_PLAY_WINDOW, c);
+ break;
+ case CMD_SCREEN_FILE:
+ switch_screen_mode(SCREEN_FILE_WINDOW, c);
+ break;
+ case CMD_SCREEN_SEARCH:
+ switch_screen_mode(SCREEN_SEARCH_WINDOW, c);
+ break;
+ case CMD_SCREEN_HELP:
+ switch_screen_mode(SCREEN_HELP_WINDOW, c);
+ break;
+ case CMD_QUIT:
+ exit(EXIT_SUCCESS);
+ case CMD_NONE:
+ case CMD_DELETE:
+ case CMD_SELECT:
+ case CMD_LIST_PREVIOUS:
+ case CMD_LIST_NEXT:
+ case CMD_LIST_FIRST:
+ case CMD_LIST_LAST:
+ case CMD_LIST_NEXT_PAGE:
+ case CMD_LIST_PREVIOUS_PAGE:
+ break;
+ }
+
+}
+
+
diff --git a/screen.h b/screen.h
--- /dev/null
+++ b/screen.h
@@ -0,0 +1,71 @@
+#ifndef SCREEN_H
+#define SCREEN_H
+#include <ncurses.h>
+#include "screen_utils.h"
+
+#define TOP_HEADER_PREFIX "Music Player Client - "
+#define TOP_HEADER_PLAY TOP_HEADER_PREFIX "Playlist"
+#define TOP_HEADER_FILE TOP_HEADER_PREFIX "Browse"
+#define TOP_HEADER_HELP TOP_HEADER_PREFIX "Help"
+#define TOP_HEADER_SEARCH TOP_HEADER_PREFIX "Search"
+
+#define SCREEN_MIN_COLS 14
+#define SCREEN_MIN_ROWS 5
+
+#define IS_PLAYING(s) (s==MPD_STATUS_STATE_PLAY)
+
+typedef enum
+{
+ SCREEN_PLAY_WINDOW = 0,
+ SCREEN_FILE_WINDOW,
+ SCREEN_HELP_WINDOW,
+ SCREEN_SEARCH_WINDOW
+
+} screen_mode_t;
+
+
+
+typedef struct
+{
+ WINDOW *w;
+ int rows, cols;
+
+} window_t;
+
+
+
+typedef struct
+{
+ window_t top_window;
+ window_t main_window;
+ window_t progress_window;
+ window_t status_window;
+ time_t status_timestamp;
+
+ list_window_t *playlist;
+ list_window_t *filelist;
+ list_window_t *helplist;
+
+ int cols, rows;
+
+ screen_mode_t mode;
+
+ char *buf;
+ size_t buf_size;
+
+ int painted;
+
+} screen_t;
+
+
+
+int screen_init(void);
+int screen_exit(void);
+void screen_resized(int sig);
+void screen_status_message(mpd_client_t *c, char *msg);
+char *screen_error(void);
+void screen_paint(mpd_client_t *c);
+void screen_update(mpd_client_t *c);
+void screen_cmd(mpd_client_t *c, command_t cmd);
+
+#endif
diff --git a/screen_file.c b/screen_file.c
--- /dev/null
+++ b/screen_file.c
@@ -0,0 +1,333 @@
+/*
+ * $Id: screen_file.c,v 1.9 2004/03/18 09:33:07 kalle Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#include "config.h"
+#include "support.h"
+#include "libmpdclient.h"
+#include "mpc.h"
+#include "command.h"
+#include "screen.h"
+#include "screen_file.h"
+
+
+static char *
+list_callback(int index, int *highlight, void *data)
+{
+ mpd_client_t *c = (mpd_client_t *) data;
+ filelist_entry_t *entry;
+ mpd_InfoEntity *entity;
+
+ *highlight = 0;
+ if( (entry=(filelist_entry_t *) g_list_nth_data(c->filelist, index))==NULL )
+ return NULL;
+
+ entity = entry->entity;
+ *highlight = entry->selected;
+
+ if( entity == NULL )
+ {
+ return "[Back]";
+ }
+ if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
+ {
+
+ mpd_Directory *dir = entity->info.directory;
+
+ return utf8(basename(dir->path));
+ }
+ else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
+ {
+ mpd_Song *song = entity->info.song;
+ return mpc_get_song_name(song);
+ }
+
+ return NULL;
+}
+
+static void
+change_directory(screen_t *screen, mpd_client_t *c)
+{
+ list_window_t *w = screen->filelist;
+ filelist_entry_t *entry;
+ mpd_InfoEntity *entity;
+
+ entry = ( filelist_entry_t *) g_list_nth_data(c->filelist, w->selected);
+ if( entry==NULL )
+ return;
+
+ entity = entry->entity;
+ if( entity==NULL )
+ {
+ char *parent = g_path_get_dirname(c->cwd);
+
+ if( strcmp(parent,".") == 0 )
+ {
+ parent[0] = '\0';
+ }
+ if( c->cwd )
+ free(c->cwd);
+ c->cwd = parent;
+ }
+ else
+ if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY)
+ {
+ mpd_Directory *dir = entity->info.directory;
+ if( c->cwd )
+ free(c->cwd);
+ c->cwd = strdup(dir->path);
+ }
+ else
+ return;
+
+ mpc_update_filelist(c);
+ list_window_reset(w);
+}
+
+
+static int
+add_directory(mpd_client_t *c, char *dir)
+{
+ mpd_InfoEntity *entity;
+ GList *subdir_list = NULL;
+ GList *list = NULL;
+ char buf[80];
+
+ snprintf(buf, 80, "Adding directory %s...\n", dir);
+ screen_status_message(c, buf);
+
+ mpd_sendLsInfoCommand(c->connection, dir);
+ mpd_sendCommandListBegin(c->connection);
+ while( (entity=mpd_getNextInfoEntity(c->connection)) )
+ {
+ if( entity->type==MPD_INFO_ENTITY_TYPE_SONG )
+ {
+ mpd_Song *song = entity->info.song;
+ mpd_sendAddCommand(c->connection, song->file);
+ mpd_freeInfoEntity(entity);
+ }
+ else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
+ {
+ subdir_list = g_list_append(subdir_list, (gpointer) entity);
+ }
+ else
+ mpd_freeInfoEntity(entity);
+ }
+ mpd_sendCommandListEnd(c->connection);
+ mpd_finishCommand(c->connection);
+
+ list = g_list_first(subdir_list);
+ while( list!=NULL )
+ {
+ mpd_Directory *dir;
+
+ entity = list->data;
+ dir = entity->info.directory;
+ add_directory(c, dir->path);
+ mpd_freeInfoEntity(entity);
+ list->data=NULL;
+ list=list->next;
+ }
+ g_list_free(subdir_list);
+ return 0;
+}
+
+static void
+select_entry(screen_t *screen, mpd_client_t *c)
+{
+ list_window_t *w = screen->filelist;
+ filelist_entry_t *entry;
+
+ entry = ( filelist_entry_t *) g_list_nth_data(c->filelist, w->selected);
+ if( entry==NULL || entry->entity==NULL)
+ return;
+
+ if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY )
+ {
+ mpd_Directory *dir = entry->entity->info.directory;
+ add_directory(c, dir->path);
+ return;
+ }
+
+ if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG )
+ return; /* No support for adding dirs... :( */
+
+ entry->selected = !entry->selected;
+
+ if( entry->selected )
+ {
+ if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
+ {
+ char buf[80];
+ mpd_Song *song = entry->entity->info.song;
+
+ mpd_sendAddCommand(c->connection, song->file);
+ mpd_finishCommand(c->connection);
+
+ snprintf(buf, 80,
+ "Adding \'%s\' to playlist\n",
+ mpc_get_song_name(song));
+ screen_status_message(c, buf);
+ }
+ }
+ else
+ {
+ if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG )
+ {
+ int i;
+ char buf[80];
+ mpd_Song *song = entry->entity->info.song;
+
+ i = mpc_playlist_get_song_index(c, song->file);
+ if( i>=0 )
+ {
+ mpd_sendDeleteCommand(c->connection, i);
+ mpd_finishCommand(c->connection);
+ snprintf(buf, 80,
+ "Removed \'%s\' from playlist\n",
+ mpc_get_song_name(song));
+ screen_status_message(c, buf);
+ }
+ }
+ }
+
+}
+
+void
+file_clear_highlights(mpd_client_t *c)
+{
+ GList *list = g_list_first(c->filelist);
+
+ while( list )
+ {
+ filelist_entry_t *entry = list->data;
+
+ entry->selected = 0;
+ list = list->next;
+ }
+}
+
+void
+file_clear_highlight(mpd_client_t *c, mpd_Song *song)
+{
+ GList *list = g_list_first(c->filelist);
+
+ if( !song )
+ return;
+
+ while( list )
+ {
+ filelist_entry_t *entry = list->data;
+ mpd_InfoEntity *entity = entry->entity;
+
+ if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
+ {
+ mpd_Song *song2 = entity->info.song;
+
+ if( strcmp(song->file, song2->file) == 0 )
+ {
+ entry->selected = 0;
+ }
+ }
+ list = list->next;
+ }
+}
+
+char *
+file_get_header(mpd_client_t *c)
+{
+ static char buf[64];
+
+ snprintf(buf, 64,
+ TOP_HEADER_FILE ": %s ",
+ basename(c->cwd)
+ );
+
+ return buf;
+}
+
+void
+file_open(screen_t *screen, mpd_client_t *c)
+{
+ if( c->filelist == NULL )
+ {
+ mpc_update_filelist(c);
+ }
+}
+
+void
+file_close(screen_t *screen, mpd_client_t *c)
+{
+}
+
+void
+file_paint(screen_t *screen, mpd_client_t *c)
+{
+ list_window_t *w = screen->filelist;
+
+ w->clear = 1;
+
+ list_window_paint(screen->filelist, list_callback, (void *) c);
+ wrefresh(screen->filelist->w);
+}
+
+void
+file_update(screen_t *screen, mpd_client_t *c)
+{
+ if( c->filelist_updated )
+ {
+ file_paint(screen, c);
+ c->filelist_updated = 0;
+ return;
+ }
+ list_window_paint(screen->filelist, list_callback, (void *) c);
+ wrefresh(screen->filelist->w);
+}
+
+
+int
+file_cmd(screen_t *screen, mpd_client_t *c, command_t cmd)
+{
+ switch(cmd)
+ {
+ case CMD_PLAY:
+ change_directory(screen, c);
+ break;
+ case CMD_LIST_PREVIOUS:
+ list_window_previous(screen->filelist);
+ screen->filelist->repaint=1;
+ break;
+ case CMD_SELECT:
+ select_entry(screen, c);
+ /* continue and select next item... */
+ case CMD_LIST_NEXT:
+ list_window_next(screen->filelist, c->filelist_length);
+ screen->filelist->repaint=1;
+ break;
+ case CMD_LIST_FIRST:
+ list_window_first(screen->filelist);
+ screen->filelist->repaint = 1;
+ break;
+ case CMD_LIST_LAST:
+ list_window_last(screen->filelist, c->filelist_length);
+ screen->filelist->repaint = 1;
+ break;
+ case CMD_LIST_NEXT_PAGE:
+ list_window_next_page(screen->filelist, c->filelist_length);
+ screen->filelist->repaint = 1;
+ break;
+ case CMD_LIST_PREVIOUS_PAGE:
+ list_window_previous_page(screen->filelist);
+ screen->filelist->repaint = 1;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
diff --git a/screen_file.h b/screen_file.h
--- /dev/null
+++ b/screen_file.h
@@ -0,0 +1,14 @@
+
+char *file_get_header(mpd_client_t *c);
+
+void file_clear_highlight(mpd_client_t *c, mpd_Song *song);
+void file_clear_highlights(mpd_client_t *c);
+
+void file_open(screen_t *screen, mpd_client_t *c);
+void file_close(screen_t *screen, mpd_client_t *c);
+
+void file_paint(screen_t *screen, mpd_client_t *c);
+void file_update(screen_t *screen, mpd_client_t *c);
+
+int file_cmd(screen_t *screen, mpd_client_t *c, command_t cmd);
+
diff --git a/screen_help.c b/screen_help.c
--- /dev/null
+++ b/screen_help.c
@@ -0,0 +1,165 @@
+/*
+ * $Id: screen_help.c,v 1.8 2004/03/17 13:40:25 kalle Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#include "config.h"
+#include "libmpdclient.h"
+#include "mpc.h"
+#include "command.h"
+#include "screen.h"
+#include "screen_help.h"
+
+typedef struct
+{
+ char highlight;
+ command_t command;
+ char *text;
+} help_text_row_t;
+
+static help_text_row_t help_text[] =
+{
+ { 1, CMD_NONE, " Keys " },
+ { 0, CMD_NONE, " --------" },
+ { 0, CMD_STOP, "Stop" },
+ { 0, CMD_PAUSE, "Pause" },
+ { 0, CMD_TRACK_NEXT, "Next track" },
+ { 0, CMD_TRACK_PREVIOUS, "Prevoius track (back)" },
+ { 0, CMD_VOLUME_DOWN, "Volume down" },
+ { 0, CMD_VOLUME_UP, "Volume up" },
+ { 0, CMD_NONE, " " },
+ { 0, CMD_LIST_NEXT, "Move cursor up" },
+ { 0, CMD_LIST_PREVIOUS, "Move cursor down" },
+ { 0, CMD_SCREEN_NEXT, "Change screen" },
+ { 0, CMD_SCREEN_HELP, "Help screen" },
+ { 0, CMD_SCREEN_PLAY, "Playlist screen" },
+ { 0, CMD_SCREEN_FILE, "Browse screen" },
+ { 0, CMD_QUIT, "Quit" },
+ { 0, CMD_NONE, " " },
+ { 0, CMD_NONE, " " },
+ { 1, CMD_NONE, " Keys - Playlist screen " },
+ { 0, CMD_NONE, " --------------------------" },
+ { 0, CMD_PLAY, "Play selected entry" },
+ { 0, CMD_DELETE, "Delete selected entry from platlist" },
+ { 0, CMD_SHUFFLE, "Shuffle playlist" },
+ { 0, CMD_CLEAR, "Clear playlist" },
+ { 0, CMD_REPEAT, "Toggle repeat mode" },
+ { 0, CMD_RANDOM, "Toggle random mode" },
+ { 0, CMD_NONE, " " },
+ { 0, CMD_NONE, " " },
+ { 1, CMD_NONE, " Keys - Browse screen " },
+ { 0, CMD_NONE, " ------------------------" },
+ { 0, CMD_PLAY, "Change to selected directory" },
+ { 0, CMD_SELECT, "Add/Remove selected file" },
+ { 0, CMD_NONE, " " },
+ { 0, CMD_NONE, " " },
+ { 1, CMD_NONE, " " PACKAGE " version " VERSION },
+ { 0, CMD_NONE, NULL }
+};
+
+static int help_text_rows = -1;
+
+
+
+static char *
+list_callback(int index, int *highlight, void *data)
+{
+ static char buf[256];
+
+ if( help_text_rows<0 )
+ {
+ help_text_rows = 0;
+ while( help_text[help_text_rows].text )
+ help_text_rows++;
+ }
+
+ *highlight = 0;
+ if( index<help_text_rows )
+ {
+ *highlight = help_text[index].highlight;
+ if( help_text[index].command == CMD_NONE )
+ return help_text[index].text;
+ snprintf(buf, 256,
+ "%20s : %s",
+ command_get_keys(help_text[index].command),
+ help_text[index].text);
+ return buf;
+ }
+
+ return NULL;
+}
+
+
+void
+help_open(screen_t *screen, mpd_client_t *c)
+{
+}
+
+void
+help_close(screen_t *screen, mpd_client_t *c)
+{
+}
+
+void
+help_paint(screen_t *screen, mpd_client_t *c)
+{
+ list_window_t *w = screen->helplist;
+
+ w->clear = 1;
+ list_window_paint(screen->helplist, list_callback, NULL);
+ wrefresh(screen->helplist->w);
+}
+
+void
+help_update(screen_t *screen, mpd_client_t *c)
+{
+ list_window_t *w = screen->helplist;
+
+ if( w->repaint )
+ {
+ list_window_paint(screen->helplist, list_callback, NULL);
+ wrefresh(screen->helplist->w);
+ w->repaint = 0;
+ }
+}
+
+
+int
+help_cmd(screen_t *screen, mpd_client_t *c, command_t cmd)
+{
+ switch(cmd)
+ {
+ case CMD_LIST_PREVIOUS:
+ list_window_previous(screen->helplist);
+ screen->helplist->repaint=1;
+ break;
+ case CMD_LIST_NEXT:
+ list_window_next(screen->helplist, help_text_rows);
+ screen->helplist->repaint=1;
+ break;
+ case CMD_LIST_FIRST:
+ list_window_first(screen->helplist);
+ screen->helplist->repaint = 1;
+ break;
+ case CMD_LIST_LAST:
+ list_window_last(screen->helplist, help_text_rows);
+ screen->helplist->repaint = 1;
+ break;
+ case CMD_LIST_PREVIOUS_PAGE:
+ list_window_previous_page(screen->helplist);
+ screen->helplist->repaint = 1;
+ break;
+ case CMD_LIST_NEXT_PAGE:
+ list_window_next_page(screen->helplist, help_text_rows);
+ screen->helplist->repaint = 1;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
diff --git a/screen_help.h b/screen_help.h
--- /dev/null
+++ b/screen_help.h
@@ -0,0 +1,9 @@
+
+void help_open(screen_t *screen, mpd_client_t *c);
+void help_close(screen_t *screen, mpd_client_t *c);
+
+void help_paint(screen_t *screen, mpd_client_t *c);
+void help_update(screen_t *screen, mpd_client_t *c);
+
+int help_cmd(screen_t *screen, mpd_client_t *c, command_t cmd);
+
diff --git a/screen_play.c b/screen_play.c
--- /dev/null
+++ b/screen_play.c
@@ -0,0 +1,129 @@
+/*
+ * $Id: screen_play.c,v 1.6 2004/03/17 14:49:31 kalle Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#include "libmpdclient.h"
+#include "mpc.h"
+#include "command.h"
+#include "screen.h"
+#include "screen_file.h"
+#include "screen_play.h"
+
+
+#define BUFSIZE 256
+
+static char *
+list_callback(int index, int *highlight, void *data)
+{
+ mpd_client_t *c = (mpd_client_t *) data;
+ mpd_Song *song;
+
+ *highlight = 0;
+ if( (song=mpc_playlist_get_song(c, index)) == NULL )
+ {
+ return NULL;
+ }
+
+ if( IS_PLAYING(c->status->state) && index==c->song_id )
+ {
+ *highlight = 1;
+ }
+
+ return mpc_get_song_name(song);
+}
+
+void
+play_open(screen_t *screen, mpd_client_t *c)
+{
+
+}
+
+void
+play_close(screen_t *screen, mpd_client_t *c)
+{
+}
+
+void
+play_paint(screen_t *screen, mpd_client_t *c)
+{
+ list_window_t *w = screen->playlist;
+
+ w->clear = 1;
+
+ list_window_paint(screen->playlist, list_callback, (void *) c);
+ wrefresh(screen->playlist->w);
+}
+
+void
+play_update(screen_t *screen, mpd_client_t *c)
+{
+ if( c->playlist_updated )
+ {
+ if( screen->playlist->selected >= c->playlist_length )
+ screen->playlist->selected = c->playlist_length-1;
+ if( screen->playlist->start >= c->playlist_length )
+ list_window_reset(screen->playlist);
+
+ play_paint(screen, c);
+ c->playlist_updated = 0;
+ }
+ else if( screen->playlist->repaint || 1)
+ {
+ list_window_paint(screen->playlist, list_callback, (void *) c);
+ wrefresh(screen->playlist->w);
+ screen->playlist->repaint = 0;
+ }
+}
+
+int
+play_cmd(screen_t *screen, mpd_client_t *c, command_t cmd)
+{
+ char buf[256];
+ mpd_Song *song;
+
+ switch(cmd)
+ {
+ case CMD_DELETE:
+ song = mpc_playlist_get_song(c, screen->playlist->selected);
+ file_clear_highlight(c, song);
+ mpd_sendDeleteCommand(c->connection, screen->playlist->selected);
+ mpd_finishCommand(c->connection);
+ snprintf(buf, 256,
+ "Removed \'%s\' from playlist!",
+ mpc_get_song_name(song));
+ screen_status_message(c, buf);
+ break;
+ case CMD_LIST_PREVIOUS:
+ list_window_previous(screen->playlist);
+ screen->playlist->repaint=1;
+ break;
+ case CMD_LIST_NEXT:
+ list_window_next(screen->playlist, c->playlist_length);
+ screen->playlist->repaint=1;
+ break;
+ case CMD_LIST_FIRST:
+ list_window_first(screen->playlist);
+ screen->playlist->repaint = 1;
+ break;
+ case CMD_LIST_LAST:
+ list_window_last(screen->playlist, c->playlist_length);
+ screen->playlist->repaint = 1;
+ case CMD_LIST_NEXT_PAGE:
+ list_window_next_page(screen->playlist, c->playlist_length);
+ screen->playlist->repaint = 1;
+ break;
+ case CMD_LIST_PREVIOUS_PAGE:
+ list_window_previous_page(screen->playlist);
+ screen->playlist->repaint = 1;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
diff --git a/screen_play.h b/screen_play.h
--- /dev/null
+++ b/screen_play.h
@@ -0,0 +1,10 @@
+
+
+void play_open(screen_t *screen, mpd_client_t *c);
+void play_close(screen_t *screen, mpd_client_t *c);
+
+void play_paint(screen_t *screen, mpd_client_t *c);
+void play_update(screen_t *screen, mpd_client_t *c);
+
+int play_cmd(screen_t *screen, mpd_client_t *c, command_t cmd);
+
diff --git a/screen_search.c b/screen_search.c
--- /dev/null
+++ b/screen_search.c
@@ -0,0 +1,37 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#include "libmpdclient.h"
+#include "config.h"
+#include "mpc.h"
+#include "command.h"
+#include "screen.h"
+#include "screen_search.h"
+
+void
+search_open(screen_t *screen, mpd_client_t *c)
+{
+}
+
+void
+search_close(screen_t *screen, mpd_client_t *c)
+{
+}
+
+void
+search_paint(screen_t *screen, mpd_client_t *c)
+{
+}
+
+void
+search_update(screen_t *screen, mpd_client_t *c)
+{
+}
+
+int
+search_cmd(screen_t *screen, mpd_client_t *c, command_t cmd)
+{
+ return 0;
+}
diff --git a/screen_search.h b/screen_search.h
--- /dev/null
+++ b/screen_search.h
@@ -0,0 +1,8 @@
+
+void search_open(screen_t *screen, mpd_client_t *c);
+void search_close(screen_t *screen, mpd_client_t *c);
+
+void search_paint(screen_t *screen, mpd_client_t *c);
+void search_update(screen_t *screen, mpd_client_t *c);
+
+int search_cmd(screen_t *screen, mpd_client_t *c, command_t cmd);
diff --git a/screen_utils.c b/screen_utils.c
--- /dev/null
+++ b/screen_utils.c
@@ -0,0 +1,152 @@
+/*
+ * $Id: screen_utils.c,v 1.4 2004/03/16 13:57:24 kalle Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#include "libmpdclient.h"
+#include "mpc.h"
+#include "command.h"
+#include "screen.h"
+
+
+
+list_window_t *
+list_window_init(WINDOW *w, int width, int height)
+{
+ list_window_t *lw;
+
+ lw = malloc(sizeof(list_window_t));
+ memset(lw, 0, sizeof(list_window_t));
+ lw->w = w;
+ lw->cols = width;
+ lw->rows = height;
+ lw->clear = 1;
+ return lw;
+}
+
+list_window_t *
+list_window_free(list_window_t *lw)
+{
+ if( lw )
+ {
+ memset(lw, 0, sizeof(list_window_t));
+ free(lw);
+ }
+ return NULL;
+}
+
+void
+list_window_reset(list_window_t *lw)
+{
+ lw->selected = 0;
+ lw->start = 0;
+ lw->clear = 1;
+}
+
+void
+list_window_set_selected(list_window_t *lw, int n)
+{
+ lw->selected=n;
+}
+
+void
+list_window_paint(list_window_t *lw,
+ list_window_callback_fn_t callback,
+ void *callback_data)
+{
+ int i;
+
+ while( lw->selected < lw->start )
+ {
+ lw->start--;
+ lw->clear=1;
+ }
+ while( lw->selected >= lw->start+lw->rows )
+ {
+ lw->start++;
+ lw->clear=1;
+ }
+ if( lw->clear )
+ {
+ wclear(lw->w);
+ lw->clear=0;
+ }
+
+ for(i=0; i<lw->rows; i++)
+ {
+ int highlight;
+ char *label;
+
+ label = (callback) (lw->start+i, &highlight, callback_data);
+ if( label )
+ {
+ wmove(lw->w, i, 0);
+ if( highlight )
+ wattron(lw->w, A_BOLD);
+ if( lw->start+i == lw->selected )
+ wattron(lw->w, A_REVERSE);
+
+ waddnstr(lw->w, label, lw->cols);
+
+ if( highlight )
+ wattroff(lw->w, A_BOLD);
+ if( lw->start+i == lw->selected )
+ wattroff(lw->w, A_REVERSE);
+ }
+ }
+}
+
+void
+list_window_next(list_window_t *lw, int length)
+{
+ if( lw->selected < length-1 )
+ lw->selected++;
+}
+
+void
+list_window_previous(list_window_t *lw)
+{
+ if( lw->selected > 0 )
+ lw->selected--;
+}
+
+void
+list_window_first(list_window_t *lw)
+{
+ lw->selected = 0;
+}
+
+void
+list_window_last(list_window_t *lw, int length)
+{
+ lw->selected = length-1;
+}
+
+void
+list_window_next_page(list_window_t *lw, int length)
+{
+ int step = lw->rows-1;
+ if( step<= 0 )
+ return;
+ if( lw->selected+step < length-1 )
+ lw->selected+=step;
+ else
+ return list_window_last(lw,length);
+}
+
+void
+list_window_previous_page(list_window_t *lw)
+{
+ int step = lw->rows-1;
+ if( step<= 0 )
+ return;
+ if( lw->selected-step > 0 )
+ lw->selected-=step;
+ else
+ list_window_first(lw);
+}
diff --git a/screen_utils.h b/screen_utils.h
--- /dev/null
+++ b/screen_utils.h
@@ -0,0 +1,42 @@
+
+typedef char * (*list_window_callback_fn_t) (int index,
+ int *highlight,
+ void *data);
+
+
+
+typedef struct
+{
+ WINDOW *w;
+ int rows, cols;
+
+ int start;
+ int selected;
+ int clear;
+ int repaint;
+
+} list_window_t;
+
+
+
+list_window_t *list_window_init(WINDOW *w, int width, int height);
+list_window_t *list_window_free(list_window_t *lw);
+
+
+void list_window_reset(list_window_t *lw);
+void list_window_set_selected(list_window_t *lw, int n);
+
+void list_window_paint(list_window_t *lw,
+ list_window_callback_fn_t callback,
+ void *callback_data);
+
+
+
+
+void list_window_previous(list_window_t *lw);
+void list_window_next(list_window_t *lw, int length);
+void list_window_first(list_window_t *lw);
+void list_window_last(list_window_t *lw, int length);
+void list_window_previous_page(list_window_t *lw);
+void list_window_next_page(list_window_t *lw, int length);
+
diff --git a/support.c b/support.c
--- /dev/null
+++ b/support.c
@@ -0,0 +1,64 @@
+/*
+ * $Id: support.c,v 1.2 2004/03/17 23:17:09 kalle Exp $
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#include "config.h"
+#include "support.h"
+
+#ifndef HAVE_LIBGEN_H
+
+char *
+remove_trailing_slash(char *path)
+{
+ int len;
+
+ if( path==NULL )
+ return NULL;
+
+ len=strlen(path);
+ if( len>1 && path[len-1] == '/' )
+ path[len-1] = '\0';
+
+ return path;
+}
+
+
+char *
+basename(char *path)
+{
+ char *end;
+
+ path = remove_trailing_slash(path);
+ end = path + strlen(path);
+
+ while( end>path && *end!='/' )
+ end--;
+
+ if( *end=='/' && end!=path )
+ return end+1;
+
+ return path;
+}
+
+#endif /* HAVE_LIBGEN_H */
+
+char *
+utf8(char *str)
+{
+ static const gchar *charset = NULL;
+ static gboolean locale_is_utf8 = FALSE;
+
+ if( !charset )
+ locale_is_utf8 = g_get_charset(&charset);
+
+ if( locale_is_utf8 )
+ return str;
+
+ return g_locale_from_utf8(str, -1, NULL, NULL, NULL);
+}
diff --git a/support.h b/support.h
--- /dev/null
+++ b/support.h
@@ -0,0 +1,9 @@
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#else
+char *basename(char *path);
+#endif
+
+char *utf8(char *str);
+