From ce8503f859345990a14be90bf89dadf7f73d7613 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun 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. --- COPYING | 2 +- 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 +++++++++++++--- 9 files changed, 197 insertions(+), 143 deletions(-) diff --git a/COPYING b/COPYING index af7b233..1ca7f75 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2008-2010 +Copyright (c) 2008-2011 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy 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 Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,10 +27,9 @@ THE SOFTWARE. #include #include -#include - #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 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 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 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 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 Permission is hereby granted, free of charge, to any person obtaining a copy @@ -24,6 +24,10 @@ THE SOFTWARE. #include #include #include +#include +#include + +#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 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 +#include + +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