From 71d9447316133c8e447ca7538d9533e1ef61e0eb Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Fri, 10 Apr 2009 14:32:16 +0200 Subject: Goodbye readline "It's too big and too slow." -- man readline --- INSTALL | 1 - src/CMakeLists.txt | 10 +-- src/FindReadline.cmake | 37 ---------- src/main.c | 14 ++-- src/ui.c | 69 +++++------------ src/ui_act.c | 30 +++----- src/ui_readline.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ui_readline.h | 28 +++++++ 8 files changed, 263 insertions(+), 121 deletions(-) delete mode 100644 src/FindReadline.cmake create mode 100644 src/ui_readline.c create mode 100644 src/ui_readline.h diff --git a/INSTALL b/INSTALL index 151ff6e..e458e15 100644 --- a/INSTALL +++ b/INSTALL @@ -13,7 +13,6 @@ libfaad2 http://www.audiocoding.com/downloads.html AND/OR libmad http://www.underbit.com/products/mad/ libxml2 http://xmlsoft.org/ pthreads -readline UTF-8 console/locale! Building diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f871b7b..5fb020c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,6 @@ option (USE_MAD "Use libmad for mp3 decoding (if available)" on) find_package (LibXml2 REQUIRED) find_package (CURL REQUIRED) -find_package (Readline REQUIRED) find_package (LibAo REQUIRED) # find threading implementation @@ -57,12 +56,13 @@ include_directories ( ${pianobar_SOURCE_DIR}/libpiano/src ${pianobar_SOURCE_DIR}/libwardrobe/src ${LIBXML2_INCLUDE_DIR} - ${CURL_INCLUDE_DIRS} ${READLINE_INCLUDE_DIR} ${FAAD_INCLUDE_DIRS} - ${LIBAO_INCLUDE_DIRS} ${MAD_INCLUDE_DIRS}) + ${CURL_INCLUDE_DIRS} ${FAAD_INCLUDE_DIRS} ${LIBAO_INCLUDE_DIRS} + ${MAD_INCLUDE_DIRS}) -add_executable (pianobar main.c terminal.c settings.c player.c ui.c ui_act.c) +add_executable (pianobar main.c terminal.c settings.c player.c ui.c ui_act.c + ui_readline.c) target_link_libraries (pianobar piano wardrobe ${CURL_LIBRARIES} - ${LIBXML2_LIBRARIES} ${READLINE_LIBRARY} ${FAAD_LIBRARY} ${LIBAO_LIBRARY} + ${LIBXML2_LIBRARIES} ${FAAD_LIBRARY} ${LIBAO_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MAD_LIBRARIES} ${LIBM}) install (TARGETS pianobar RUNTIME DESTINATION bin) diff --git a/src/FindReadline.cmake b/src/FindReadline.cmake deleted file mode 100644 index 67ea232..0000000 --- a/src/FindReadline.cmake +++ /dev/null @@ -1,37 +0,0 @@ -# - Find readline -# -# READLINE_INCLUDE_DIRS - where to find curl/curl.h, etc. -# READLINE_LIBRARIES - List of libraries when using curl. -# READLINE_FOUND - True if curl found. - -# Look for the header file. -FIND_PATH(READLINE_INCLUDE_DIR NAMES readline/readline.h) -MARK_AS_ADVANCED(READLINE_INCLUDE_DIR) - -# Look for the library. -FIND_LIBRARY(READLINE_LIBRARY NAMES readline) -MARK_AS_ADVANCED(READLINE_LIBRARY) - -# Copy the results to the output variables. -IF(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) - SET(READLINE_FOUND 1) - SET(READLINE_LIBRARIES ${READLINE_LIBRARY}) - SET(READLINE_INCLUDE_DIRS ${READLINE_INCLUDE_DIR}) -ELSE(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) - SET(READLINE_FOUND 0) - SET(READLINE_LIBRARIES) - SET(READLINE_INCLUDE_DIRS) -ENDIF(READLINE_INCLUDE_DIR AND READLINE_LIBRARY) - -# Report the results. -IF(NOT READLINE_FOUND) - SET(READLINE_DIR_MESSAGE - "READLINE was not found. Make sure READLINE_LIBRARY and READLINE_INCLUDE_DIR are set.") - IF(NOT READLINE_FIND_QUIETLY) - MESSAGE(STATUS "${READLINE_DIR_MESSAGE}") - ELSE(NOT READLINE_FIND_QUIETLY) - IF(READLINE_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "${READLINE_DIR_MESSAGE}") - ENDIF(READLINE_FIND_REQUIRED) - ENDIF(NOT READLINE_FIND_QUIETLY) -ENDIF(NOT READLINE_FOUND) diff --git a/src/main.c b/src/main.c index 06f5975..8de5d6f 100644 --- a/src/main.c +++ b/src/main.c @@ -44,8 +44,6 @@ THE SOFTWARE. #include -#include - /* pandora.com library */ #include @@ -55,6 +53,7 @@ THE SOFTWARE. #include "terminal.h" #include "config.h" #include "ui.h" +#include "ui_readline.h" int main (int argc, char **argv) { /* handles */ @@ -79,6 +78,7 @@ int main (int argc, char **argv) { BarUiMsg (MSG_NONE, "Welcome to " PACKAGE "!\n"); /* init some things */ + BarTermSetEcho (0); curl_global_init (CURL_GLOBAL_SSL); xmlInitParser (); ao_initialize (); @@ -101,14 +101,16 @@ int main (int argc, char **argv) { } if (settings.username == NULL) { + char nameBuf[100]; BarUiMsg (MSG_QUESTION, "Username: "); - settings.username = readline (NULL); + BarReadlineStr (nameBuf, sizeof (nameBuf), 0); + settings.username = strdup (nameBuf); } if (settings.password == NULL) { - BarTermSetEcho (0); + char passBuf[100]; BarUiMsg (MSG_QUESTION, "Password: "); - settings.password = readline (NULL); - BarTermSetEcho (1); + BarReadlineStr (passBuf, sizeof (passBuf), 1); + settings.password = strdup (passBuf); BarUiMsg (MSG_NONE, "\n"); } diff --git a/src/ui.c b/src/ui.c index 6154371..91860cf 100644 --- a/src/ui.c +++ b/src/ui.c @@ -36,6 +36,7 @@ THE SOFTWARE. #include #include "ui.h" +#include "ui_readline.h" /* output message and flush stdout * @param message @@ -90,41 +91,6 @@ inline PianoReturn_t BarUiPrintPianoStatus (PianoReturn_t ret) { return ret; } -/* check if all characters of string are numeric - * @param the string - * @return 1 = yes, 0 = not numeric - */ -char BarIsNumericStr (const char *str) { - while (*str != '\0') { - if (isdigit (*str) == 0) { - return 0; - } - str++; - } - return 1; -} - -/* use readline to get integer value - * @param prompt or NULL - * @param returns integer - * @return 1 = success, 0 = failure (not an integer, ...) - */ -char BarReadlineInt (const char *prompt, int *retVal) { - char *buf; - char ret = 0; - - BarUiMsg (MSG_QUESTION, prompt); - if ((buf = readline (NULL)) != NULL && strlen (buf) > 0 && - BarIsNumericStr (buf)) { - *retVal = atoi (buf); - ret = 1; - } - if (buf != NULL) { - free (buf); - } - return ret; -} - /* sort linked list (station) * @param stations * @return NULL-terminated array with sorted stations @@ -200,7 +166,8 @@ PianoStation_t *BarUiSelectStation (PianoHandle_t *ph, const char *prompt) { i++; } - if (!BarReadlineInt (prompt, &i)) { + BarUiMsg (MSG_QUESTION, prompt); + if (BarReadlineInt (&i) == 0) { free (ss); return NULL; } @@ -230,7 +197,8 @@ PianoSong_t *BarUiSelectSong (PianoSong_t *startSong) { i++; tmpSong = tmpSong->next; } - if (!BarReadlineInt ("Select song: ", &i)) { + BarUiMsg (MSG_QUESTION, "Select song: "); + if (BarReadlineInt (&i) == 0) { return NULL; } tmpSong = startSong; @@ -256,7 +224,8 @@ PianoArtist_t *BarUiSelectArtist (PianoArtist_t *startArtist) { i++; tmpArtist = tmpArtist->next; } - if (!BarReadlineInt ("Select artist: ", &i)) { + BarUiMsg (MSG_QUESTION, "Select artist: "); + if (BarReadlineInt (&i) == 0) { return NULL; } tmpArtist = startArtist; @@ -272,19 +241,17 @@ PianoArtist_t *BarUiSelectArtist (PianoArtist_t *startArtist) { * @return musicId or NULL on abort/error */ char *BarUiSelectMusicId (const PianoHandle_t *ph) { - char *musicId = NULL, *lineBuf; - char yesnoBuf; + char *musicId = NULL; + char lineBuf[100], selectBuf[2]; PianoSearchResult_t searchResult; PianoArtist_t *tmpArtist; PianoSong_t *tmpSong; BarUiMsg (MSG_QUESTION, "Search for artist/title: "); - lineBuf = readline (NULL); - if (lineBuf != NULL && strlen (lineBuf) > 0) { + if (BarReadlineStr (lineBuf, sizeof (lineBuf), 0) > 0) { BarUiMsg (MSG_INFO, "Searching... "); if (BarUiPrintPianoStatus (PianoSearchMusic (ph, lineBuf, &searchResult)) != PIANO_RET_OK) { - free (lineBuf); return NULL; } BarUiMsg (MSG_NONE, "\r"); @@ -292,14 +259,13 @@ char *BarUiSelectMusicId (const PianoHandle_t *ph) { /* songs and artists found */ BarUiMsg (MSG_QUESTION, "Is this an [a]rtist or [t]rack name? " "Press c to abort. "); - read (fileno (stdin), &yesnoBuf, sizeof (yesnoBuf)); - BarUiMsg (MSG_NONE, "\n"); - if (yesnoBuf == 'a') { + BarReadline (selectBuf, sizeof (selectBuf), "atc", 1, 0); + if (*selectBuf == 'a') { tmpArtist = BarUiSelectArtist (searchResult.artists); if (tmpArtist != NULL) { musicId = strdup (tmpArtist->musicId); } - } else if (yesnoBuf == 't') { + } else if (*selectBuf == 't') { tmpSong = BarUiSelectSong (searchResult.songs); if (tmpSong != NULL) { musicId = strdup (tmpSong->musicId); @@ -322,9 +288,6 @@ char *BarUiSelectMusicId (const PianoHandle_t *ph) { } PianoDestroySearchResult (&searchResult); } - if (lineBuf != NULL) { - free (lineBuf); - } return musicId; } @@ -355,7 +318,8 @@ void BarStationFromGenre (PianoHandle_t *ph) { curCat = curCat->next; } /* select category or exit */ - if (!BarReadlineInt ("Select category: ", &i)) { + BarUiMsg (MSG_QUESTION, "Select category: "); + if (BarReadlineInt (&i) == 0) { return; } curCat = ph->genreStations; @@ -372,7 +336,8 @@ void BarStationFromGenre (PianoHandle_t *ph) { i++; curStation = curStation->next; } - if (!BarReadlineInt ("Select genre: ", &i)) { + BarUiMsg (MSG_QUESTION, "Select genre: "); + if (BarReadlineInt (&i) == 0) { return; } curStation = curCat->stations; diff --git a/src/ui_act.c b/src/ui_act.c index 2a2e041..951116e 100644 --- a/src/ui_act.c +++ b/src/ui_act.c @@ -26,12 +26,10 @@ THE SOFTWARE. #include #include #include -/* needed by readline */ -#include -#include #include "ui.h" #include "ui_act.h" +#include "ui_readline.h" #define RETURN_IF_NO_STATION if (*curStation == NULL) { \ BarUiMsg (MSG_ERR, "No station selected.\n"); \ @@ -130,29 +128,24 @@ void BarUiActCreateStation (BAR_KS_ARGS) { /* add shared station by id */ void BarUiActAddSharedStation (BAR_KS_ARGS) { - char *stationId = NULL; + char stationId[50]; BarUiMsg (MSG_QUESTION, "Station id: "); - if ((stationId = readline (NULL)) != NULL && - strlen (stationId) > 0) { + if (BarReadline (stationId, sizeof (stationId), "0123456789", 0, 0) > 0) { BarUiMsg (MSG_INFO, "Adding shared station... "); - BarUiPrintPianoStatus (PianoCreateStation (ph, "sh", stationId)); - free (stationId); + BarUiPrintPianoStatus (PianoCreateStation (ph, "sh", + (char *) stationId)); } } /* delete current station */ void BarUiActDeleteStation (BAR_KS_ARGS) { - char yesNoBuf; - RETURN_IF_NO_STATION; BarUiMsg (MSG_QUESTION, "Really delete \"%s\"? [yN] ", (*curStation)->name); - read (fileno (stdin), &yesNoBuf, sizeof (yesNoBuf)); - BarUiMsg (MSG_NONE, "\n"); - if (yesNoBuf == 'y') { + if (BarReadlineYesNo (0)) { BarUiMsg (MSG_INFO, "Deleting station... "); if (BarUiPrintPianoStatus (PianoDeleteStation (ph, *curStation)) == PIANO_RET_OK) { @@ -280,21 +273,18 @@ void BarUiActPause (BAR_KS_ARGS) { /* rename current station */ void BarUiActRenameStation (BAR_KS_ARGS) { - char *lineBuf; + char lineBuf[100]; RETURN_IF_NO_STATION; BarUiMsg (MSG_QUESTION, "New name: "); - lineBuf = readline (NULL); - if (lineBuf != NULL && strlen (lineBuf) > 0) { + if (BarReadlineStr (lineBuf, sizeof (lineBuf), 0) > 0) { if (!BarTransformIfShared (ph, *curStation)) { return; } BarUiMsg (MSG_INFO, "Renaming station... "); - BarUiPrintPianoStatus (PianoRenameStation (ph, *curStation, lineBuf)); - } - if (lineBuf != NULL) { - free (lineBuf); + BarUiPrintPianoStatus (PianoRenameStation (ph, *curStation, + (char *) lineBuf)); } } diff --git a/src/ui_readline.c b/src/ui_readline.c new file mode 100644 index 0000000..fd4eb29 --- /dev/null +++ b/src/ui_readline.c @@ -0,0 +1,195 @@ +/* +Copyright (c) 2008-2009 + Lars-Dominik Braun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include + +inline void BarReadlineMoveLeft (char *buf, size_t *bufPos, + size_t *bufLen) { + char *tmpBuf = &buf[*bufPos-1]; + while (tmpBuf < &buf[*bufLen]) { + *tmpBuf = *(tmpBuf+1); + ++tmpBuf; + } + --(*bufPos); + --(*bufLen); +} + +inline char BarReadlineIsAscii (char b) { + return !(b & (1 << 7)); +} + +inline char BarReadlineIsUtf8Start (char b) { + return (b & (1 << 7)) && (b & (1 << 6)); +} + +inline char BarReadlineIsUtf8Content (char b) { + return (b & (1 << 7)) && !(b & (1 << 6)); +} + +/* readline replacement + * @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) + * @return number of bytes read from stdin + */ +size_t BarReadline (char *buf, size_t bufSize, const char *mask, + char fullReturn, char noEcho) { + int chr = 0; + size_t bufPos = 0; + size_t bufLen = 0; + unsigned char escapeState = 0; + + memset (buf, 0, bufSize); + + while ((chr = fgetc (stdin)) != EOF) { + switch (chr) { + /* EOT */ + case 4: + printf ("\n"); + return bufLen; + break; + + /* return */ + case 10: + printf ("\n"); + return bufLen; + break; + + /* escape */ + case 27: + escapeState = 1; + break; + + /* del */ + case 126: + break; + + /* backspace */ + case 127: + if (bufPos > 0) { + if (BarReadlineIsAscii (buf[bufPos-1])) { + BarReadlineMoveLeft (buf, &bufPos, &bufLen); + } else { + /* delete utf-8 multibyte chars */ + /* char content */ + while (bufPos >= 0 && + BarReadlineIsUtf8Content (buf[bufPos-1])) { + BarReadlineMoveLeft (buf, &bufPos, &bufLen); + } + /* char length */ + if (bufPos >= 0 && + BarReadlineIsUtf8Start (buf[bufPos-1])) { + BarReadlineMoveLeft (buf, &bufPos, &bufLen); + } + } + /* move caret back and delete last character */ + if (!noEcho) { + printf ("\033[D\033[K"); + fflush (stdout); + } + } else if (bufPos == 0 && buf[bufPos] != '\0') { + buf[bufPos] = '\0'; + if (!noEcho) { + printf ("\033[K"); + fflush (stdout); + } + } + break; + + default: + /* ignore control/escape characters */ + if (chr <= 0x1F) { + break; + } + if (escapeState == 2) { + escapeState = 0; + break; + } + if (escapeState == 1 && chr == '[') { + escapeState = 2; + break; + } + /* don't accept chars not in mask */ + if (mask != NULL && !strchr (mask, chr)) { + break; + } + /* don't write beyond buffer's limits */ + if (bufPos < bufSize-1) { + buf[bufPos] = chr; + ++bufPos; + ++bufLen; + if (!noEcho) { + fputc (chr, stdout); + } + /* buffer full => return if requested */ + if (fullReturn && bufPos >= bufSize-1) { + printf ("\n"); + return bufLen; + } + } + break; + } + } + return 0; +} + +/* Read string from stdin + * @param buffer + * @param buffer size + * @return number of bytes read from stdin + */ +inline size_t BarReadlineStr (char *buf, size_t bufSize, char noEcho) { + return BarReadline (buf, bufSize, NULL, 0, noEcho); +} + +/* Read int from stdin + * @param write result into this variable + * @return number of bytes read from stdin + */ +size_t BarReadlineInt (int *ret) { + int rlRet = 0; + char buf[16]; + + rlRet = BarReadline (buf, sizeof (buf), "0123456789", 0, 0); + *ret = atoi ((char *) buf); + + return rlRet; +} + +/* Yes/No? + * @param defaul (user presses enter) + */ +int BarReadlineYesNo (char def) { + char buf[2]; + BarReadline (buf, sizeof (buf), "yYnN", 1, 0); + if (*buf == 'y' || *buf == 'Y' || (def == 1 && *buf == '\0')) { + return 1; + } else { + return 0; + } +} + diff --git a/src/ui_readline.h b/src/ui_readline.h new file mode 100644 index 0000000..3f2e886 --- /dev/null +++ b/src/ui_readline.h @@ -0,0 +1,28 @@ +/* +Copyright (c) 2008-2009 + Lars-Dominik Braun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +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); +inline size_t BarReadlineStr (char *, size_t, char); +size_t BarReadlineInt (int *); +int BarReadlineYesNo (char def); + -- cgit v1.2.3