From: Andre Noll Date: Sun, 4 Dec 2011 23:13:32 +0000 (+0100) Subject: Merge branch 't/interactive' X-Git-Tag: v0.4.9~1 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=ef1f130a1f9ac61a51da10ee56c5bd9ef1b52366;hp=-c Merge branch 't/interactive' Conflicts: string.h --- ef1f130a1f9ac61a51da10ee56c5bd9ef1b52366 diff --combined FEATURES index 613acc19,d723541c..6ad420df --- a/FEATURES +++ b/FEATURES @@@ -5,7 -5,7 +5,7 @@@ Feature * Runs on Linux, Mac OS, FreeBSD, NetBSD, Solaris and probably other Unixes - * Mp3, ogg/vorbis, ogg/speex, aac (m4a) and wma support + * Mp3, ogg/vorbis, ogg/speex, aac (m4a), wma and flac support * Native Alsa, OSS, CoreAudio output support * Support for ESD, Pulseaudio, AIX, Solaris, IRIX through libao * Local or remote http, dccp and udp network audio streaming @@@ -18,6 -18,7 +18,7 @@@ * Sophisticated audio file selector * Small memory footprint * Command line interface for easy scripting in high-level languages + * Interactive sessions offer command completion and command line history * RSA user authentication * Encrypted communications * GPL licensed diff --combined NEWS index 8bb2862e,d4a58fd1..1c6d821d --- a/NEWS +++ b/NEWS @@@ -2,12 -2,8 +2,16 @@@ 0.4.9 (to be announced) "hybrid causality" ------------------------------------------ - Support for another audio format and many small improvements/fixes - all over the place. ++Support for another audio format, interactive mode for para_client ++and para_audiod and many small improvements/fixes all over the place. + + - Support for flac, the free lossless audio codec. - Fix for an endless loop in the mp3 decoder for certain (corrupt) mp3 files. ++ - When executed without specifying a command, para_client ++ and para_audioc start an interactive shell (requires ++ libreadline being installed). The interactive mode offers ++ full tab completion and command line history. - autogen.sh now detects a distcc setup and adjusts the parameter for the -j option of make accordingly. - Shared memory areas are no longer restricted to 64K. We now @@@ -21,9 -17,6 +25,9 @@@ use the DMIX plugin. - Simplified and unified receiver code. - Makefile cleanups. + - Commands which print a list of matching audio files now + emit a meaningful error message if no audio file matched the + given pattern(s). -------------------------------------- 0.4.8 (2011-08-19) "nested assignment" diff --combined afs.c index 78d8571f,b4c85dcb..ff3c95fe --- a/afs.c +++ b/afs.c @@@ -363,10 -363,7 +363,10 @@@ static int action_if_pattern_matches(st continue; if (ret) return -E_FNMATCH; - return pmd->action(pmd->table, row, name, pmd->data); + ret = pmd->action(pmd->table, row, name, pmd->data); + if (ret >= 0) + pmd->num_matches++; + return ret; } return 1; } @@@ -730,11 -727,11 +730,11 @@@ static void afs_signal_post_select(stru } PARA_EMERG_LOG("terminating on signal %d\n", signum); shutdown: - sched_shutdown(); + sched_shutdown(s); t->error = -E_AFS_SIGNAL; } - static void register_signal_task(void) + static void register_signal_task(struct sched *s) { struct signal_task *st = &signal_task_struct; @@@ -748,7 -745,7 +748,7 @@@ st->task.pre_select = signal_pre_select; st->task.post_select = afs_signal_post_select; sprintf(st->task.status, "signal task"); - register_task(&st->task); + register_task(s, &st->task); } static struct list_head afs_client_list; @@@ -909,7 -906,7 +909,7 @@@ static void command_post_select(struct ret = execute_server_command(&s->rfds); if (ret < 0) { PARA_EMERG_LOG("%s\n", para_strerror(-ret)); - sched_shutdown(); + sched_shutdown(s); return; } /* Check the list of connected clients. */ @@@ -944,7 -941,7 +944,7 @@@ para_list_add(&client->node, &afs_client_list); } - static void register_command_task(uint32_t cookie) + static void register_command_task(uint32_t cookie, struct sched *s) { struct command_task *ct = &command_task_struct; ct->fd = setup_command_socket_or_die(); @@@ -953,7 -950,7 +953,7 @@@ ct->task.pre_select = command_pre_select; ct->task.post_select = command_post_select; sprintf(ct->task.status, "afs command task"); - register_task(&ct->task); + register_task(s, &ct->task); } /** @@@ -967,7 -964,7 +967,7 @@@ __noreturn void afs_init(uint32_t cooki static struct sched s; int i, ret; - register_signal_task(); + register_signal_task(&s); INIT_LIST_HEAD(&afs_client_list); for (i = 0; i < NUM_AFS_TABLES; i++) afs_tables[i].init(&afs_tables[i]); @@@ -981,7 -978,7 +981,7 @@@ PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n", server_socket, (unsigned) cookie); init_admissible_files(conf.afs_initial_mode_arg); - register_command_task(cookie); + register_command_task(cookie, &s); s.default_timeout.tv_sec = 0; s.default_timeout.tv_usec = 999 * 1000; ret = schedule(&s); diff --combined configure.ac index a2e5fed1,66541fe4..89242d96 --- a/configure.ac +++ b/configure.ac @@@ -729,47 -729,6 +729,47 @@@ if test ${have_libid3tag} = yes; the else AC_MSG_WARN([no support for id3v2 tags]) fi +########################################################################### flac +OLD_CPPFLAGS="$CPPFLAGS" +OLD_LD_FLAGS="$LDFLAGS" +OLD_LIBS="$LIBS" + +have_flac="yes" +AC_ARG_WITH(flac_headers, [AC_HELP_STRING(--with-flac-headers=dir, + [look for flac headers also in dir])]) +if test -n "$with_flac_headers"; then + flac_cppflags="-I$with_flac_headers" + CPPFLAGS="$CPPFLAGS $flac_cppflags" +fi +AC_ARG_WITH(flac_libs, [AC_HELP_STRING(--with-flac-libs=dir, + [look for flac libs also in dir])]) +if test -n "$with_flac_libs"; then + flac_libs="-L$with_flac_libs" + LDFLAGS="$LDFLAGS $flac_libs" +fi +AC_CHECK_HEADER(FLAC/stream_decoder.h, [], have_flac=no) +AC_CHECK_LIB([FLAC], [FLAC__stream_decoder_init_file], [], have_flac=no) +if test "$have_flac" = "yes"; then + AC_DEFINE(HAVE_FLAC, 1, define to 1 if you want to build the flacdec filter) + all_errlist_objs="$all_errlist_objs flacdec_filter flac_afh" + filter_errlist_objs="$filter_errlist_objs flacdec_filter" + audiod_errlist_objs="$audiod_errlist_objs flacdec_filter" + afh_errlist_objs="$afh_errlist_objs flac_afh" + server_errlist_objs="$server_errlist_objs flac_afh" + filter_ldflags="$filter_ldflags $flac_libs -lFLAC" + audiod_ldflags="$audiod_ldflags $flac_libs -lFLAC" + server_ldflags="$server_ldflags $flac_libs -lFLAC" + afh_ldflags="$afh_ldflags $flac_libs -lFLAC" + filters="$filters flacdec" + server_audio_formats="$server_audio_formats flac" + audiod_audio_formats="$audiod_audio_formats flac" + AC_SUBST(flac_cppflags) +else + AC_MSG_WARN([no flac support in para_audiod/para_filter]) +fi +CPPFLAGS="$OLD_CPPFLAGS" +LDFLAGS="$OLD_LDFLAGS" +LIBS="$OLD_LIBS" ########################################################################### oss OLD_CPPFLAGS="$CPPFLAGS" OLD_LD_FLAGS="$LDFLAGS" @@@ -905,7 -864,69 +905,69 @@@ f CPPFLAGS="$OLD_CPPFLAGS" LDFLAGS="$OLD_LDFLAGS" LIBS="$OLD_LIBS" + ############################################################# readline + OLD_CPPFLAGS="$CPPFLAGS" + OLD_LD_FLAGS="$LDFLAGS" + OLD_LIBS="$LIBS" + + have_readline="yes" + AC_ARG_WITH(readline_headers, [AC_HELP_STRING(--with-readline-headers=dir, + [look for libreadline header files also in dir])]) + if test -n "$with_readline_headers"; then + readline_cppflags="-I$with_readline_headers" + CPPFLAGS="$CPPFLAGS $readline_cppflags" + fi + AC_ARG_WITH(readline_libs, [AC_HELP_STRING(--with-readline-libs=dir, + [look for readline library also in dir])]) + if test -n "$with_readline_libs"; then + readline_libs="-L$with_readline_libs" + LDFLAGS="$LDFLAGS $readline_libs" + fi + msg="no interactive cli support" + AC_CHECK_HEADERS([readline/readline.h], [ + ], [ + have_readline="no" + AC_MSG_WARN([readline/readline.h not found, $msg]) + ]) + if test "$have_readline" = "yes"; then + readline_libs="$readline_libs -lreadline" + AC_SEARCH_LIBS([rl_replace_line], [readline], [], [have_readline="no"]) + if test "$have_readline" = "no"; then # try with -lcurses + # clear cache + AC_MSG_NOTICE([trying again with -lcurses]) + unset ac_cv_search_rl_replace_line 2> /dev/null + AC_SEARCH_LIBS([rl_replace_line], [readline], [ + have_readline=yes + readline_libs="$readline_libs -lcurses" + ], [], [-lcurses]) + fi + if test "$have_readline" = "no"; then # try with -ltermcap + # clear cache + AC_MSG_NOTICE([trying again with -ltermcap]) + unset ac_cv_search_rl_replace_line 2> /dev/null + AC_SEARCH_LIBS([rl_replace_line], [readline], [ + have_readline=yes + readline_libs="$readline_libs -ltermcap" + ], [], [-ltermcap]) + fi + fi + + if test "$have_readline" = "yes"; then + all_errlist_objs="$all_errlist_objs interactive" + client_errlist_objs="$client_errlist_objs interactive" + client_ldflags="$client_ldflags $readline_libs" + audioc_errlist_objs="$audioc_errlist_objs buffer_tree interactive sched time" + audioc_ldflags="$audioc_ldflags $readline_libs" + AC_SUBST(readline_cppflags) + AC_DEFINE(HAVE_READLINE, 1, define to 1 to turn on readline support) + else + AC_MSG_WARN([libreadline not found or unusable]) + fi + CPPFLAGS="$OLD_CPPFLAGS" + LDFLAGS="$OLD_LDFLAGS" + LIBS="$OLD_LIBS" + ############################################################# AC_SUBST(install_sh, [$INSTALL]) @@@ -1048,6 -1069,7 +1110,7 @@@ AC_MSG_NOTICE( paraslash configuration: ~~~~~~~~~~~~~~~~~~~~~~~~ unix socket credentials: $have_ucred + readline (interactive CLIs): $have_readline audio formats supported by para_server/para_afh: $server_audio_formats id3 version2 support: $have_libid3tag filters supported by para_audiod/para_filter: $filters diff --combined error.h index 311b6ebd,55c0ab02..ae94157f --- a/error.h +++ b/error.h @@@ -38,24 -38,6 +38,24 @@@ DEFINE_ERRLIST_OBJECT_ENUM extern const char **para_errlist[]; +#define FLACDEC_FILTER_ERRORS \ + PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \ + PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \ + PARA_ERROR(FLACDEC_EOF, "flacdec encountered end of file condition"), \ + + +#define FLAC_AFH_ERRORS \ + PARA_ERROR(FLAC_CHAIN_ALLOC, "could not create metadata chain"), \ + PARA_ERROR(FLAC_CHAIN_READ, "could not read meta chain"), \ + PARA_ERROR(FLAC_ITER_ALLOC, "could not allocate meta iterator"), \ + PARA_ERROR(FLAC_VARBLOCK, "variable blocksize not supported"), \ + PARA_ERROR(FLAC_AFH_DECODER_ALLOC, "could not allocate stream decoder"), \ + PARA_ERROR(FLAC_AFH_DECODER_INIT, "could not init stream decoder"), \ + PARA_ERROR(FLAC_SKIP_META, "could not skip metadata"), \ + PARA_ERROR(FLAC_DECODE_POS, "could not get decode position"), \ + PARA_ERROR(FLAC_STREAMINFO, "could not read stream info meta block"), \ + + #define OGG_AFH_COMMON_ERRORS \ PARA_ERROR(STREAM_PACKETOUT, "ogg stream packet-out error (first packet)"), \ PARA_ERROR(SYNC_PAGEOUT, "ogg sync page-out error (no ogg file?)"), \ @@@ -248,6 -230,7 +248,7 @@@ #define AUDIOC_ERRORS \ PARA_ERROR(AUDIOC_SYNTAX, "audioc syntax error"), \ + PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \ #define CLIENT_COMMON_ERRORS \ @@@ -459,6 -442,10 +460,10 @@@ PARA_ERROR(QUEUE, "packet queue overrun"), \ + #define INTERACTIVE_ERRORS \ + PARA_ERROR(I9E_EOF, "end of input"), \ + PARA_ERROR(I9E_SETUPTERM, "failed to set up terminal"), \ + /** \endcond errors */ /** diff --combined string.c index f7d70353,d226af96..3bddd5e8 --- a/string.c +++ b/string.c @@@ -711,6 -711,32 +711,32 @@@ out return ret; } + /** + * Get the number of the word the cursor is on. + * + * \param buf The zero-terminated line buffer. + * \param delim Characters that separate words. + * \param point The cursor position. + * + * \return Zero-based word number. + */ + int compute_word_num(const char *buf, const char *delim, int point) + { + int ret, num_words; + const char *p; + char *word; + + for (p = buf, num_words = 0; ; p += ret, num_words++) { + ret = get_next_word(p, delim, &word); + if (ret <= 0) + break; + free(word); + if (p + ret >= buf + point) + break; + } + return num_words; + } + /** * Free an array of words created by create_argv(). * @@@ -793,56 -819,3 +819,56 @@@ int para_regcomp(regex_t *preg, const c free(buf); return -E_REGEX; } + +/** + * strdup() for not necessarily zero-terminated strings. + * + * \param src The source buffer. + * \param len The number of bytes to be copied. + * + * \return A 0-terminated buffer of length \a len + 1. + * + * This is similar to strndup(), which is a GNU extension. However, one + * difference is that strndup() returns \p NULL if insufficient memory was + * available while this function aborts in this case. + * + * \sa strdup(), \ref para_strdup(). + */ +char *safe_strdup(const char *src, size_t len) +{ + char *p; + + assert(len < (size_t)-1); + p = para_malloc(len + 1); + if (len > 0) + memcpy(p, src, len); + p[len] = '\0'; + return p; +} + +/** + * Copy the value of a key=value pair. + * + * This checks whether the given buffer starts with "key=", ignoring case. If + * yes, a copy of the value is returned. The source buffer may not be + * zero-terminated. + * + * \param src The source buffer. + * \param len The number of bytes of the tag. + * \param key Only copy if it is the value of this key. + * + * \return A zero-terminated buffer, or \p NULL if the key was + * not of the given type. + */ +char *key_value_copy(const char *src, size_t len, const char *key) +{ + int keylen = strlen(key); + + if (len <= keylen) + return NULL; + if (strncasecmp(src, key, keylen)) + return NULL; + if (src[keylen] != '=') + return NULL; + return safe_strdup(src + keylen + 1, len - keylen - 1); +} diff --combined string.h index 403b9c6f,23b79002..30af1fdb --- a/string.h +++ b/string.h @@@ -83,5 -83,4 +83,6 @@@ int create_argv(const char *buf, const void free_argv(char **argv); int para_regcomp(regex_t *preg, const char *regex, int cflags); void freep(void *arg); + int compute_word_num(const char *buf, const char *delim, int offset); +char *safe_strdup(const char *src, size_t len); +char *key_value_copy(const char *src, size_t len, const char *key); diff --combined web/manual.m4 index 7eecdbb3,5d9fbbb7..041457e6 --- a/web/manual.m4 +++ b/web/manual.m4 @@@ -115,6 -115,9 +115,9 @@@ All connections between para_server an with a symmetric RC4 session key. For each user of paraslash you must create a public/secret RSA key pair for authentication. + If para_client is started without non-option arguments, an interactive + session (shell) is started. Command history and command completion are + supported through libreadline. *para_audiod* @@@ -135,7 -138,8 +138,8 @@@ socket credentials, if available The client program which talks to para_audiod. Used to control para_audiod, to receive status info, or to grab the stream at any - point of the decoding process. + point of the decoding process. Like para_client, para_audioc supports + interactive sessions on systems with libreadline. *para_recv* @@@ -254,10 -258,6 +258,10 @@@ Optional - XREFERENCE(http://www.speex.org/, speex). In order to stream or decode speex files, libspeex (libspeex-dev) is required. + - XREFERENCE(http://flac.sourceforge.net/, flac). To stream + or decode files encoded with the _Free Lossless Audio Codec_, + libFLAC (libFLAC-dev) must be installed. + - XREFERENCE(ftp://ftp.alsa-project.org/pub/lib/, alsa-lib). On Linux, you'll need to have ALSA's development package libasound2-dev installed. @@@ -266,6 -266,10 +270,10 @@@ libao). Needed to build the ao writer (ESD, PulseAudio,...). Debian package: libao-dev. + - XREFERENCE(http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html, + GNU Readline). If this library (libreadline-dev) is installed, + para_client and para_audioc support interactive sessions. + Installation ~~~~~~~~~~~~ @@@ -1115,15 -1119,6 +1123,15 @@@ how meta data about the file is to be e is composed of superframes, each containing one or more frames of 2048 samples. For 16 bit stereo a WMA superframe is about 8K large. +*FLAC* + +The Free Lossless Audio Codec (FLAC) compresses audio without quality +loss. It gives better compression ratios than a general purpose +compressor like zip or bzip2 because FLAC is designed specifically +for audio. A FLAC-encoded file consits of frames of varying size, up +to 16K. Each frame starts with a header that contains all information +necessary to decode the frame. + Meta data ~~~~~~~~~ @@@ -1136,10 -1131,10 +1144,10 @@@ title, album, year and comment tags. Ea 32 characters long. ID3, version 2 is much more flexible but requires a separate library being installed for paraslash to support it. -Ogg vorbis files contain meta data as Vorbis comments, which are -typically implemented as strings of the form "[TAG]=[VALUE]". Unlike -ID3 version 1 tags, one may use whichever tags are appropriate for -the content. +Ogg vorbis, ogg speex and flac files contain meta data as Vorbis +comments, which are typically implemented as strings of the form +"[TAG]=[VALUE]". Unlike ID3 version 1 tags, one may use whichever +tags are appropriate for the content. AAC files usually use the MPEG-4 container format for storing meta data while WMA files wrap meta data as special objects within the @@@ -1157,7 -1152,7 +1165,7 @@@ paraslash uses the word "chunk" as comm of an audio file. For MP3 files, a chunk is the same as an MP3 frame, while for OGG files a chunk is an OGG page, etc. Therefore the chunk size varies considerably between audio formats, from a few hundred -bytes (MP3) up to 8K (WMA). +bytes (MP3) up to 16K (FLAC). The chunk table contains the offsets within the audio file that correspond to the chunk boundaries of the file. Like the meta data,