Merge branch 'master' into next
authorAndre Noll <maan@systemlinux.org>
Sat, 11 Jul 2009 19:01:40 +0000 (21:01 +0200)
committerAndre Noll <maan@systemlinux.org>
Sat, 11 Jul 2009 19:01:40 +0000 (21:01 +0200)
1  2 
afs.c
aft.c
attribute.c
blob.c

diff --combined afs.c
--- 1/afs.c
--- 2/afs.c
+++ b/afs.c
@@@ -6,16 -6,11 +6,16 @@@
  
  /** \file afs.c Paraslash's audio file selector. */
  
 +#include <regex.h>
  #include <signal.h>
  #include <fnmatch.h>
 +#include <openssl/rc4.h>
 +#include <osl.h>
 +
  #include "server.cmdline.h"
  #include "para.h"
  #include "error.h"
 +#include "crypt.h"
  #include "string.h"
  #include "afh.h"
  #include "afs.h"
@@@ -29,7 -24,6 +29,7 @@@
  #include "sched.h"
  #include "signal.h"
  #include "fd.h"
 +#include "mood.h"
  
  /** The osl tables used by afs. \sa blob.c. */
  enum afs_table_num {
  };
  
  static struct afs_table afs_tables[NUM_AFS_TABLES] = {
-       [TBLNUM_AUDIO_FILES] = {.init = aft_init},
-       [TBLNUM_ATTRIBUTES] = {.init = attribute_init},
-       [TBLNUM_SCORES] = {.init = score_init},
-       [TBLNUM_MOODS] = {.init = moods_init},
-       [TBLNUM_LYRICS] = {.init = lyrics_init},
-       [TBLNUM_IMAGES] = {.init = images_init},
-       [TBLNUM_PLAYLIST] = {.init = playlists_init},
+       [TBLNUM_AUDIO_FILES] = {.init = aft_init, .name = "audio_files"},
+       [TBLNUM_ATTRIBUTES] = {.init = attribute_init, .name = "attributes"},
+       [TBLNUM_SCORES] = {.init = score_init, .name = "scores"},
+       [TBLNUM_MOODS] = {.init = moods_init, .name = "moods"},
+       [TBLNUM_LYRICS] = {.init = lyrics_init, .name = "lyrics"},
+       [TBLNUM_IMAGES] = {.init = images_init, .name = "images"},
+       [TBLNUM_PLAYLIST] = {.init = playlists_init, .name = "playlists"},
  };
  
  struct command_task {
@@@ -91,6 -85,7 +91,6 @@@ static struct signal_task signal_task_s
  static enum play_mode current_play_mode;
  static char *current_mop; /* mode or playlist specifier. NULL means dummy mooe */
  
 -
  /**
   * A random number used to "authenticate" the connection.
   *
@@@ -347,7 -342,7 +347,7 @@@ static int action_if_pattern_matches(st
        struct pattern_match_data *pmd = data;
        struct osl_object name_obj;
        const char *p, *name;
 -      int ret = osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj);
 +      int ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj));
        const char *pattern_txt = (const char *)pmd->patterns.data;
  
        if (ret < 0)
   *
   * \param pmd Describes what to match and how.
   *
 - * \return The return value of the underlying call to osl_rbtree_loop()
 - * or osl_rbtree_loop_reverse().
 + * \return Standard.
   */
  int for_each_matching_row(struct pattern_match_data *pmd)
  {
        if (pmd->pm_flags & PM_REVERSE_LOOP)
 -              return osl_rbtree_loop_reverse(pmd->table, pmd->loop_col_num, pmd,
 -                      action_if_pattern_matches);
 -      return osl_rbtree_loop(pmd->table, pmd->loop_col_num, pmd,
 -                      action_if_pattern_matches);
 +              return osl(osl_rbtree_loop_reverse(pmd->table, pmd->loop_col_num, pmd,
 +                      action_if_pattern_matches));
 +      return osl(osl_rbtree_loop(pmd->table, pmd->loop_col_num, pmd,
 +                      action_if_pattern_matches));
  }
  
  /**
@@@ -407,6 -403,80 +407,6 @@@ int string_compare(const struct osl_obj
        return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size));
  }
  
 -/*
 - * write input from fd to dynamically allocated buffer,
 - * but maximal max_size byte.
 - */
 -static int fd2buf(int fd, unsigned max_size, struct osl_object *obj)
 -{
 -      const size_t chunk_size = 1024;
 -      size_t size = 2048, received = 0;
 -      int ret;
 -      char *buf = para_malloc(size);
 -
 -      for (;;) {
 -              ret = recv_bin_buffer(fd, buf + received, chunk_size);
 -              if (ret <= 0)
 -                      break;
 -              received += ret;
 -              if (received + chunk_size >= size) {
 -                      size *= 2;
 -                      ret = -E_INPUT_TOO_LARGE;
 -                      if (size > max_size)
 -                              break;
 -                      buf = para_realloc(buf, size);
 -              }
 -      }
 -      obj->data = buf;
 -      obj->size = received;
 -      if (ret < 0)
 -              free(buf);
 -      return ret;
 -}
 -
 -/**
 - * Read data from a file descriptor, and send it to the afs process.
 - *
 - * \param fd File descriptor to read data from.
 - * \param arg_obj Pointer to the arguments to \a f.
 - * \param f The callback function.
 - * \param max_len Don't read more than that many bytes from stdin.
 - * \param result_handler See \ref send_callback_request.
 - * \param private_result_data See \ref send_callback_request.
 - *
 - * This function is used by commands that wish to let para_server store
 - * arbitrary data specified by the user (for instance the add_blob family of
 - * commands). First, at most \a max_len bytes are read from \a fd, the result
 - * is concatenated with the buffer given by \a arg_obj, and the combined buffer
 - * is made available to the afs process via the callback method. See \ref
 - * send_callback_request for details.
 - *
 - * \return Negative on errors, the return value of the underlying call to
 - * send_callback_request() otherwise.
 - */
 -int stdin_command(int fd, struct osl_object *arg_obj, callback_function *f,
 -              unsigned max_len, callback_result_handler *result_handler,
 -              void *private_result_data)
 -{
 -      struct osl_object query, stdin_obj;
 -      int ret;
 -
 -      ret = send_buffer(fd, AWAITING_DATA_MSG);
 -      if (ret < 0)
 -              return ret;
 -      ret = fd2buf(fd, max_len, &stdin_obj);
 -      if (ret < 0)
 -              return ret;
 -      query.size = arg_obj->size + stdin_obj.size;
 -      query.data = para_malloc(query.size);
 -      memcpy(query.data, arg_obj->data, arg_obj->size);
 -      memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size);
 -      free(stdin_obj.data);
 -      ret = send_callback_request(f, &query, result_handler, private_result_data);
 -      free(query.data);
 -      return ret;
 -}
 -
  static int pass_afd(int fd, char *buf, size_t size)
  {
        struct msghdr msg = {.msg_iov = NULL};
@@@ -582,22 -652,21 +582,22 @@@ out
   * Result handler for sending data to the para_client process.
   *
   * \param result The data to be sent.
 - * \param fd_ptr Pointer to the file descriptor.
 + * \param private Pointer to rc4 context.
   *
 - * \return The return value of the underlying call to send_bin_buffer().
 + * \return The return value of the underlying call to rc4_send_bin_buffer().
   *
 - * \sa \ref callback_result_handler.
 + * \sa \ref callback_result_handler, \ref rc4_send_bin_buffer().
   */
 -int send_result(struct osl_object *result, void *fd_ptr)
 +int rc4_send_result(struct osl_object *result, void *private)
  {
 -      int fd = *(int *)fd_ptr;
 +      struct rc4_context *rc4c = private;
 +
        if (!result->size)
                return 1;
 -      return send_bin_buffer(fd, result->data, result->size);
 +      return rc4_send_bin_buffer(rc4c, result->data, result->size);
  }
  
 -int com_select(int fd, int argc, char * const * const argv)
 +int com_select(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        struct osl_object query;
  
        query.data = argv[1];
        query.size = strlen(argv[1]) + 1;
        return send_callback_request(com_select_callback, &query,
 -              &send_result, &fd);
 +              &rc4_send_result, rc4c);
  }
  
  static void init_admissible_files(char *arg)
@@@ -661,7 -730,7 +661,7 @@@ static void get_database_dir(void
                else {
                        char *home = para_homedir();
                        database_dir = make_message(
 -                              "%s/.paraslash/afs_database", home);
 +                              "%s/.paraslash/afs_database-0.4", home);
                        free(home);
                }
        }
@@@ -1003,7 -1072,7 +1003,7 @@@ static void create_tables_callback(int 
  {
        uint32_t table_mask = *(uint32_t *)query->data;
        int i, ret;
-       char *buf;
+       struct para_buffer pb = {.buf = NULL};
  
        close_afs_tables();
        for (i = 0; i < NUM_AFS_TABLES; i++) {
                ret = t->create(database_dir);
                if (ret < 0)
                        goto out;
+               para_printf(&pb, "successfully created %s table\n", t->name);
        }
        ret = open_afs_tables();
  out:
-       if (ret >= 0)
-               buf = make_message("successfully created afs table(s)\n");
-       else
-               buf = make_message("%s\n", para_strerror(-ret));
-       pass_buffer_as_shm(buf, strlen(buf), &fd);
-       free(buf);
+       if (ret < 0)
+               para_printf(&pb, "%s\n", para_strerror(-ret));
+       if (pb.buf)
+               pass_buffer_as_shm(pb.buf, pb.offset, &fd);
+       free(pb.buf);
  }
  
 -int com_init(int fd, int argc, char * const * const argv)
 +int com_init(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        int i, j, ret;
        uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1;
                                return -E_BAD_TABLE_NAME;
                }
        }
 -      ret = send_callback_request(create_tables_callback, &query, &send_result, &fd);
 +      ret = send_callback_request(create_tables_callback, &query,
 +              rc4_send_result, rc4c);
        if (ret < 0)
 -              return send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              return rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
@@@ -1073,7 -1141,7 +1073,7 @@@ enum com_check_flags 
        CHECK_PLAYLISTS = 4
  };
  
 -int com_check(int fd, int argc, char * const * const argv)
 +int com_check(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        unsigned flags = 0;
        int i, ret;
        if (!flags)
                flags = ~0U;
        if (flags & CHECK_AFT) {
 -              ret = send_callback_request(aft_check_callback, NULL, send_result, &fd);
 +              ret = send_callback_request(aft_check_callback, NULL,
 +                      rc4_send_result, rc4c);
                if (ret < 0)
                        return ret;
        }
        if (flags & CHECK_PLAYLISTS) {
 -              ret = send_callback_request(playlist_check_callback, NULL, send_result, &fd);
 +              ret = send_callback_request(playlist_check_callback,
 +                      NULL, rc4_send_result, rc4c);
                if (ret < 0)
                        return ret;
        }
        if (flags & CHECK_MOODS) {
 -              ret = send_callback_request(mood_check_callback, NULL, send_result, &fd);
 +              ret = send_callback_request(mood_check_callback, NULL,
 +                      rc4_send_result, rc4c);
                if (ret < 0)
                        return ret;
        }
diff --combined aft.c
--- 1/aft.c
--- 2/aft.c
+++ b/aft.c
@@@ -6,18 -6,14 +6,18 @@@
  
  /** \file aft.c Audio file table functions. */
  
 +#include <regex.h>
  #include <dirent.h> /* readdir() */
 -#include "para.h"
 -#include "error.h"
 -#include "string.h"
 +#include <openssl/rc4.h>
  #include <sys/mman.h>
  #include <fnmatch.h>
  #include <sys/shm.h>
 +#include <osl.h>
  
 +#include "para.h"
 +#include "error.h"
 +#include "crypt.h"
 +#include "string.h"
  #include "afh.h"
  #include "afs.h"
  #include "net.h"
@@@ -27,8 -23,6 +27,8 @@@
  #include "portable_io.h"
  
  static struct osl_table *audio_file_table;
 +static char *status_items;
 +static char *parser_friendly_status_items;
  
  /** The different sorting methods of the ls command. */
  enum ls_sorting_method {
@@@ -69,9 -63,7 +69,9 @@@ enum ls_listing_mode 
        /** -lm */
        LS_MODE_MBOX,
        /** -lc */
 -      LS_MODE_CHUNKS
 +      LS_MODE_CHUNKS,
 +      /** -lp */
 +      LS_MODE_PARSER,
  };
  
  /** The flags accepted by the ls command. */
@@@ -228,27 -220,12 +228,27 @@@ enum audio_file_table_columns 
        NUM_AFT_COLUMNS
  };
  
 +/**
 + * Compare two osl objects pointing to hash values.
 + *
 + * \param obj1 Pointer to the first hash object.
 + * \param obj2 Pointer to the second hash object.
 + *
 + * \return The values required for an osl compare function.
 + *
 + * \sa osl_compare_func, uint32_compare().
 + */
 +int aft_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2)
 +{
 +      return hash_compare((HASH_TYPE *)obj1->data, (HASH_TYPE *)obj2->data);
 +}
 +
  static struct osl_column_description aft_cols[] = {
        [AFTCOL_HASH] = {
                .storage_type = OSL_MAPPED_STORAGE,
                .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
                .name = "hash",
 -              .compare_function = osl_hash_compare,
 +              .compare_function = aft_hash_compare,
                .data_size = HASH_SIZE
        },
        [AFTCOL_PATH] = {
@@@ -361,29 -338,23 +361,29 @@@ enum afhi_offsets 
        CHUNK_TV_TV_USEC_OFFSET = 36,
        /** Number of channels is stored here. (1 byte) */
        AFHI_CHANNELS_OFFSET = 40,
 -      /** EOF timeout in ms. (2 byte) */
 -      AFHI_EOF_OFFSET = 41,
        /** The tag info position. */
 -      AFHI_INFO_STRING_OFFSET = 43,
 +      AFHI_INFO_STRING_OFFSET = 41,
        /** Minimal on-disk size of a valid afhi struct. */
 -      MIN_AFHI_SIZE = 44
 +      MIN_AFHI_SIZE = 47, /* at least 6 null bytes for techinfo/tags */
  };
  
  static unsigned sizeof_afhi_buf(const struct afh_info *afhi)
  {
        if (!afhi)
                return 0;
 -      return strlen(afhi->info_string) + MIN_AFHI_SIZE;
 +      return MIN_AFHI_SIZE
 +              + strlen(afhi->techinfo)
 +              + strlen(afhi->tags.artist)
 +              + strlen(afhi->tags.title)
 +              + strlen(afhi->tags.year)
 +              + strlen(afhi->tags.album)
 +              + strlen(afhi->tags.comment);
  }
  
  static void save_afhi(struct afh_info *afhi, char *buf)
  {
 +      char *p;
 +
        if (!afhi)
                return;
        write_u32(buf + AFHI_SECONDS_TOTAL_OFFSET, afhi->seconds_total);
        write_u32(buf + HEADER_OFFSET_OFFSET, afhi->header_offset);
        write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec);
        write_u32(buf + CHUNK_TV_TV_USEC_OFFSET, afhi->chunk_tv.tv_usec);
 -      write_u16(buf + AFHI_EOF_OFFSET, tv2ms(&afhi->eof_tv));
 -      strcpy(buf + AFHI_INFO_STRING_OFFSET, afhi->info_string); /* OK */
 +      p = buf + AFHI_INFO_STRING_OFFSET;
 +      /* The sprintf's below are OK as our caller made sure that buf is large enough */
 +      p += sprintf(p, "%s", afhi->techinfo) + 1;
 +      p += sprintf(p, "%s", afhi->tags.artist) + 1;
 +      p += sprintf(p, "%s", afhi->tags.title) + 1;
 +      p += sprintf(p, "%s", afhi->tags.year) + 1;
 +      p += sprintf(p, "%s", afhi->tags.album) + 1;
 +      sprintf(p, "%s", afhi->tags.comment);
  }
  
  static void load_afhi(const char *buf, struct afh_info *afhi)
        afhi->header_offset = read_u32(buf + HEADER_OFFSET_OFFSET);
        afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET);
        afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET);
 -      ms2tv(read_u16(buf + AFHI_EOF_OFFSET), &afhi->eof_tv);
 -      afhi->info_string = para_strdup(buf + AFHI_INFO_STRING_OFFSET);
 +      afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET;
 +      afhi->tags.artist = afhi->techinfo + strlen(afhi->techinfo) + 1;
 +      afhi->tags.title = afhi->tags.artist + strlen(afhi->tags.artist) + 1;
 +      afhi->tags.year = afhi->tags.title + strlen(afhi->tags.title) + 1;
 +      afhi->tags.album = afhi->tags.year + strlen(afhi->tags.year) + 1;
 +      afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1;
  }
  
  static unsigned sizeof_chunk_table(struct afh_info *afhi)
@@@ -458,27 -419,27 +458,27 @@@ static void load_chunk_table(struct afh
   * \param path The full path of the audio file.
   * \param row Result pointer.
   *
 - * \return The return value of the underlying call to osl_get_row().
 + * \return Standard.
   */
  int aft_get_row_of_path(const char *path, struct osl_row **row)
  {
        struct osl_object obj = {.data = (char *)path, .size = strlen(path) + 1};
  
 -      return osl_get_row(audio_file_table, AFTCOL_PATH, &obj, row);
 +      return osl(osl_get_row(audio_file_table, AFTCOL_PATH, &obj, row));
  }
  
  /**
   * Get the row of the audio file table corresponding to the given hash value.
   *
   * \param hash The hash value of the desired audio file.
 - * \param row resul pointer.
 + * \param row Result pointer.
   *
 - * \return The return value of the underlying call to osl_get_row().
 + * \return Standard.
   */
  int aft_get_row_of_hash(HASH_TYPE *hash, struct osl_row **row)
  {
        const struct osl_object obj = {.data = hash, .size = HASH_SIZE};
 -      return osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row);
 +      return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row));
  }
  
  /**
   * \param row Pointer to a row in the audio file table.
   * \param obj Result pointer.
   *
 - * \return The return value of the underlying call to osl_get_object().
 + * \return Standard.
   */
  int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj)
  {
 -      return osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj);
 +      return osl(osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj));
  }
  
  /**
@@@ -560,8 -521,8 +560,8 @@@ int get_afsi_of_path(const char *path, 
  int get_audio_file_path_of_row(const struct osl_row *row, char **path)
  {
        struct osl_object path_obj;
 -      int ret = osl_get_object(audio_file_table, row, AFTCOL_PATH,
 -              &path_obj);
 +      int ret = osl(osl_get_object(audio_file_table, row, AFTCOL_PATH,
 +              &path_obj));
        if (ret < 0)
                return ret;
        *path = path_obj.data;
   *
   * \sa get_hash_of_row().
   */
 -static int get_hash_object_of_aft_row(const struct osl_row *row, struct osl_object *obj)
 +static int get_hash_object_of_aft_row(const struct osl_row *row,
 +              struct osl_object *obj)
  {
 -      return osl_get_object(audio_file_table, row, AFTCOL_HASH, obj);
 +      return osl(osl_get_object(audio_file_table, row, AFTCOL_HASH, obj));
  }
  
  /**
@@@ -619,8 -579,8 +619,8 @@@ static int get_hash_of_row(const struc
  int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi)
  {
        struct osl_object obj;
 -      int ret = osl_get_object(audio_file_table, row, AFTCOL_AFHI,
 -              &obj);
 +      int ret = osl(osl_get_object(audio_file_table, row, AFTCOL_AFHI,
 +              &obj));
        if (ret < 0)
                return ret;
        load_afhi(obj.data, afhi);
@@@ -731,68 -691,56 +731,68 @@@ static void get_duration_buf(int second
        }
  }
  
 -static char *make_attribute_lines(const char *att_bitmap, struct afs_info *afsi)
 +
 +static int write_attribute_items(struct para_buffer *b,
 +              const char *att_bitmap, struct afs_info *afsi)
  {
 -      char *att_text, *att_lines;
 +      char *att_text;
 +      int ret;
  
 -      get_attribute_text(&afsi->attributes, " ", &att_text);
 -      if (!att_text)
 -              return para_strdup(att_bitmap);
 -      att_lines = make_message("%s: %s\n%s: %s",
 -              status_item_list[SI_ATTRIBUTES_BITMAP], att_bitmap,
 -              status_item_list[SI_ATTRIBUTES_TXT], att_text);
 +      ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap);
 +      if (ret < 0)
 +              return ret;
 +      ret = get_attribute_text(&afsi->attributes, " ", &att_text);
 +      if (ret < 0)
 +              return ret;
 +      ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text);
        free(att_text);
 -      return att_lines;
 +      return ret;
  }
  
 -static char *make_lyrics_lines(struct afs_info *afsi)
 +static int write_lyrics_items(struct para_buffer *b, struct afs_info *afsi)
  {
        char *lyrics_name;
 +      int ret;
  
 +      ret = WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id);
 +      if (ret < 0)
 +              return ret;
        lyr_get_name_by_id(afsi->lyrics_id, &lyrics_name);
 -      return make_message("%s: %u\n%s: %s\n",
 -              status_item_list[SI_LYRICS_ID], afsi->lyrics_id,
 -              status_item_list[SI_LYRICS_NAME], lyrics_name?
 -                      lyrics_name : "(none)");
 +      return WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name?
 +              lyrics_name : "(none)");
  }
  
 -static char *make_image_lines(struct afs_info *afsi)
 +static int write_image_items(struct para_buffer *b, struct afs_info *afsi)
  {
        char *image_name;
 +      int ret;
 +
 +      ret = WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id);
 +      if (ret < 0)
 +              return ret;
        img_get_name_by_id(afsi->image_id, &image_name);
 -      return make_message("%s: %u\n%s: %s\n",
 -              status_item_list[SI_IMAGE_ID], afsi->image_id,
 -              status_item_list[SI_IMAGE_NAME], image_name?
 -                      image_name : "(none)");
 +      return WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name?
 +              image_name : "(none)");
  }
  
 -static char *make_filename_lines(const char *path, unsigned flags)
 +static int write_filename_items(struct para_buffer *b, const char *path,
 +              unsigned flags)
  {
 -      char *dirname, *ret;
 -      const char *basename;
 +      char *val;
 +      int ret;
  
        if (!(flags & LS_FLAG_FULL_PATH))
 -              return make_message("%s: %s\n",
 -                      status_item_list[SI_BASENAME], path);
 -      basename = para_basename(path),
 -      dirname = para_dirname(path);
 -      ret = make_message("%s: %s\n%s: %s\n%s: %s\n",
 -              status_item_list[SI_PATH], path,
 -              status_item_list[SI_DIRECTORY], dirname? dirname : "?",
 -              status_item_list[SI_BASENAME], basename? basename : "?");
 -      free(dirname);
 +              return WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
 +      ret = WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path);
 +      if (ret < 0)
 +              return ret;
 +      val = para_basename(path);
 +      ret = WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : "");
 +      if (ret < 0)
 +              return ret;
 +      val = para_dirname(path);
 +      ret = WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : "");
 +      free(val);
        return ret;
  }
  
@@@ -830,14 -778,6 +830,14 @@@ out
        return ret;
  }
  
 +static int write_score(struct para_buffer *b, struct ls_data *d,
 +              struct ls_options *opts)
 +{
 +      if (!(opts->flags & LS_FLAG_ADMISSIBLE_ONLY)) /* no score*/
 +              return 0;
 +      return WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
 +}
 +
  static int print_list_item(struct ls_data *d, struct ls_options *opts,
        struct para_buffer *b, time_t current_time)
  {
        char att_buf[65];
        char last_played_time[30];
        char duration_buf[30]; /* nobody has an audio file long enough to overflow this */
 -      char score_buf[30] = "";
        struct afs_info *afsi = &d->afsi;
        struct afh_info *afhi = &d->afhi;
 -      struct ls_widths *w = &opts->widths;
 -      int have_score = opts->flags & LS_FLAG_ADMISSIBLE_ONLY;
        char asc_hash[2 * HASH_SIZE + 1];
 -      char *att_lines, *lyrics_lines, *image_lines, *filename_lines;
  
        if (opts->mode == LS_MODE_SHORT) {
                ret = para_printf(b, "%s\n", d->path);
                        goto out;
        }
        get_duration_buf(afhi->seconds_total, duration_buf, opts);
 -      if (have_score) {
 -              if (opts->mode == LS_MODE_LONG)
 -                      sprintf(score_buf, "%*li ", w->score_width, d->score);
 -              else
 -                      sprintf(score_buf, "%li ", d->score);
 -      }
 -
        if (opts->mode == LS_MODE_LONG) {
 +              struct ls_widths *w = &opts->widths;
 +              if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) {
 +                      ret = para_printf(b, "%*li ",
 +                              opts->widths.score_width, d->score);
 +                      if (ret < 0)
 +                              goto out;
 +              }
                ret = para_printf(b,
 -                      "%s"    /* score */
                        "%s "   /* attributes */
                        "%*u "  /* amp */
                        "%*d "  /* image_id  */
                        "%*d "  /* num_played */
                        "%s "   /* last_played */
                        "%s\n", /* path */
 -                      score_buf,
                        att_buf,
                        w->amp_width, afsi->amp,
                        w->image_id_width, afsi->image_id,
                );
                goto out;
        }
 -      hash_to_asc(d->hash, asc_hash);
 -      att_lines = make_attribute_lines(att_buf, afsi);
 -      lyrics_lines = make_lyrics_lines(afsi);
 -      image_lines = make_image_lines(afsi);
 -      filename_lines = make_filename_lines(d->path, opts->flags);
        if (opts->mode == LS_MODE_MBOX) {
                const char *bn = para_basename(d->path);
                ret = para_printf(b,
                if (ret < 0)
                        goto out;
        }
 -      ret = para_printf(b,
 -              "%s" /* filename stuff */
 -              "%s%s%s%s" /* score */
 -              "%s\n" /* attributes */
 -              "%s: %s\n" /* hash */
 -              "%s" /* image id, image name */
 -              "%s" /* lyrics */
 -              "%s: %dkbit/s\n" /* bitrate */
 -              "%s: %s\n" /* format */
 -              "%s: %dHz\n" /* frequency */
 -              "%s: %d\n" /* channels */
 -              "%s: %s\n" /* duration */
 -              "%s: %lu\n" /* seconds total */
 -              "%s: %s\n" /* last played time */
 -              "%s: %d\n" /* num_played */
 -              "%s: %u\n" /* ampplification */
 -              "%s" /* tag info */
 -              "%s: %lu\n" /* chunk time */
 -              "%s: %lu\n", /* num chunks */
 -              filename_lines,
 -              have_score? status_item_list[SI_SCORE] : "",
 -                      have_score? ": " : "",
 -                      score_buf,
 -                      have_score? "\n" : "",
 -              att_lines,
 -              status_item_list[SI_HASH], asc_hash,
 -              image_lines,
 -              lyrics_lines,
 -              status_item_list[SI_BITRATE], afhi->bitrate,
 -              status_item_list[SI_FORMAT], audio_format_name(afsi->audio_format_id),
 -              status_item_list[SI_FREQUENCY], afhi->frequency,
 -              status_item_list[SI_CHANNELS], afhi->channels,
 -              status_item_list[SI_DURATION], duration_buf,
 -              status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total,
 -              status_item_list[SI_LAST_PLAYED], last_played_time,
 -              status_item_list[SI_NUM_PLAYED], afsi->num_played,
 -              status_item_list[SI_AMPLIFICATION], afsi->amp,
 -              afhi->info_string,
 -              status_item_list[SI_CHUNK_TIME], tv2ms(&afhi->chunk_tv),
 -              status_item_list[SI_NUM_CHUNKS], afhi->chunks_total
 -      );
 +      ret = write_filename_items(b, d->path, opts->flags);
 +      if (ret < 0)
 +              goto out;
 +      ret = write_score(b, d, opts);
 +      if (ret < 0)
 +              goto out;
 +      ret = write_attribute_items(b, att_buf, afsi);
 +      if (ret < 0)
 +              goto out;
 +      ret = write_image_items(b, afsi);
 +      if (ret < 0)
 +              goto out;
 +      ret = write_lyrics_items(b, afsi);
 +      if (ret < 0)
 +              goto out;
 +      hash_to_asc(d->hash, asc_hash);
 +      ret = WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n",
 +              audio_format_name(afsi->audio_format_id));
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%lu\n",
 +              afhi->seconds_total);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n",
 +              tv2ms(&afhi->chunk_tv));
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%lu\n",
 +              afhi->chunks_total);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album);
 +      if (ret < 0)
 +              goto out;
 +      ret = WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment);
        if (ret < 0)
                goto out;
        if (opts->mode == LS_MODE_MBOX) {
                        osl_close_disk_object(&lyrics_def);
                }
        }
 -      free(att_lines);
 -      free(lyrics_lines);
 -      free(image_lines);
 -      free(filename_lines);
  out:
 -      free(afhi->info_string);
        return ret;
  }
  
 -/**
 - * Write a list of audio-file related status items with empty values.
 - *
 - * \param buf Result pointer.
 - *
 - * This is used by vss when currently no audio file is open.
 - */
 -void make_empty_status_items(char *buf)
 -{
 -      sprintf(buf,
 -              "%s: \n" /* path */
 -              "%s: \n" /* dirname */
 -              "%s: \n" /* basename */
 -              "%s: \n" /* score */
 -              "%s: \n" /* attributes bitmap */
 -              "%s: \n" /* attributes txt */
 -              "%s: \n" /* hash */
 -              "%s: \n" /* image id */
 -              "%s: \n" /* image name */
 -              "%s: \n" /* lyrics id */
 -              "%s: \n" /* lyrics name */
 -              "%s: \n" /* bitrate */
 -              "%s: \n" /* format */
 -              "%s: \n" /* frequency */
 -              "%s: \n" /* channels */
 -              "%s: \n" /* duration */
 -              "%s: \n" /* seconds total */
 -              "%s: \n" /* num played */
 -              "%s: \n" /* last played */
 -              "%s: \n" /* audio file info */
 -              "%s: \n" /* taginfo1 */
 -              "%s: \n" /* taginfo2 */
 -              "%s: \n" /* amplification */
 -              ,
 -              status_item_list[SI_PATH],
 -              status_item_list[SI_DIRECTORY],
 -              status_item_list[SI_BASENAME],
 -              status_item_list[SI_SCORE],
 -              status_item_list[SI_ATTRIBUTES_BITMAP],
 -              status_item_list[SI_ATTRIBUTES_TXT],
 -              status_item_list[SI_HASH],
 -              status_item_list[SI_IMAGE_ID],
 -              status_item_list[SI_IMAGE_NAME],
 -              status_item_list[SI_LYRICS_ID],
 -              status_item_list[SI_LYRICS_NAME],
 -              status_item_list[SI_BITRATE],
 -              status_item_list[SI_FORMAT],
 -              status_item_list[SI_FREQUENCY],
 -              status_item_list[SI_CHANNELS],
 -              status_item_list[SI_DURATION],
 -              status_item_list[SI_SECONDS_TOTAL],
 -              status_item_list[SI_NUM_PLAYED],
 -              status_item_list[SI_LAST_PLAYED],
 -              status_item_list[SI_AUDIO_FILE_INFO],
 -              status_item_list[SI_TAGINFO1],
 -              status_item_list[SI_TAGINFO2],
 -              status_item_list[SI_AMPLIFICATION]
 -      );
 -}
 -
 -static void fixup_taginfo(char *begin, char *end)
 -{
 -      char *p = begin;
 -
 -      for (;;) {
 -              p = strchr(p, '\n');
 -              if (!p)
 -                      break;
 -              if (p >= end - 1)
 -                      break;
 -              *p = ' ';
 -              p++;
 -      }
 -}
 -
 -/* crap, remove this ASAP. */
 -static int fixup_info_string(char *info_string)
 -{
 -      char *t1, *t2, *end;
 -
 -      if (strncmp(info_string, "audio_file_info:", 16))
 -              return -ERRNO_TO_PARA_ERROR(EINVAL);
 -      t1 = strstr(info_string, "\ntaginfo1:");
 -      if (!t1)
 -              return -ERRNO_TO_PARA_ERROR(EINVAL);
 -      t2 = strstr(info_string, "\ntaginfo2: ");
 -      if (!t2)
 -              return -ERRNO_TO_PARA_ERROR(EINVAL);
 -
 -      end = t2 + strlen(t2) + 1;
 -      fixup_taginfo(info_string + 16, t1);
 -      fixup_taginfo(t1 + 10, t2);
 -      fixup_taginfo(t2 + 10, end);
 -
 -      if (t1 - info_string < 80 && t2 - t1 < 80 && end - t2 < 80)
 -              return 0;
 -      if (t1 - info_string >= 80) {
 -              memmove(info_string + 80, t1, end - t1);
 -              t1 = info_string + 80;
 -              t2 -= t1 - info_string - 80;
 -              end -= t1 - info_string - 80;
 -      }
 -      if (t2 - t1 >= 80) {
 -              memmove(t1 + 80, t2, end - t2);
 -              end -= t2 - t1 - 80;
 -              t2 = t1 + 80;
 -      }
 -      if (end - t2 >= 80) {
 -              t2[78] = '\n';
 -              t2[79] = '\0';
 -      }
 -      return 1;
 -}
 -
  static int make_status_items(struct audio_file_data *afd,
                struct afs_info *afsi, char *path, long score,
                HASH_TYPE *hash)
                .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY,
                .mode = LS_MODE_VERBOSE,
        };
 -      struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1};
 +      struct para_buffer pb = {.max_size = SHMMAX - 1};
        time_t current_time;
        int ret;
  
 -      ret = fixup_info_string(afd->afhi.info_string);
 -      if (ret < 0) {
 -              PARA_WARNING_LOG("ignoring invalid tag info\n");
 -              afd->afhi.info_string[0] = '\0';
 -      } else if (ret)
 -              PARA_NOTICE_LOG("truncated overlong tag info\n");
        time(&current_time);
 -      ret = print_list_item(&d, &opts, &pb, current_time); /* frees info string */
 -      afd->afhi.info_string = NULL;
 +      ret = print_list_item(&d, &opts, &pb, current_time);
        if (ret < 0)
 -              goto out;
 -      strncpy(afd->verbose_ls_output, pb.buf, VERBOSE_LS_OUTPUT_SIZE);
 -      afd->verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE - 1] = '\0';
 -out:
 -      free(pb.buf);
 -      return ret;
 +              return ret;
 +      free(status_items);
 +      status_items = pb.buf;
 +      memset(&pb, 0, sizeof(pb));
 +      pb.max_size = SHMMAX - 1;
 +      pb.flags = PBF_SIZE_PREFIX;
 +      ret = print_list_item(&d, &opts, &pb, current_time);
 +      if (ret < 0) {
 +              free(status_items);
 +              status_items = NULL;
 +              return ret;
 +      }
 +      free(parser_friendly_status_items);
 +      parser_friendly_status_items = pb.buf;
 +      return 1;
  }
  
  /**
@@@ -1110,6 -1147,7 +1110,6 @@@ int open_and_update_audio_file(struct o
        ret = save_afd(afd);
  err:
        free(afd->afhi.chunk_table);
 -      free(afd->afhi.info_string);
        osl_close_disk_object(&chunk_table_obj);
        return ret;
  }
@@@ -1312,6 -1350,7 +1312,6 @@@ static int prepare_ls_row(struct osl_ro
        }
        return 1;
  err:
 -      free(d->afhi.info_string);
        return ret;
  }
  
@@@ -1320,11 -1359,11 +1320,11 @@@ static void com_ls_callback(int fd, con
        struct ls_options *opts = query->data;
        char *p, *pattern_start = (char *)query->data + sizeof(*opts);
        struct para_buffer b = {.max_size = SHMMAX,
 +              .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0,
                .max_size_handler = pass_buffer_as_shm, .private_data = &fd};
        int i = 0, ret;
        time_t current_time;
  
 -
        if (opts->num_patterns) {
                opts->patterns = para_malloc(opts->num_patterns * sizeof(char *));
                for (i = 0, p = pattern_start; i < opts->num_patterns; i++) {
        if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY)
                ret = admissible_file_loop(opts, prepare_ls_row);
        else
 -              ret = osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
 -                      prepare_ls_row);
 +              ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
 +                      prepare_ls_row));
        if (ret < 0)
                goto out;
        if (!opts->num_matching_paths)
@@@ -1370,7 -1409,7 +1370,7 @@@ out
  /*
   * TODO: flags -h (sort by hash)
   */
 -int com_ls(int fd, int argc, char * const * const argv)
 +int com_ls(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        int i, ret;
        unsigned flags = 0;
                        case 'c':
                                mode = LS_MODE_CHUNKS;
                                continue;
 +                      case 'p':
 +                              mode = LS_MODE_PARSER;
 +                              continue;
                        default:
                                return -E_AFT_SYNTAX;
                        }
        opts.mode = mode;
        opts.num_patterns = argc - i;
        ret = send_option_arg_callback_request(&query, opts.num_patterns,
 -              argv + i, com_ls_callback, send_result, &fd);
 +              argv + i, com_ls_callback, rc4_send_result, rc4c);
        return ret;
  }
  
   * \param private_data An arbitrary data pointer, passed to \a func.
   * \param func The custom function to be called.
   *
 - * \return The return value of the underlying call to osl_rbtree_loop().
 + * \return Standard.
   */
  int audio_file_loop(void *private_data, osl_rbtree_loop_func *func)
  {
 -      return osl_rbtree_loop(audio_file_table, AFTCOL_HASH, private_data,
 -              func);
 +      return osl(osl_rbtree_loop(audio_file_table, AFTCOL_HASH, private_data,
 +              func));
  }
  
  static struct osl_row *find_hash_sister(HASH_TYPE *hash)
@@@ -1657,7 -1693,7 +1657,7 @@@ static void com_add_callback(int fd, co
        PARA_INFO_LOG("request to add %s\n", path);
        hs = find_hash_sister(hash);
        ret = aft_get_row_of_path(path, &pb);
 -      if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
 +      if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
                goto out;
        if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
                if (flags & ADD_FLAG_VERBOSE)
                                if (ret < 0)
                                        goto out;
                        }
 -                      ret = osl_del_row(audio_file_table, pb);
 +                      ret = osl(osl_del_row(audio_file_table, pb));
                        if (ret < 0)
                                goto out;
                        pb = NULL;
                }
                /* file rename, update hs' path */
                if (flags & ADD_FLAG_VERBOSE) {
 -                      ret = osl_get_object(audio_file_table, hs,
 -                              AFTCOL_PATH, &obj);
 +                      ret = osl(osl_get_object(audio_file_table, hs,
 +                              AFTCOL_PATH, &obj));
                        if (ret < 0)
                                goto out;
                        ret = para_printf(&msg, "renamed from %s\n", (char *)obj.data);
                        if (ret < 0)
                                goto out;
                }
 -              ret = osl_update_object(audio_file_table, hs, AFTCOL_PATH,
 -                      &objs[AFTCOL_PATH]);
 +              ret = osl(osl_update_object(audio_file_table, hs, AFTCOL_PATH,
 +                      &objs[AFTCOL_PATH]));
                if (ret < 0)
                        goto out;
                afs_event(AUDIO_FILE_RENAME, &msg, hs);
                        if (ret < 0)
                                goto out;
                }
 -              ret = osl_update_object(audio_file_table, row, AFTCOL_AFHI,
 -                      &objs[AFTCOL_AFHI]);
 +              ret = osl(osl_update_object(audio_file_table, row, AFTCOL_AFHI,
 +                      &objs[AFTCOL_AFHI]));
                if (ret < 0)
                        goto out;
 -              ret = osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
 -                      &objs[AFTCOL_CHUNKS]);
 +              ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
 +                      &objs[AFTCOL_CHUNKS]));
                if (ret < 0)
                        goto out;
                afs_event(AFHI_CHANGE, &msg, row);
        objs[AFTCOL_AFSI].data = &afsi_buf;
        objs[AFTCOL_AFSI].size = AFSI_SIZE;
        save_afsi(&default_afsi, &objs[AFTCOL_AFSI]);
 -      ret = osl_add_and_get_row(audio_file_table, objs, &aft_row);
 +      ret = osl(osl_add_and_get_row(audio_file_table, objs, &aft_row));
        afs_event(AUDIO_FILE_ADD, &msg, aft_row);
  out:
        if (ret < 0)
  
  /** Used by com_add(). */
  struct private_add_data {
 -      /** The socket file descriptor. */
 -      int fd;
 +      /** The socket file descriptor, including rc4 keys. */
 +      struct rc4_context *rc4c;
        /** The given add flags. */
        uint32_t flags;
  };
@@@ -1820,13 -1856,12 +1820,13 @@@ static int add_one_audio_file(const cha
        query.size = strlen(path) + 1;
        ret = send_callback_request(path_brother_callback, &query,
                get_row_pointer_from_result, &pb);
 -      if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
 +      if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
                goto out_free;
        ret = 1;
        if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
                if (pad->flags & ADD_FLAG_VERBOSE)
 -                      send_ret = send_va_buffer(pad->fd, "lazy-ignore: %s\n", path);
 +                      send_ret = rc4_send_va_buffer(pad->rc4c,
 +                              "lazy-ignore: %s\n", path);
                goto out_free;
        }
        /* We still want to add this file. Compute its hash. */
        ret = 1;
        if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
                if (pad->flags & ADD_FLAG_VERBOSE)
 -                      send_ret = send_va_buffer(pad->fd,
 +                      send_ret = rc4_send_va_buffer(pad->rc4c,
                                "%s exists, not forcing update\n", path);
                goto out_unmap;
        }
        munmap(map.data, map.size);
        close(fd);
        if (pad->flags & ADD_FLAG_VERBOSE) {
 -              send_ret = send_va_buffer(pad->fd, "adding %s\n", path);
 +              send_ret = rc4_send_va_buffer(pad->rc4c, "adding %s\n", path);
                if (send_ret < 0)
                        goto out_free;
        }
        save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj);
        /* Ask afs to consider this entry for adding. */
 -      ret = send_callback_request(com_add_callback, &obj, send_result, &pad->fd);
 +      ret = send_callback_request(com_add_callback, &obj, rc4_send_result, pad->rc4c);
        goto out_free;
  
  out_unmap:
        munmap(map.data, map.size);
  out_free:
        if (ret < 0 && send_ret >= 0)
 -              send_ret = send_va_buffer(pad->fd, "failed to add %s (%s)\n", path,
 -                      para_strerror(-ret));
 +              send_ret = rc4_send_va_buffer(pad->rc4c,
 +                      "failed to add %s (%s)\n", path, para_strerror(-ret));
        free(obj.data);
        if (afhi_ptr) {
                free(afhi_ptr->chunk_table);
 -              free(afhi_ptr->info_string);
 +              free(afhi_ptr->techinfo);
 +              free(afhi_ptr->tags.artist);
 +              free(afhi_ptr->tags.title);
 +              free(afhi_ptr->tags.year);
 +              free(afhi_ptr->tags.album);
 +              free(afhi_ptr->tags.comment);
        }
        /* Stop adding files only on send errors. */
        return send_ret;
  }
  
 -int com_add(int fd, int argc, char * const * const argv)
 +int com_add(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        int i, ret;
 -      struct private_add_data pad = {.fd = fd, .flags = 0};
 +      struct private_add_data pad = {.rc4c = rc4c, .flags = 0};
        struct stat statbuf;
  
        for (i = 1; i < argc; i++) {
                char *path;
                ret = verify_path(argv[i], &path);
                if (ret < 0) {
 -                      ret = send_va_buffer(fd, "%s: %s\n", argv[i],
 +                      ret = rc4_send_va_buffer(rc4c, "%s: %s\n", argv[i],
                                para_strerror(-ret));
                        if (ret < 0)
                                return ret;
                }
                ret = stat(path, &statbuf);
                if (ret < 0) {
 -                      ret = send_va_buffer(fd, "failed to stat %s (%s)\n", path,
 +                      ret = rc4_send_va_buffer(rc4c, "failed to stat %s (%s)\n", path,
                                strerror(errno));
                        free(path);
                        if (ret < 0)
                else
                        ret = add_one_audio_file(path, &pad);
                if (ret < 0) {
 -                      send_va_buffer(fd, "%s: %s\n", path, para_strerror(-ret));
 +                      rc4_send_va_buffer(rc4c, "%s: %s\n", path, para_strerror(-ret));
                        free(path);
                        return ret;
                }
@@@ -2084,7 -2114,7 +2084,7 @@@ static void com_touch_callback(int fd, 
        free(tad.pb.buf);
  }
  
 -int com_touch(int fd, int argc, char * const * const argv)
 +int com_touch(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        struct com_touch_options cto = {
                .num_played = -1,
                        i++;
                        break;
                }
 -              if (!strncmp(arg, "-n", 2)) {
 -                      ret = para_atoi32(arg + 2, &cto.num_played);
 +              if (!strncmp(arg, "-n=", 3)) {
 +                      ret = para_atoi32(arg + 3, &cto.num_played);
                        if (ret < 0)
                                return ret;
                        continue;
                }
 -              if (!strncmp(arg, "-l", 2)) {
 -                      ret = para_atoi64(arg + 2, &cto.last_played);
 +              if (!strncmp(arg, "-l=", 3)) {
 +                      ret = para_atoi64(arg + 3, &cto.last_played);
                        if (ret < 0)
                                return ret;
                        continue;
                }
 -              if (!strncmp(arg, "-y", 2)) {
 -                      ret = para_atoi32(arg + 2, &cto.lyrics_id);
 +              if (!strncmp(arg, "-y=", 3)) {
 +                      ret = para_atoi32(arg + 3, &cto.lyrics_id);
                        if (ret < 0)
                                return ret;
                        continue;
                }
 -              if (!strncmp(arg, "-i", 2)) {
 -                      ret = para_atoi32(arg + 2, &cto.image_id);
 +              if (!strncmp(arg, "-i=", 3)) {
 +                      ret = para_atoi32(arg + 3, &cto.image_id);
                        if (ret < 0)
                                return ret;
                        continue;
                }
 -              if (!strncmp(arg, "-a", 2)) {
 +              if (!strncmp(arg, "-a=", 3)) {
                        int32_t val;
 -                      ret = para_atoi32(arg + 2, &val);
 +                      ret = para_atoi32(arg + 3, &val);
                        if (ret < 0)
                                return ret;
                        if (val < 0 || val > 255)
        if (i >= argc)
                return -E_AFT_SYNTAX;
        ret = send_option_arg_callback_request(&query, argc - i,
 -              argv + i, com_touch_callback, send_result, &fd);
 +              argv + i, com_touch_callback, rc4_send_result, rc4c);
        if (ret < 0)
 -              send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
@@@ -2190,7 -2220,7 +2190,7 @@@ static int remove_audio_file(__a_unuse
                        return ret;
        }
        afs_event(AUDIO_FILE_REMOVE, &crd->pb, row);
 -      ret = osl_del_row(audio_file_table, row);
 +      ret = osl(osl_del_row(audio_file_table, row));
        if (ret < 0)
                para_printf(&crd->pb, "%s: %s\n", name, para_strerror(-ret));
        else
@@@ -2236,7 -2266,7 +2236,7 @@@ static void com_rm_callback(int fd, con
  }
  
  /* TODO options: -r (recursive) */
 -int com_rm(int fd, int argc,  char * const * const argv)
 +int com_rm(struct rc4_context *rc4c, int argc,  char * const * const argv)
  {
        uint32_t flags = 0;
        struct osl_object query = {.data = &flags, .size = sizeof(flags)};
        if (i >= argc)
                return -E_AFT_SYNTAX;
        ret = send_option_arg_callback_request(&query, argc - i, argv + i,
 -              com_rm_callback, send_result, &fd);
 +              com_rm_callback, rc4_send_result, rc4c);
        if (ret < 0)
 -              send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
@@@ -2384,7 -2414,7 +2384,7 @@@ out
        free(cad.pb.buf);
  }
  
 -int com_cpsi(int fd, int argc,  char * const * const argv)
 +int com_cpsi(struct rc4_context *rc4c, int argc,  char * const * const argv)
  {
        unsigned flags = 0;
        int i, ret;
                }
                break;
        }
 -      if (i + 1 >= argc) /* need at least souce file and pattern */
 +      if (i + 1 >= argc) /* need at least source file and pattern */
                return -E_AFT_SYNTAX;
        if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
                flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
        ret = send_option_arg_callback_request(&options, argc - i, argv + i,
 -              com_cpsi_callback, send_result, &fd);
 +              com_cpsi_callback, rc4_send_result, rc4c);
        if (ret < 0)
 -              send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
 +void afs_stat_callback(int fd, const struct osl_object *query)
 +{
 +      int *parser_friendly = query->data;
 +      char *buf = *parser_friendly?
 +              parser_friendly_status_items : status_items;
 +
 +      if (!buf)
 +              return;
 +      pass_buffer_as_shm(buf, strlen(buf), &fd);
 +}
 +
 +int send_afs_status(struct rc4_context *rc4c, int parser_friendly)
 +{
 +      struct osl_object query = {.data = &parser_friendly,
 +              .size = sizeof(parser_friendly)};
 +
 +      return send_callback_request(afs_stat_callback, &query, rc4_send_result, rc4c);
 +}
 +
  /* TODO: optionally fix problems by removing offending rows */
  static int check_audio_file(struct osl_row *row, void *data)
  {
@@@ -2532,10 -2543,6 +2532,10 @@@ static void aft_close(void
  {
        osl_close_table(audio_file_table, OSL_MARK_CLEAN);
        audio_file_table = NULL;
 +      free(status_items);
 +      status_items = NULL;
 +      free(parser_friendly_status_items);
 +      parser_friendly_status_items = NULL;
  }
  
  /**
@@@ -2552,7 -2559,7 +2552,7 @@@ static int aft_open(const char *dir
        int ret;
  
        audio_file_table_desc.dir = dir;
 -      ret = osl_open_table(&audio_file_table_desc, &audio_file_table);
 +      ret = osl(osl_open_table(&audio_file_table_desc, &audio_file_table));
        if (ret >= 0) {
                unsigned num;
                osl_get_num_rows(audio_file_table, &num);
        }
        PARA_INFO_LOG("failed to open audio file table\n");
        audio_file_table = NULL;
 -      if (ret >= 0 || is_errno(-ret, ENOENT))
 +      if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
                return 1;
        return ret;
  }
  static int aft_create(const char *dir)
  {
        audio_file_table_desc.dir = dir;
 -      return osl_create_table(&audio_file_table_desc);
 +      return osl(osl_create_table(&audio_file_table_desc));
  }
  
  static int clear_attribute(struct osl_row *row, void *data)
@@@ -2612,7 -2619,6 +2612,6 @@@ static int aft_event_handler(enum afs_e
  
  void aft_init(struct afs_table *t)
  {
-       t->name = audio_file_table_desc.name;
        t->open = aft_open;
        t->close = aft_close;
        t->create = aft_create;
diff --combined attribute.c
@@@ -5,14 -5,8 +5,14 @@@
   */
  
  /** \file attribute.c Attribute handling functions. */
 +
 +#include <regex.h>
 +#include <openssl/rc4.h>
 +#include <osl.h>
 +
  #include "para.h"
  #include "error.h"
 +#include "crypt.h"
  #include "string.h"
  #include "afh.h"
  #include "afs.h"
@@@ -95,11 -89,11 +95,11 @@@ int get_attribute_bitnum_by_name(const 
        struct osl_object obj = {.data = (char *)att_name,
                .size = strlen(att_name) + 1};
        struct osl_row *row;
 -      int ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row);
 +      int ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
  
        if (ret < 0)
                return ret;
 -      ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj);
 +      ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM, &obj));
        if (ret < 0)
                return ret;
        *bitnum = *(unsigned char *)obj.data;
@@@ -137,7 -131,7 +137,7 @@@ static int print_attribute(struct osl_t
  
        if (!(laad->flags & LSATT_FLAG_LONG))
                return para_printf(&laad->pb, "%s\n", name);
 -      ret = osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj);
 +      ret = osl(osl_get_object(table, row, ATTCOL_BITNUM, &bitnum_obj));
        if (ret < 0) {
                para_printf(&laad->pb, "%s: %s\n", name, para_strerror(-ret));
                return ret;
@@@ -177,7 -171,7 +177,7 @@@ static void com_lsatt_callback(int fd, 
        free(laad.pb.buf);
  }
  
 -int com_lsatt(int fd, int argc, char * const * const argv)
 +int com_lsatt(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        unsigned flags = 0;
        struct osl_object options = {.data = &flags, .size = sizeof(flags)};
                }
        }
        ret = send_option_arg_callback_request(&options, argc - i, argv + i,
 -              com_lsatt_callback, send_result, &fd);
 +              com_lsatt_callback, rc4_send_result, rc4c);
        if (!ret) {
                if (argc > 1)
 -                      ret = send_va_buffer(fd, "no matches\n");
 +                      ret = rc4_send_va_buffer(rc4c, "no matches\n");
        } else if (ret < 0)
 -              send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
@@@ -236,11 -230,11 +236,11 @@@ static void com_setatt_callback(__a_unu
                p[len - 1] = '\0';
                obj.data = p;
                obj.size = len + 1;
 -              ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row);
 +              ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
                if (ret < 0)
                        goto out;
 -              ret = osl_get_object(attribute_table, row, ATTCOL_BITNUM,
 -                      &obj);
 +              ret = osl(osl_get_object(attribute_table, row, ATTCOL_BITNUM,
 +                      &obj));
                if (ret < 0)
                        goto out;
                if (c == '+')
@@@ -278,7 -272,7 +278,7 @@@ out
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
  }
  
 -int com_setatt(__a_unused int fd, int argc, char * const * const argv)
 +int com_setatt(__a_unused struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        if (argc < 3)
                return -E_ATTR_SYNTAX;
@@@ -323,15 -317,15 +323,15 @@@ static void com_addatt_callback(int fd
                                goto out;
                        continue;
                }
 -              if (ret != -E_RB_KEY_NOT_FOUND) /* error */
 +              if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */
                        goto out;
                objs[ATTCOL_BITNUM].size = 1;
                /* find smallest unused attribute */
                for (bitnum = 0; bitnum < 64; bitnum++) {
                        objs[ATTCOL_BITNUM].data = &bitnum;
 -                      ret = osl_get_row(attribute_table, ATTCOL_BITNUM,
 -                              &objs[ATTCOL_BITNUM], &row);
 -                      if (ret == -E_RB_KEY_NOT_FOUND)
 +                      ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM,
 +                              &objs[ATTCOL_BITNUM], &row));
 +                      if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
                                break; /* this bitnum is unused, use it */
                        if (ret < 0) /* error */
                                goto out;
                }
                objs[ATTCOL_NAME].data = p;
                objs[ATTCOL_NAME].size = len + 1;
 -              ret = osl_add_row(attribute_table, objs);
 +              ret = osl(osl_add_row(attribute_table, objs));
                if (ret < 0)
                        goto out;
                aed.name = p;
@@@ -359,16 -353,16 +359,16 @@@ out
        free(pb.buf);
  }
  
 -int com_addatt(int fd, int argc, char * const * const argv)
 +int com_addatt(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        int ret;
  
        if (argc < 2)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(argc - 1, argv + 1, com_addatt_callback,
 -              send_result, &fd);
 +              rc4_send_result, rc4c);
        if (ret < 0)
 -              send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
@@@ -386,12 -380,12 +386,12 @@@ static void com_mvatt_callback(int fd, 
        };
        int ret;
  
 -      ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row);
 +      ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
        if (ret < 0)
                goto out;
        obj.data = new;
        obj.size = strlen(new) + 1;
 -      ret = osl_update_object(attribute_table, row, ATTCOL_NAME, &obj);
 +      ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
  out:
        if (ret < 0)
                para_printf(&pb, "%s\n", para_strerror(-ret));
        free(pb.buf);
  }
  
 -int com_mvatt(int fd, int argc, char * const * const argv)
 +int com_mvatt(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        int ret;
  
        if (argc != 3)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(argc - 1, argv + 1, com_mvatt_callback,
 -              send_result, &fd);
 +              rc4_send_result, rc4c);
        if (ret < 0)
 -              send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
@@@ -435,7 -429,7 +435,7 @@@ static int remove_attribute(struct osl_
        ret = get_attribute_bitnum_by_name(name, &red.bitnum);
        if (ret < 0)
                return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
 -      ret = osl_del_row(table, row);
 +      ret = osl(osl_del_row(table, row));
        if (ret < 0)
                return para_printf(&raad->pb, "%s: %s\n", name, para_strerror(-ret));
        ret = para_printf(&raad->pb, "removed attribute %s\n", name);
@@@ -474,16 -468,16 +474,16 @@@ static void com_rmatt_callback(int fd, 
        free(raad.pb.buf);
  }
  
 -int com_rmatt(int fd, int argc, char * const * const argv)
 +int com_rmatt(struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        int ret;
  
        if (argc < 2)
                return -E_ATTR_SYNTAX;
        ret = send_standard_callback_request(argc - 1, argv + 1, com_rmatt_callback,
 -              send_result, &fd);
 +              rc4_send_result, rc4c);
        if (ret < 0)
 -              send_va_buffer(fd, "%s\n", para_strerror(-ret));
 +              rc4_send_va_buffer(rc4c, "%s\n", para_strerror(-ret));
        return ret;
  }
  
@@@ -539,10 -533,10 +539,10 @@@ int get_attribute_text(uint64_t *atts, 
  
                if (!(*atts & (one << i)))
                        continue;
 -              ret = osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row);
 +              ret = osl(osl_get_row(attribute_table, ATTCOL_BITNUM, &obj, &row));
                if (ret < 0)
                        goto err;
 -              ret = osl_get_object(attribute_table, row, ATTCOL_NAME, &obj);
 +              ret = osl(osl_get_object(attribute_table, row, ATTCOL_NAME, &obj));
                if (ret < 0)
                        goto err;
                if (*text) {
@@@ -585,14 -579,14 +585,14 @@@ static int attribute_open(const char *d
        int ret;
  
        attribute_table_desc.dir = dir;
 -      ret = osl_open_table(&attribute_table_desc, &attribute_table);
 +      ret = osl(osl_open_table(&attribute_table_desc, &attribute_table));
        greatest_att_bitnum = -1; /* no atts available */
        if (ret >= 0) {
                find_greatest_att_bitnum();
                return ret;
        }
        attribute_table = NULL;
 -      if (ret >= 0 || is_errno(-ret, ENOENT))
 +      if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
                return 1;
        return ret;
  }
  static int attribute_create(const char *dir)
  {
        attribute_table_desc.dir = dir;
 -      return osl_create_table(&attribute_table_desc);
 +      return osl(osl_create_table(&attribute_table_desc));
  }
  
  /**
   */
  void attribute_init(struct afs_table *t)
  {
-       t->name = attribute_table_desc.name;
        t->open = attribute_open;
        t->close = attribute_close;
        t->create = attribute_create;
diff --combined blob.c
--- 1/blob.c
--- 2/blob.c
+++ b/blob.c
@@@ -6,42 -6,14 +6,42 @@@
  
  /** \file blob.c Macros and functions for blob handling. */
  
 +#include <regex.h>
  #include <fnmatch.h>
 +#include <openssl/rc4.h>
 +#include <osl.h>
 +
  #include "para.h"
  #include "error.h"
 +#include "crypt.h"
  #include "string.h"
  #include "afh.h"
  #include "afs.h"
  #include "net.h"
  #include "ipc.h"
 +#include "portable_io.h"
 +
 +/**
 + * Compare two osl objects pointing to unsigned integers of 32 bit size.
 + *
 + * \param obj1 Pointer to the first integer.
 + * \param obj2 Pointer to the second integer.
 + *
 + * \return The values required for an osl compare function.
 + *
 + * \sa osl_compare_func, osl_hash_compare().
 + */
 +static int uint32_compare(const struct osl_object *obj1, const struct osl_object *obj2)
 +{
 +      uint32_t d1 = read_u32((const char *)obj1->data);
 +      uint32_t d2 = read_u32((const char *)obj2->data);
 +
 +      if (d1 < d2)
 +              return 1;
 +      if (d1 > d2)
 +              return -1;
 +      return 0;
 +}
  
  static struct osl_column_description blob_cols[] = {
        [BLOBCOL_ID] = {
@@@ -99,7 -71,7 +99,7 @@@ static int print_blob(struct osl_table 
  
        if (!(lbad->flags & BLOB_LS_FLAG_LONG))
                return para_printf(&lbad->pb, "%s\n", name);
 -      ret = osl_get_object(table, row, BLOBCOL_ID, &obj);
 +      ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
        if (ret < 0) {
                para_printf(&lbad->pb, "%s: %s\n", name, para_strerror(-ret));
                return ret;
@@@ -144,7 -116,7 +144,7 @@@ static void com_lsblob_callback(struct 
        free(lbad.pb.buf);
  }
  
 -static int com_lsblob(callback_function *f, int fd, int argc, char * const * const argv)
 +static int com_lsblob(callback_function *f, struct rc4_context *rc4c, int argc, char * const * const argv)
  {
        uint32_t flags = 0;
        struct osl_object options = {.data = &flags, .size = sizeof(flags)};
  //    if (argc > i)
  //            return -E_BLOB_SYNTAX;
        return send_option_arg_callback_request(&options, argc - i,
 -              argv + i, f, send_result, &fd);
 +              argv + i, f, rc4_send_result, rc4c);
  }
  
  static int cat_blob(struct osl_table *table, struct osl_row *row,
        int ret = 0, ret2;
        struct osl_object obj;
  
 -      ret = osl_open_disk_object(table, row, BLOBCOL_DEF, &obj);
 +      ret = osl(osl_open_disk_object(table, row, BLOBCOL_DEF, &obj));
        if (ret < 0)
                return ret;
        if (obj.size)
                ret = pass_buffer_as_shm(obj.data, obj.size, data);
 -      ret2 = osl_close_disk_object(&obj);
 +      ret2 = osl(osl_close_disk_object(&obj));
        return (ret < 0)? ret : ret2;
  }
  
@@@ -208,13 -180,12 +208,13 @@@ static void com_catblob_callback(struc
        for_each_matching_row(&pmd);
  }
  
 -static int com_catblob(callback_function *f, int fd, int argc,
 +static int com_catblob(callback_function *f, struct rc4_context *rc4c, int argc,
                char * const * const argv)
  {
        if (argc < 2)
                return -E_BLOB_SYNTAX;
 -      return send_standard_callback_request(argc - 1, argv + 1, f, send_result, &fd);
 +      return send_standard_callback_request(argc - 1, argv + 1, f,
 +              rc4_send_result, rc4c);
  }
  
  /** Used for removing rows from a blob table. */
@@@ -229,7 -200,7 +229,7 @@@ static int remove_blob(struct osl_tabl
                const char *name, void *data)
  {
        struct rmblob_data *rmbd = data;
 -      int ret = osl_del_row(table, row);
 +      int ret = osl(osl_del_row(table, row));
        if (ret < 0) {
                para_printf(&rmbd->pb, "%s: %s\n", name, para_strerror(-ret));
                return ret;
@@@ -277,13 -248,13 +277,13 @@@ out
        free(rmbd.pb.buf);
  }
  
 -static int com_rmblob(callback_function *f, int fd, int argc,
 +static int com_rmblob(callback_function *f, struct rc4_context *rc4c, int argc,
                char * const * const argv)
  {
        if (argc < 2)
                return -E_MOOD_SYNTAX;
        return send_option_arg_callback_request(NULL, argc - 1, argv + 1, f,
 -              send_result, &fd);
 +              rc4_send_result, rc4c);
  }
  
  static void com_addblob_callback(struct osl_table *table, __a_unused int fd,
        unsigned num_rows;
        int ret;
  
 -      ret = osl_get_num_rows(table, &num_rows);
 +      ret = osl(osl_get_num_rows(table, &num_rows));
        if (ret < 0)
                goto out;
        if (!num_rows) { /* this is the first entry ever added */
                objs[BLOBCOL_NAME].size = 1;
                objs[BLOBCOL_DEF].data = "";
                objs[BLOBCOL_DEF].size = 1;
 -              ret = osl_add_row(table, objs);
 +              ret = osl(osl_add_row(table, objs));
                if (ret < 0)
                        goto out;
        } else {
                /* check if name already exists */
                struct osl_row *row;
                struct osl_object obj = {.data = name, .size = name_len};
 -              ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
 -              if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND)
 +              ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 +              if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
                        goto out;
                if (ret >= 0) { /* we already have a blob with this name */
                        obj.data = name + name_len;
                        obj.size = query->size - name_len;
 -                      ret = osl_update_object(table, row, BLOBCOL_DEF, &obj);
 +                      ret = osl(osl_update_object(table, row, BLOBCOL_DEF, &obj));
                        goto out;
                }
                /* new blob, get id of the dummy row and increment it */
                obj.data = "";
                obj.size = 1;
 -              ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
 +              ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
                if (ret < 0)
                        goto out;
 -              ret = osl_get_object(table, row, BLOBCOL_ID, &obj);
 +              ret = osl(osl_get_object(table, row, BLOBCOL_ID, &obj));
                if (ret < 0)
                        goto out;
                id = *(uint32_t *)obj.data + 1;
                obj.data = &id;
 -              ret = osl_update_object(table, row, BLOBCOL_ID, &obj);
 +              ret = osl(osl_update_object(table, row, BLOBCOL_ID, &obj));
                if (ret < 0)
                        goto out;
        }
        objs[BLOBCOL_NAME].size = name_len;
        objs[BLOBCOL_DEF].data = name + name_len;
        objs[BLOBCOL_DEF].size = query->size - name_len;
 -      ret = osl_add_row(table, objs);
 +      ret = osl(osl_add_row(table, objs));
        if (ret < 0)
                goto out;
        afs_event(BLOB_ADD, NULL, table);
@@@ -355,82 -326,7 +355,82 @@@ out
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
  }
  
 -static int com_addblob(callback_function *f, int fd, int argc,
 +/*
 + * write input from fd to dynamically allocated buffer,
 + * but maximal max_size byte.
 + */
 +static int fd2buf(struct rc4_context *rc4c, unsigned max_size, struct osl_object *obj)
 +{
 +      const size_t chunk_size = 1024;
 +      size_t size = 2048, received = 0;
 +      int ret;
 +      char *buf = para_malloc(size);
 +
 +      for (;;) {
 +              ret = rc4_recv_bin_buffer(rc4c, buf + received, chunk_size);
 +              if (ret <= 0)
 +                      break;
 +              received += ret;
 +              if (received + chunk_size >= size) {
 +                      size *= 2;
 +                      ret = -E_INPUT_TOO_LARGE;
 +                      if (size > max_size)
 +                              break;
 +                      buf = para_realloc(buf, size);
 +              }
 +      }
 +      obj->data = buf;
 +      obj->size = received;
 +      if (ret < 0)
 +              free(buf);
 +      return ret;
 +}
 +
 +/*
 + * Read data from a file descriptor, and send it to the afs process.
 + *
 + * \param rc4c crypt context containing the file descriptor to read data from.
 + * \param arg_obj Pointer to the arguments to \a f.
 + * \param f The callback function.
 + * \param max_len Don't read more than that many bytes from stdin.
 + * \param result_handler See \ref send_callback_request.
 + * \param private_result_data See \ref send_callback_request.
 + *
 + * This function is used by commands that wish to let para_server store
 + * arbitrary data specified by the user (for instance the add_blob family of
 + * commands). First, at most \a max_len bytes are read and decrypted from the
 + * file descriptor given by \a rc4c. The result is concatenated with the buffer
 + * given by \a arg_obj, and the combined buffer is made available to the afs
 + * process via the callback method. See \ref send_callback_request for details.
 + *
 + * \return Negative on errors, the return value of the underlying call to
 + * send_callback_request() otherwise.
 + */
 +static int stdin_command(struct rc4_context *rc4c, struct osl_object *arg_obj,
 +              callback_function *f, unsigned max_len,
 +              callback_result_handler *result_handler,
 +              void *private_result_data)
 +{
 +      struct osl_object query, stdin_obj;
 +      int ret;
 +
 +      ret = rc4_send_buffer(rc4c, AWAITING_DATA_MSG);
 +      if (ret < 0)
 +              return ret;
 +      ret = fd2buf(rc4c, max_len, &stdin_obj);
 +      if (ret < 0)
 +              return ret;
 +      query.size = arg_obj->size + stdin_obj.size;
 +      query.data = para_malloc(query.size);
 +      memcpy(query.data, arg_obj->data, arg_obj->size);
 +      memcpy((char *)query.data + arg_obj->size, stdin_obj.data, stdin_obj.size);
 +      free(stdin_obj.data);
 +      ret = send_callback_request(f, &query, result_handler, private_result_data);
 +      free(query.data);
 +      return ret;
 +}
 +
 +static int com_addblob(callback_function *f, struct rc4_context *rc4c, int argc,
                char * const * const argv)
  {
        struct osl_object arg_obj;
                return -E_BLOB_SYNTAX;
        arg_obj.size = strlen(argv[1]) + 1;
        arg_obj.data = (char *)argv[1];
 -      return stdin_command(fd, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL);
 +      return stdin_command(rc4c, &arg_obj, f, 10 * 1024 * 1024, NULL, NULL);
  }
  
  /* FIXME: Print output to client, not to log file */
@@@ -452,13 -348,13 +452,13 @@@ static void com_mvblob_callback(struct 
        struct osl_object obj = {.data = src, .size = strlen(src) + 1};
        char *dest = src + obj.size;
        struct osl_row *row;
 -      int ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
 +      int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
  
        if (ret < 0)
                goto out;
        obj.data = dest;
        obj.size = strlen(dest) + 1;
 -      ret = osl_update_object(table, row, BLOBCOL_NAME, &obj);
 +      ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0)
                goto out;
        afs_event(BLOB_RENAME, NULL, table);
@@@ -467,7 -363,7 +467,7 @@@ out
                PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
  }
  
 -static int com_mvblob(callback_function *f, __a_unused int fd,
 +static int com_mvblob(callback_function *f, __a_unused struct rc4_context *rc4c,
                int argc, char * const * const argv)
  {
        if (argc != 3)
        { \
                return com_ ## cmd_name ## blob_callback(table_name ## _table, fd, query); \
        } \
 -      int com_ ## cmd_name ## cmd_prefix(int fd, int argc, char * const * const argv) \
 +      int com_ ## cmd_name ## cmd_prefix(struct rc4_context *rc4c, int argc, char * const * const argv) \
        { \
 -              return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, fd, argc, argv); \
 +              return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, rc4c, argc, argv); \
        }
  
  static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
        *name = NULL;
        if (!id)
                return 1;
 -      ret = osl_get_row(table, BLOBCOL_ID, &obj, &row);
 +      ret = osl(osl_get_row(table, BLOBCOL_ID, &obj, &row));
        if (ret < 0)
                return ret;
 -      ret = osl_get_object(table, row, BLOBCOL_NAME, &obj);
 +      ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0)
                return ret;
        *name = (char *)obj.data;
@@@ -524,10 -420,10 +524,10 @@@ static int blob_get_def_by_name(struct 
        def->data = NULL;
        if (!*name)
                return 1;
 -      ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
 +      ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
        if (ret < 0)
                return ret;
 -      return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
 +      return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def));
  }
  
  /** Define the \p get_def_by_id function for this blob type. */
@@@ -547,10 -443,10 +547,10 @@@ static int blob_get_def_by_id(struct os
        def->data = NULL;
        if (!id)
                return 1;
 -      ret = osl_get_row(table, BLOBCOL_ID, &obj, &row);
 +      ret = osl(osl_get_row(table, BLOBCOL_ID, &obj, &row));
        if (ret < 0)
                return ret;
 -      return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
 +      return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def));
  }
  
  /** Define the \p get_def_by_id function for this blob type. */
@@@ -564,11 -460,11 +564,11 @@@ static int blob_get_name_and_def_by_row
                const struct osl_row *row, char **name, struct osl_object *def)
  {
        struct osl_object obj;
 -      int ret = osl_get_object(table, row, BLOBCOL_NAME, &obj);
 +      int ret = osl(osl_get_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0)
                return ret;
        *name = obj.data;
 -      return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
 +      return osl(osl_open_disk_object(table, row, BLOBCOL_DEF, def));
  }
  /** Define the \p get_name_and_def_by_row function for this blob type. */
  #define DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix) \
@@@ -601,11 -497,11 +601,11 @@@ static int blob_open(struct osl_table *
  {
        int ret;
        desc->dir = dir;
 -      ret = osl_open_table(desc, table);
 +      ret = osl(osl_open_table(desc, table));
        if (ret >= 0)
                return ret;
        *table = NULL;
 -      if (ret >= 0 || is_errno(-ret, ENOENT))
 +      if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
                return 1;
        return ret;
  }
  #define DEFINE_BLOB_INIT(table_name) \
        void table_name ## _init(struct afs_table *t) \
        { \
-               t->name = table_name ## _table_desc.name; \
                t->open = table_name ## _open; \
                t->close = table_name ## _close; \
                t->create = table_name ## _create;\