]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 't/interactive'
authorAndre Noll <maan@systemlinux.org>
Sun, 4 Dec 2011 23:13:32 +0000 (00:13 +0100)
committerAndre Noll <maan@systemlinux.org>
Sun, 4 Dec 2011 23:22:32 +0000 (00:22 +0100)
Conflicts:
string.h

1  2 
FEATURES
NEWS
afs.c
configure.ac
error.h
string.c
string.h
web/manual.m4

diff --combined FEATURES
index 613acc19c2f34a14d15a86921aaf94486ac04df2,d723541c7269ab670411f69d54ee7e64e0ba691c..6ad420dfe7a63800b3725f1295ec3c84cb580d5d
+++ 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 8bb2862e93012c815d50be0513dee99c2b8eda87,d4a58fd1e9a4e24f2a2d7e0b4d00ce1431602db1..1c6d821dbbc362ae77190724a37c2cc497d7fcd6
--- 1/NEWS
--- 2/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 78d8571fbaf1902b05054a3c15954795de79f858,b4c85dcb82eb2061501c2567005f66015b30b6f0..ff3c95fe03853b0aaa80d873ea75748a10bf73d2
--- 1/afs.c
--- 2/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;
  
        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. */
        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();
        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]);
        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 a2e5fed14569deb9c059172d76b83dce0f10daa1,66541fe4fd637c564875f5f45f734a086c6a86de..89242d969bbab440a8ac085262b3de45ea3d3392
@@@ -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"
  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 311b6ebdab1c4248acefd0c08d1c5346948ec996,55c0ab0264b241f50207cc1e0311443fdbc33f42..ae94157f81690568fc20a741c0f1757ef664e83a
+++ 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?)"), \
  
  #define AUDIOC_ERRORS \
        PARA_ERROR(AUDIOC_SYNTAX, "audioc syntax error"), \
+       PARA_ERROR(AUDIOC_EOF, "audioc: end of file"), \
  
  
  #define CLIENT_COMMON_ERRORS \
        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 f7d70353db977d6543055c19c7bd68a17b83a3ce,d226af963af82c6c99e557402694b919b26c2192..3bddd5e80896e843470687671a05cb0ef09051e2
+++ 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 403b9c6f15d15404076eeed7ea641b8b22ed600f,23b790021277475ea281e12a9b2a39d05e7ffb62..30af1fdb647f6701086b235324a6ff2867210822
+++ 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 7eecdbb31ad09169666f4d2e353f215432e12961,5d9fbbb7c78514317238b49f08a538fe1f7abb92..041457e6157a585cd479edba8e65ab45fcd5e207
@@@ -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.
        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,