From ce8503f859345990a14be90bf89dadf7f73d7613 Mon Sep 17 00:00:00 2001
From: Lars-Dominik Braun <lars@6xq.net>
Date: Thu, 6 Jan 2011 16:25:40 +0100
Subject: BarReadline rewrite

Doesn't use c streams any more, allows multiplexing of fifo/stdin in all
situations.
---
 src/main.c        |  83 +++++++++++++++-------------------------
 src/main.h        |  10 ++---
 src/ui.c          |  55 +++++++++++++++------------
 src/ui.h          |  14 ++++---
 src/ui_act.c      |  30 ++++++++-------
 src/ui_act.h      |   4 +-
 src/ui_readline.c | 110 +++++++++++++++++++++++++++++++++++++++---------------
 src/ui_readline.h |  32 +++++++++++++---
 8 files changed, 196 insertions(+), 142 deletions(-)

(limited to 'src')

diff --git a/src/main.c b/src/main.c
index 14e7c89..54d6006 100644
--- a/src/main.c
+++ b/src/main.c
@@ -53,7 +53,7 @@ THE SOFTWARE.
 #include "ui_act.h"
 #include "ui_readline.h"
 
-typedef void (*BarKeyShortcutFunc_t) (BarApp_t *app, FILE *curFd);
+typedef void (*BarKeyShortcutFunc_t) (BarApp_t *app);
 
 /*	copy proxy settings to waitress handle
  */
@@ -94,17 +94,19 @@ static bool BarMainLoginUser (BarApp_t *app) {
 
 /*	ask for username/password if none were provided in settings
  */
-static void BarMainGetLoginCredentials (BarSettings_t *settings) {
+static void BarMainGetLoginCredentials (BarSettings_t *settings,
+		BarReadlineFds_t *input) {
 	if (settings->username == NULL) {
 		char nameBuf[100];
 		BarUiMsg (MSG_QUESTION, "Username: ");
-		BarReadlineStr (nameBuf, sizeof (nameBuf), 0, stdin);
+		BarReadlineStr (nameBuf, sizeof (nameBuf), input, BAR_RL_DEFAULT);
 		settings->username = strdup (nameBuf);
 	}
 	if (settings->password == NULL) {
 		char passBuf[100];
 		BarUiMsg (MSG_QUESTION, "Password: ");
-		BarReadlineStr (passBuf, sizeof (passBuf), 1, stdin);
+		BarReadlineStr (passBuf, sizeof (passBuf), input, BAR_RL_NOECHO);
+		write (STDIN_FILENO, "\n", 1);
 		settings->password = strdup (passBuf);
 	}
 }
@@ -136,8 +138,8 @@ static void BarMainGetInitialStation (BarApp_t *app) {
 	}
 	/* no autostart? ask the user */
 	if (app->curStation == NULL) {
-		app->curStation = BarUiSelectStation (&(app->ph), "Select station: ",
-				app->settings.sortOrder, stdin);
+		app->curStation = BarUiSelectStation (&app->ph, "Select station: ",
+				app->settings.sortOrder, &app->input);
 	}
 	if (app->curStation != NULL) {
 		BarUiPrintStation (app->curStation);
@@ -147,35 +149,12 @@ static void BarMainGetInitialStation (BarApp_t *app) {
 /*	wait for user input
  */
 static void BarMainHandleUserInput (BarApp_t *app) {
-	struct timeval selectTimeout;
-	fd_set readSetCopy;
-	char buf = '\0';
-
-	/* select modifies its arguments => copy the set */
-	memcpy (&readSetCopy, &app->readSet, sizeof (app->readSet));
-	selectTimeout.tv_sec = 1;
-	selectTimeout.tv_usec = 0;
-
-	/* in the meantime: wait for user actions */
-	if (select (app->maxFd, &readSetCopy, NULL, NULL, &selectTimeout) > 0) {
-		FILE *curFd = NULL;
-
-		if (FD_ISSET(app->selectFds[0], &readSetCopy)) {
-			curFd = stdin;
-		} else if (app->selectFds[1] != -1 && FD_ISSET(app->selectFds[1],
-				&readSetCopy)) {
-			curFd = app->ctlFd;
-		}
-		buf = fgetc (curFd);
-		if (buf == EOF) {
-			/* select() is going wild if fdset contains EOFed fd's */
-			FD_CLR (fileno (curFd), &app->readSet);
-		}
-
-		size_t i;
-		for (i = 0; i < BAR_KS_COUNT; i++) {
+	char buf[2];
+	if (BarReadline (buf, sizeof (buf), NULL, &app->input,
+			BAR_RL_FULLRETURN | BAR_RL_NOECHO, 1) > 0) {
+		for (size_t i = 0; i < BAR_KS_COUNT; i++) {
 			if (app->settings.keys[i] != BAR_KS_DISABLED &&
-					app->settings.keys[i] == buf) {
+					app->settings.keys[i] == buf[0]) {
 				static const BarKeyShortcutFunc_t idToF[] = {BarUiActHelp,
 						BarUiActLoveSong, BarUiActBanSong,
 						BarUiActAddMusic, BarUiActCreateStation,
@@ -188,11 +167,11 @@ static void BarMainHandleUserInput (BarApp_t *app) {
 						BarUiActPrintUpcoming, BarUiActSelectQuickMix,
 						BarUiActDebug, BarUiActBookmark, BarUiActVolDown,
 						BarUiActVolUp};
-				idToF[i] (app, curFd);
+				idToF[i] (app);
 				break;
 			}
-		}
-	}
+		} /* end for */
+	} /* end if */
 }
 
 /*	append current song to history list and move to the next song
@@ -345,7 +324,7 @@ static void BarMainPrintTime (BarApp_t *app) {
 static void BarMainLoop (BarApp_t *app) {
 	pthread_t playerThread;
 
-	BarMainGetLoginCredentials (&app->settings);
+	BarMainGetLoginCredentials (&app->settings, &app->input);
 
 	BarMainLoadProxy (&app->settings, &app->waith);
 
@@ -435,28 +414,26 @@ int main (int argc, char **argv) {
 	}
 
 	/* init fds */
-	FD_ZERO(&app.readSet);
-	app.selectFds[0] = fileno (stdin);
-	FD_SET(app.selectFds[0], &app.readSet);
+	FD_ZERO(&app.input.set);
+	app.input.fds[0] = STDIN_FILENO;
+	FD_SET(app.input.fds[0], &app.input.set);
 
 	BarGetXdgConfigDir (PACKAGE "/ctl", ctlPath, sizeof (ctlPath));
-	/* FIXME: why is r_+_ required? */
-	app.ctlFd = fopen (ctlPath, "r+");
-	if (app.ctlFd != NULL) {
-		app.selectFds[1] = fileno (app.ctlFd);
-		FD_SET(app.selectFds[1], &app.readSet);
+	/* open fifo read/write so it won't EOF if nobody writes to it */
+	assert (sizeof (app.input.fds) / sizeof (*app.input.fds) >= 2);
+	app.input.fds[1] = open (ctlPath, O_RDWR);
+	if (app.input.fds[1] != -1) {
+		FD_SET(app.input.fds[1], &app.input.set);
 		BarUiMsg (MSG_INFO, "Control fifo at %s opened\n", ctlPath);
-	} else {
-		app.selectFds[1] = -1;
 	}
-	app.maxFd = app.selectFds[0] > app.selectFds[1] ? app.selectFds[0] :
-			app.selectFds[1];
-	++app.maxFd;
+	app.input.maxfd = app.input.fds[0] > app.input.fds[1] ? app.input.fds[0] :
+			app.input.fds[1];
+	++app.input.maxfd;
 
 	BarMainLoop (&app);
 
-	if (app.ctlFd != NULL) {
-		fclose (app.ctlFd);
+	if (app.input.fds[1] != -1) {
+		close (app.input.fds[1]);
 	}
 
 	PianoDestroy (&app.ph);
diff --git a/src/main.h b/src/main.h
index 2ef651c..14d1368 100644
--- a/src/main.h
+++ b/src/main.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2010
+Copyright (c) 2008-2011
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,10 +27,9 @@ THE SOFTWARE.
 #include <piano.h>
 #include <waitress.h>
 
-#include <sys/select.h>
-
 #include "player.h"
 #include "settings.h"
+#include "ui_readline.h"
 
 typedef struct {
 	PianoHandle_t ph;
@@ -42,10 +41,7 @@ typedef struct {
 	PianoSong_t *songHistory;
 	PianoStation_t *curStation;
 	char doQuit;
-	fd_set readSet;
-	int maxFd;
-	int selectFds[2];
-	FILE *ctlFd;
+	BarReadlineFds_t input;
 } BarApp_t;
 
 #endif /* _MAIN_H */
diff --git a/src/ui.c b/src/ui.c
index 6166044..5df8cdc 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2010
+Copyright (c) 2008-2011
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -297,13 +297,16 @@ PianoStation_t **BarSortedStations (PianoStation_t *unsortedStations,
 
 /*	let user pick one station
  *	@param piano handle
+ *	@param prompt string
+ *	@param station list sort order
+ *	@param input fds
  *	@return pointer to selected station or NULL
  */
 PianoStation_t *BarUiSelectStation (PianoHandle_t *ph, const char *prompt,
-		BarStationSorting_t order, FILE *curFd) {
+		BarStationSorting_t order, BarReadlineFds_t *input) {
 	PianoStation_t **sortedStations = NULL, *retStation = NULL;
 	size_t stationCount, i;
-	int input;
+	int selected;
 
 	if (ph->stations == NULL) {
 		BarUiMsg (MSG_ERR, "No station available.\n");
@@ -323,12 +326,12 @@ PianoStation_t *BarUiSelectStation (PianoHandle_t *ph, const char *prompt,
 
 	BarUiMsg (MSG_QUESTION, prompt);
 	/* FIXME: using a _signed_ int is ugly */
-	if (BarReadlineInt (&input, curFd) == 0) {
+	if (BarReadlineInt (&selected, input) == 0) {
 		free (sortedStations);
 		return NULL;
 	}
-	if (input < stationCount) {
-		retStation = sortedStations[input];
+	if (selected < stationCount) {
+		retStation = sortedStations[selected];
 	}
 	free (sortedStations);
 	return retStation;
@@ -337,18 +340,18 @@ PianoStation_t *BarUiSelectStation (PianoHandle_t *ph, const char *prompt,
 /*	let user pick one song
  *	@param pianobar settings
  *	@param song list
- *	@param current fd
+ *	@param input fds
  *	@return pointer to selected item in song list or NULL
  */
 PianoSong_t *BarUiSelectSong (const BarSettings_t *settings,
-		PianoSong_t *startSong, FILE *curFd) {
+		PianoSong_t *startSong, BarReadlineFds_t *input) {
 	PianoSong_t *tmpSong = NULL;
 	int i = 0;
 
 	i = BarUiListSongs (settings, startSong);
 
 	BarUiMsg (MSG_QUESTION, "Select song: ");
-	if (BarReadlineInt (&i, curFd) == 0) {
+	if (BarReadlineInt (&i, input) == 0) {
 		return NULL;
 	}
 
@@ -363,9 +366,11 @@ PianoSong_t *BarUiSelectSong (const BarSettings_t *settings,
 
 /*	let user pick one artist
  *	@param artists (linked list)
+ *	@param input fds
  *	@return pointer to selected artist or NULL on abort
  */
-PianoArtist_t *BarUiSelectArtist (PianoArtist_t *startArtist, FILE *curFd) {
+PianoArtist_t *BarUiSelectArtist (PianoArtist_t *startArtist,
+		BarReadlineFds_t *input) {
 	PianoArtist_t *tmpArtist = NULL;
 	int i = 0;
 
@@ -377,7 +382,7 @@ PianoArtist_t *BarUiSelectArtist (PianoArtist_t *startArtist, FILE *curFd) {
 		tmpArtist = tmpArtist->next;
 	}
 	BarUiMsg (MSG_QUESTION, "Select artist: ");
-	if (BarReadlineInt (&i, curFd) == 0) {
+	if (BarReadlineInt (&i, input) == 0) {
 		return NULL;
 	}
 	tmpArtist = startArtist;
@@ -389,12 +394,11 @@ PianoArtist_t *BarUiSelectArtist (PianoArtist_t *startArtist, FILE *curFd) {
 }
 
 /*	search music: query, search request, return music id
- *	@param piano handle
- *	@param read data from fd
+ *	@param app handle
  *	@param allow seed suggestions if != NULL
  *	@return musicId or NULL on abort/error
  */
-char *BarUiSelectMusicId (BarApp_t *app, FILE *curFd, char *similarToId) {
+char *BarUiSelectMusicId (BarApp_t *app, char *similarToId) {
 	char *musicId = NULL;
 	char lineBuf[100], selectBuf[2];
 	PianoSearchResult_t searchResult;
@@ -402,7 +406,8 @@ char *BarUiSelectMusicId (BarApp_t *app, FILE *curFd, char *similarToId) {
 	PianoSong_t *tmpSong;
 
 	BarUiMsg (MSG_QUESTION, "Search for artist/title: ");
-	if (BarReadlineStr (lineBuf, sizeof (lineBuf), 0, curFd) > 0) {
+	if (BarReadlineStr (lineBuf, sizeof (lineBuf), &app->input,
+			BAR_RL_DEFAULT) > 0) {
 		if (strcmp ("?", lineBuf) == 0 && similarToId != NULL) {
 			PianoReturn_t pRet;
 			WaitressReturn_t wRet;
@@ -436,15 +441,17 @@ char *BarUiSelectMusicId (BarApp_t *app, FILE *curFd, char *similarToId) {
 				searchResult.artists != NULL) {
 			/* songs and artists found */
 			BarUiMsg (MSG_QUESTION, "Is this an [a]rtist or [t]rack name? ");
-			BarReadline (selectBuf, sizeof (selectBuf), "at", 1, 0, curFd);
+			BarReadline (selectBuf, sizeof (selectBuf), "at", &app->input,
+					BAR_RL_FULLRETURN, -1);
 			if (*selectBuf == 'a') {
-				tmpArtist = BarUiSelectArtist (searchResult.artists, curFd);
+				tmpArtist = BarUiSelectArtist (searchResult.artists,
+						&app->input);
 				if (tmpArtist != NULL) {
 					musicId = strdup (tmpArtist->musicId);
 				}
 			} else if (*selectBuf == 't') {
 				tmpSong = BarUiSelectSong (&app->settings, searchResult.songs,
-						curFd);
+						&app->input);
 				if (tmpSong != NULL) {
 					musicId = strdup (tmpSong->musicId);
 				}
@@ -452,13 +459,13 @@ char *BarUiSelectMusicId (BarApp_t *app, FILE *curFd, char *similarToId) {
 		} else if (searchResult.songs != NULL) {
 			/* songs found */
 			tmpSong = BarUiSelectSong (&app->settings, searchResult.songs,
-					curFd);
+					&app->input);
 			if (tmpSong != NULL) {
 				musicId = strdup (tmpSong->musicId);
 			}
 		} else if (searchResult.artists != NULL) {
 			/* artists found */
-			tmpArtist = BarUiSelectArtist (searchResult.artists, curFd);
+			tmpArtist = BarUiSelectArtist (searchResult.artists, &app->input);
 			if (tmpArtist != NULL) {
 				musicId = strdup (tmpArtist->musicId);
 			}
@@ -472,9 +479,9 @@ char *BarUiSelectMusicId (BarApp_t *app, FILE *curFd, char *similarToId) {
 }
 
 /*	browse genre stations and create shared station
- *	@param piano handle
+ *	@param app handle
  */
-void BarStationFromGenre (BarApp_t *app, FILE *curFd) {
+void BarStationFromGenre (BarApp_t *app) {
 	PianoReturn_t pRet;
 	WaitressReturn_t wRet;
 	PianoGenreCategory_t *curCat;
@@ -504,7 +511,7 @@ void BarStationFromGenre (BarApp_t *app, FILE *curFd) {
 	}
 	/* select category or exit */
 	BarUiMsg (MSG_QUESTION, "Select category: ");
-	if (BarReadlineInt (&i, curFd) == 0) {
+	if (BarReadlineInt (&i, &app->input) == 0) {
 		return;
 	}
 	curCat = app->ph.genreStations;
@@ -522,7 +529,7 @@ void BarStationFromGenre (BarApp_t *app, FILE *curFd) {
 		curGenre = curGenre->next;
 	}
 	BarUiMsg (MSG_QUESTION, "Select genre: ");
-	if (BarReadlineInt (&i, curFd) == 0) {
+	if (BarReadlineInt (&i, &app->input) == 0) {
 		return;
 	}
 	curGenre = curCat->genres;
diff --git a/src/ui.h b/src/ui.h
index 6664f8e..4d4b555 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2010
+Copyright (c) 2008-2011
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -30,6 +30,7 @@ THE SOFTWARE.
 #include "settings.h"
 #include "player.h"
 #include "main.h"
+#include "ui_readline.h"
 
 typedef enum {MSG_NONE, MSG_INFO, MSG_PLAYING, MSG_TIME, MSG_ERR,
 		MSG_QUESTION, MSG_LIST} uiMsg_t;
@@ -37,11 +38,12 @@ typedef enum {MSG_NONE, MSG_INFO, MSG_PLAYING, MSG_TIME, MSG_ERR,
 void BarUiMsg (uiMsg_t type, const char *format, ...);
 PianoReturn_t BarUiPrintPianoStatus (PianoReturn_t ret);
 PianoStation_t *BarUiSelectStation (PianoHandle_t *, const char *,
-		BarStationSorting_t, FILE *);
-PianoSong_t *BarUiSelectSong (const BarSettings_t *, PianoSong_t *, FILE *);
-PianoArtist_t *BarUiSelectArtist (PianoArtist_t *startArtist, FILE *curFd);
-char *BarUiSelectMusicId (BarApp_t *, FILE *, char *);
-void BarStationFromGenre (BarApp_t *, FILE *);
+		BarStationSorting_t, BarReadlineFds_t *);
+PianoSong_t *BarUiSelectSong (const BarSettings_t *, PianoSong_t *,
+		BarReadlineFds_t *);
+PianoArtist_t *BarUiSelectArtist (PianoArtist_t *, BarReadlineFds_t *);
+char *BarUiSelectMusicId (BarApp_t *, char *);
+void BarStationFromGenre (BarApp_t *);
 void BarUiPrintStation (PianoStation_t *);
 void BarUiPrintSong (const BarSettings_t *, const PianoSong_t *, 
 		const PianoStation_t *);
diff --git a/src/ui_act.c b/src/ui_act.c
index dac104a..f8c9879 100644
--- a/src/ui_act.c
+++ b/src/ui_act.c
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2010
+Copyright (c) 2008-2011
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -126,7 +126,7 @@ BarUiActCallback(BarUiActAddMusic) {
 
 	RETURN_IF_NO_STATION;
 
-	reqData.musicId = BarUiSelectMusicId (app, curFd, app->playlist->musicId);
+	reqData.musicId = BarUiSelectMusicId (app, app->playlist->musicId);
 	if (reqData.musicId != NULL) {
 		if (!BarTransformIfShared (app, app->curStation)) {
 			return;
@@ -172,7 +172,7 @@ BarUiActCallback(BarUiActCreateStation) {
 	WaitressReturn_t wRet;
 	PianoRequestDataCreateStation_t reqData;
 
-	reqData.id = BarUiSelectMusicId (app, curFd, NULL);
+	reqData.id = BarUiSelectMusicId (app, NULL);
 	if (reqData.id != NULL) {
 		reqData.type = "mi";
 		BarUiMsg (MSG_INFO, "Creating station... ");
@@ -191,8 +191,8 @@ BarUiActCallback(BarUiActAddSharedStation) {
 	char stationId[50];
 
 	BarUiMsg (MSG_QUESTION, "Station id: ");
-	if (BarReadline (stationId, sizeof (stationId), "0123456789", 0, 0,
-			curFd) > 0) {
+	if (BarReadline (stationId, sizeof (stationId), "0123456789", &app->input,
+			BAR_RL_DEFAULT, -1) > 0) {
 		reqData.id = stationId;
 		reqData.type = "sh";
 		BarUiMsg (MSG_INFO, "Adding shared station... ");
@@ -211,7 +211,7 @@ BarUiActCallback(BarUiActDeleteStation) {
 
 	BarUiMsg (MSG_QUESTION, "Really delete \"%s\"? [yN] ",
 			app->curStation->name);
-	if (BarReadlineYesNo (0, curFd)) {
+	if (BarReadlineYesNo (false, &app->input)) {
 		BarUiMsg (MSG_INFO, "Deleting station... ");
 		if (BarUiActDefaultPianoCall (PIANO_REQUEST_DELETE_STATION,
 				app->curStation)) {
@@ -247,7 +247,7 @@ BarUiActCallback(BarUiActExplain) {
  */
 BarUiActCallback(BarUiActStationFromGenre) {
 	/* use genre station */
-	BarStationFromGenre (app, curFd);
+	BarStationFromGenre (app);
 }
 
 /*	print verbose song information
@@ -333,7 +333,7 @@ BarUiActCallback(BarUiActMoveSong) {
 	RETURN_IF_NO_SONG;
 
 	reqData.to = BarUiSelectStation (&app->ph, "Move song to station: ",
-			app->settings.sortOrder, curFd);
+			app->settings.sortOrder, &app->input);
 	if (reqData.to != NULL) {
 		/* find original station (just is case we're playing a quickmix
 		 * station) */
@@ -376,7 +376,7 @@ BarUiActCallback(BarUiActRenameStation) {
 	RETURN_IF_NO_STATION;
 
 	BarUiMsg (MSG_QUESTION, "New name: ");
-	if (BarReadlineStr (lineBuf, sizeof (lineBuf), 0, curFd) > 0) {
+	if (BarReadlineStr (lineBuf, sizeof (lineBuf), &app->input, BAR_RL_DEFAULT) > 0) {
 		PianoRequestDataRenameStation_t reqData;
 		if (!BarTransformIfShared (app, app->curStation)) {
 			return;
@@ -395,7 +395,7 @@ BarUiActCallback(BarUiActRenameStation) {
  */
 BarUiActCallback(BarUiActSelectStation) {
 	PianoStation_t *newStation = BarUiSelectStation (&app->ph, "Select station: ",
-			app->settings.sortOrder, curFd);
+			app->settings.sortOrder, &app->input);
 	if (newStation != NULL) {
 		app->curStation = newStation;
 		BarUiPrintStation (app->curStation);
@@ -446,7 +446,7 @@ BarUiActCallback(BarUiActSelectQuickMix) {
 		PianoStation_t *selStation;
 		while ((selStation = BarUiSelectStation (&app->ph,
 				"Toggle quickmix for station: ", app->settings.sortOrder,
-				curFd)) != NULL) {
+				&app->input)) != NULL) {
 			selStation->useQuickMix = !selStation->useQuickMix;
 		}
 		BarUiMsg (MSG_INFO, "Setting quickmix stations... ");
@@ -474,7 +474,7 @@ BarUiActCallback(BarUiActHistory) {
 
 	if (app->songHistory != NULL) {
 		selectedSong = BarUiSelectSong (&app->settings, app->songHistory,
-				curFd);
+				&app->input);
 		if (selectedSong != NULL) {
 			/* use user-defined keybindings */
 			allowedBuf[0] = app->settings.keys[BAR_KS_LOVE];
@@ -487,7 +487,8 @@ BarUiActCallback(BarUiActHistory) {
 					app->settings.keys[BAR_KS_LOVE],
 					app->settings.keys[BAR_KS_BAN],
 					app->settings.keys[BAR_KS_TIRED]);
-			BarReadline (selectBuf, sizeof (selectBuf), allowedBuf, 1, 0, curFd);
+			BarReadline (selectBuf, sizeof (selectBuf), allowedBuf,
+					&app->input, BAR_RL_FULLRETURN, -1);
 
 			if (selectBuf[0] == app->settings.keys[BAR_KS_LOVE] ||
 					selectBuf[0] == app->settings.keys[BAR_KS_BAN] ||
@@ -557,7 +558,8 @@ BarUiActCallback(BarUiActBookmark) {
 	RETURN_IF_NO_SONG;
 
 	BarUiMsg (MSG_QUESTION, "Bookmark [s]ong or [a]rtist? ");
-	BarReadline (selectBuf, sizeof (selectBuf), "sa", 1, 0, curFd);
+	BarReadline (selectBuf, sizeof (selectBuf), "sa", &app->input,
+			BAR_RL_FULLRETURN, -1);
 	if (selectBuf[0] == 's') {
 		BarUiMsg (MSG_INFO, "Bookmarking song... ");
 		BarUiActDefaultPianoCall (PIANO_REQUEST_BOOKMARK_SONG, app->playlist);
diff --git a/src/ui_act.h b/src/ui_act.h
index d4e6948..b1dce62 100644
--- a/src/ui_act.h
+++ b/src/ui_act.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2010
+Copyright (c) 2008-2011
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -26,7 +26,7 @@ THE SOFTWARE.
 
 #include "main.h"
 
-#define BarUiActCallback(name) void name (BarApp_t *app, FILE *curFd)
+#define BarUiActCallback(name) void name (BarApp_t *app)
 
 BarUiActCallback(BarUiActHelp);
 BarUiActCallback(BarUiActAddMusic);
diff --git a/src/ui_readline.c b/src/ui_readline.c
index 98af5ea..8e6ecb4 100644
--- a/src/ui_readline.c
+++ b/src/ui_readline.c
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2010
+Copyright (c) 2008-2011
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -24,6 +24,10 @@ THE SOFTWARE.
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "ui_readline.h"
 
 static inline void BarReadlineMoveLeft (char *buf, size_t *bufPos,
 		size_t *bufLen) {
@@ -52,33 +56,73 @@ static inline char BarReadlineIsUtf8Content (char b) {
  *	@param buffer
  *	@param buffer size
  *	@param accept these characters
- *	@param return if buffer full (otherwise more characters are not accepted)
- *	@param don't echo anything (for passwords)
- *	@param read from this fd
+ *	@param input fds
+ *	@param flags
+ *	@param timeout (seconds) or -1 (no timeout)
  *	@return number of bytes read from stdin
  */
-size_t BarReadline (char *buf, size_t bufSize, const char *mask,
-		char fullReturn, char noEcho, FILE *fd) {
-	int chr = 0;
+size_t BarReadline (char *buf, const size_t bufSize, const char *mask,
+		BarReadlineFds_t *input, const BarReadlineFlags_t flags, int timeout) {
 	size_t bufPos = 0;
 	size_t bufLen = 0;
 	unsigned char escapeState = 0;
+	fd_set set;
+	const bool echo = !(flags & BAR_RL_NOECHO);
+
+	assert (buf != NULL);
+	assert (bufSize > 0);
+	assert (input != NULL);
 
 	memset (buf, 0, bufSize);
 
 	/* if fd is a fifo fgetc will always return EOF if nobody writes to
 	 * it, stdin will block */
-	while ((chr = fgetc (fd)) != EOF) {
+	while (1) {
+		int curFd = -1;
+		char chr;
+		struct timeval timeoutstruct;
+
+		/* select modifies set and timeout */
+		memcpy (&set, &input->set, sizeof (set));
+		timeoutstruct.tv_sec = timeout;
+		timeoutstruct.tv_usec = 0;
+
+		if (select (input->maxfd, &set, NULL, NULL,
+				(timeout == -1) ? NULL : &timeoutstruct) <= 0) {
+			/* fail or timeout */
+			break;
+		}
+
+		assert (sizeof (input->fds) / sizeof (*input->fds) == 2);
+		if (FD_ISSET(input->fds[0], &set)) {
+			curFd = input->fds[0];
+		} else if (input->fds[1] != -1 && FD_ISSET(input->fds[1], &set)) {
+			curFd = input->fds[1];
+		}
+		if (read (curFd, &chr, sizeof (chr)) <= 0) {
+			/* select() is going wild if fdset contains EOFed stdin, only check
+			 * for stdin, fifo is "reopened" as soon as another writer is
+			 * available
+			 * FIXME: ugly */
+			if (curFd == STDIN_FILENO) {
+				FD_CLR (curFd, &input->set);
+			}
+			continue;
+		}
 		switch (chr) {
 			/* EOT */
 			case 4:
-				printf ("\n");
+				if (echo) {
+					fputs ("\n", stdout);
+				}
 				return bufLen;
 				break;
 
 			/* return */
 			case 10:
-				printf ("\n");
+				if (echo) {
+					fputs ("\n", stdout);
+				}
 				return bufLen;
 				break;
 
@@ -109,15 +153,15 @@ size_t BarReadline (char *buf, size_t bufSize, const char *mask,
 						}
 					}
 					/* move caret back and delete last character */
-					if (!noEcho) {
-						printf ("\033[D\033[K");
+					if (echo) {
+						fputs ("\033[D\033[K", stdout);
 						fflush (stdout);
 					}
 				} else if (bufPos == 0 && buf[bufPos] != '\0') {
 					/* delete char at position 0 but don't move cursor any further */
 					buf[bufPos] = '\0';
-					if (!noEcho) {
-						printf ("\033[K");
+					if (echo) {
+						fputs ("\033[K", stdout);
 						fflush (stdout);
 					}
 				}
@@ -145,18 +189,21 @@ size_t BarReadline (char *buf, size_t bufSize, const char *mask,
 					buf[bufPos] = chr;
 					++bufPos;
 					++bufLen;
-					if (!noEcho) {
-						fputc (chr, stdout);
+					if (echo) {
+						putchar (chr);
+						fflush (stdout);
 					}
 					/* buffer full => return if requested */
-					if (fullReturn && bufPos >= bufSize-1) {
-						printf ("\n");
+					if (bufPos >= bufSize-1 && (flags & BAR_RL_FULLRETURN)) {
+						if (echo) {
+							fputs ("\n", stdout);
+						}
 						return bufLen;
 					}
 				}
 				break;
-		}
-	}
+		} /* end switch */
+	} /* end while */
 	return 0;
 }
 
@@ -165,35 +212,36 @@ size_t BarReadline (char *buf, size_t bufSize, const char *mask,
  *	@param buffer size
  *	@return number of bytes read from stdin
  */
-size_t BarReadlineStr (char *buf, size_t bufSize, char noEcho,
-		FILE *fd) {
-	return BarReadline (buf, bufSize, NULL, 0, noEcho, fd);
+size_t BarReadlineStr (char *buf, const size_t bufSize,
+		BarReadlineFds_t *input, const BarReadlineFlags_t flags) {
+	return BarReadline (buf, bufSize, NULL, input, flags, -1);
 }
 
 /*	Read int from stdin
  *	@param write result into this variable
  *	@return number of bytes read from stdin
  */
-size_t BarReadlineInt (int *ret, FILE *fd) {
+size_t BarReadlineInt (int *ret, BarReadlineFds_t *input) {
 	int rlRet = 0;
 	char buf[16];
 
-	rlRet = BarReadline (buf, sizeof (buf), "0123456789", 0, 0, fd);
+	rlRet = BarReadline (buf, sizeof (buf), "0123456789", input,
+			BAR_RL_DEFAULT, -1);
 	*ret = atoi ((char *) buf);
 
 	return rlRet;
 }
 
 /*	Yes/No?
- *	@param defaul (user presses enter)
+ *	@param default (user presses enter)
  */
-int BarReadlineYesNo (char def, FILE *fd) {
+bool BarReadlineYesNo (bool def, BarReadlineFds_t *input) {
 	char buf[2];
-	BarReadline (buf, sizeof (buf), "yYnN", 1, 0, fd);
-	if (*buf == 'y' || *buf == 'Y' || (def == 1 && *buf == '\0')) {
-		return 1;
+	BarReadline (buf, sizeof (buf), "yYnN", input, BAR_RL_FULLRETURN, -1);
+	if (*buf == 'y' || *buf == 'Y' || (def == true && *buf == '\0')) {
+		return true;
 	} else {
-		return 0;
+		return false;
 	}
 }
 
diff --git a/src/ui_readline.h b/src/ui_readline.h
index 96356b7..55411fe 100644
--- a/src/ui_readline.h
+++ b/src/ui_readline.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2010
+Copyright (c) 2008-2011
 	Lars-Dominik Braun <lars@6xq.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -21,8 +21,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */
 
-size_t BarReadline (char *, size_t, const char *, char, char, FILE *);
-size_t BarReadlineStr (char *, size_t, char, FILE *);
-size_t BarReadlineInt (int *, FILE *);
-int BarReadlineYesNo (char def, FILE *);
+#ifndef _UI_READLINE_H
+#define _UI_READLINE_H
+
+#include <stdbool.h>
+#include <sys/select.h>
+
+typedef enum {
+	BAR_RL_DEFAULT = 0,
+	BAR_RL_FULLRETURN = 1, /* return if buffer is full */
+	BAR_RL_NOECHO = 2, /* don't echo to stdout */
+} BarReadlineFlags_t;
+
+typedef struct {
+	fd_set set;
+	int maxfd;
+	int fds[2];
+} BarReadlineFds_t;
+
+size_t BarReadline (char *, const size_t, const char *,
+		BarReadlineFds_t *, const BarReadlineFlags_t, int);
+size_t BarReadlineStr (char *, const size_t,
+		BarReadlineFds_t *, const BarReadlineFlags_t);
+size_t BarReadlineInt (int *, BarReadlineFds_t *);
+bool BarReadlineYesNo (bool, BarReadlineFds_t *);
+
+#endif /* _UI_READLINE_H */
 
-- 
cgit v1.2.3