]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 't/utf8'
authorAndre Noll <maan@systemlinux.org>
Tue, 14 May 2013 20:01:21 +0000 (22:01 +0200)
committerAndre Noll <maan@systemlinux.org>
Tue, 14 May 2013 20:04:07 +0000 (22:04 +0200)
Was cooking since 2013-03-10.

a946af string.c: Fix typo in documentation of skip_cells().
251394 Switch to HTF-8 for web pages.
09c6bc gui: Try to link against libncursesw.
36f38a UTF-8 support for para_gui.
a99804 mp3_afh: Switch to UTF-8 encoding.
f78b53 gui: Don't compute string length unnecessarily.
6224f7 Get rid of unused HAVE_NCURSES define.
6cd788 Fix --with-curses configure option.

1  2 
NEWS
configure.ac
gui.c
mp3_afh.c
string.c
string.h

diff --combined NEWS
index f323df1dda766dadfdc7fe559e6a39fa42824d58,f323df1dda766dadfdc7fe559e6a39fa42824d58..591d935d63741cc663c7064fd7903c0d1856fd9d
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -2,6 -2,6 +2,8 @@@
  0.?.? (to be announced) "spectral gravity"
  ------------------------------------------
  
++      - UTF8 support for para_gui and the mp3 audio format handler.
++
  -----------------------------------------
  0.4.12 (2012-12-20) "volatile relativity"
  -----------------------------------------
diff --combined configure.ac
index 4512c6fd38f7209aad203c14cfe31dc66d4e1d56,e1fa8116cd830648363150415d5358d1f8fe1942..de9ce055b3a32fb0d3cc5f3a79aaa8c66dff32f7
@@@ -289,7 -289,7 +289,7 @@@ els
                acl udp_send"
  
        server_ldflags=""
 -      server_audio_formats="mp3 wma"
 +      audio_format_handlers="mp3 wma"
        AC_SUBST(osl_cppflags)
        server_ldflags="$server_ldflags $osl_libs -losl"
  fi
  AC_CHECK_HEADER(curses.h, [], [
        have_curses="no"
  ])
- AC_CHECK_LIB([curses], [initscr], [], [
-       have_curses="no"
- ])
+ gui_ldflags="$curses_libs"
+ AC_CHECK_LIB([ncursesw], [initscr],
+       [gui_ldflags="$curses_libs -lncursesw"], [
+               AC_CHECK_LIB([curses], [initscr],
+                       [gui_ldflags="$curses_libs -lcurses"],
+                       [have_curses="no"]
+               )
+       ]
+ )
  if test "$have_curses" = "yes"; then
        AC_SUBST(curses_cppflags)
-       AC_DEFINE(HAVE_NCURSES, 1, [define to 1 to turn on curses support])
        extras="$extras gui"
        executables="$executables gui"
  else
@@@ -637,7 -642,7 +642,7 @@@ if test "$have_vorbis" = "yes"; the
        recv_errlist_objs="$recv_errlist_objs ogg_afh"
  
        audiod_audio_formats="$audiod_audio_formats ogg"
 -      server_audio_formats="$server_audio_formats ogg"
 +      audio_format_handlers="$audio_format_handlers ogg"
  else
        AC_MSG_WARN([no ogg/vorbis $msg])
  fi
@@@ -661,7 -666,7 +666,7 @@@ if test "$have_speex" = "yes"; the
        recv_errlist_objs="$recv_errlist_objs spx_afh spx_common"
  
        audiod_audio_formats="$audiod_audio_formats spx"
 -      server_audio_formats="$server_audio_formats spx"
 +      audio_format_handlers="$audio_format_handlers spx"
  else
        AC_MSG_WARN([no ogg/speex $msg])
  fi
@@@ -705,7 -710,7 +710,7 @@@ if test "$have_faad" = "yes"; the
        recv_ldflags="$afh_ldflags $faad_libs -lfaad"
  
        audiod_audio_formats="$audiod_audio_formats aac"
 -      server_audio_formats="$server_audio_formats aac"
 +      audio_format_handlers="$audio_format_handlers aac"
        filters="$filters aacdec"
        AC_SUBST(faad_cppflags)
  else
@@@ -836,7 -841,7 +841,7 @@@ if test "$have_flac" = "yes"; the
        afh_ldflags="$afh_ldflags $flac_libs -lFLAC"
        recv_ldflags="$afh_ldflags $flac_libs -lFLAC"
        filters="$filters flacdec"
 -      server_audio_formats="$server_audio_formats flac"
 +      audio_format_handlers="$audio_format_handlers flac"
        audiod_audio_formats="$audiod_audio_formats flac"
        AC_SUBST(flac_cppflags)
  else
@@@ -1186,7 -1191,7 +1191,7 @@@ don
  AC_DEFINE_UNQUOTED(STATUS_ITEM_ARRAY, [$result],
        [char * array of all status items])
  
 -AC_DEFINE_UNQUOTED(SERVER_AUDIO_FORMATS, "$server_audio_formats",
 +AC_DEFINE_UNQUOTED(AUDIO_FORMAT_HANDLERS, "$audio_format_handlers",
        [formats supported by para_server and para_afh])
  
  AC_SUBST(executables, add_para($executables))
@@@ -1243,6 -1248,7 +1248,7 @@@ AC_DEFINE_UNQUOTED(INIT_AUDIOC_ERRLISTS
        objlist_to_errlist($audioc_errlist_objs), errors used by para_audioc)
  
  AC_SUBST(gui_objs, add_dot_o($gui_objs))
+ AC_SUBST(gui_ldflags, $gui_ldflags)
  AC_DEFINE_UNQUOTED(INIT_GUI_ERRLISTS,
        objlist_to_errlist($gui_errlist_objs), errors used by para_gui)
  
@@@ -1284,10 -1290,10 +1290,10 @@@ paraslash configuration
  ~~~~~~~~~~~~~~~~~~~~~~~~
  unix socket credentials: $have_ucred
  readline (interactive CLIs): $have_readline
 -audio formats supported by para_server/para_afh: $server_audio_formats
 +audio formats handlers: $audio_format_handlers
  id3 version2 support: $have_libid3tag
 -filters supported by para_audiod/para_filter: $filters
 -writers supported by para_audiod/para_write: $writers
 +filters: $filters
 +writers: $writers
  optional executables: $extras
  $mixer_summary
  ])
diff --combined gui.c
index a4ee72758d6944cbd28aa8476049268ebb762256,ac9ee19ffe91df4f865dbe80f1b7ac38aa192a91..631e7cc54113a6e03229f144c4a6a04feba7950d
--- 1/gui.c
--- 2/gui.c
+++ b/gui.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 1998-2012 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 1998-2013 Andre Noll <maan@systemlinux.org>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
@@@ -10,6 -10,7 +10,7 @@@
  #include <signal.h>
  #include <sys/types.h>
  #include <curses.h>
+ #include <locale.h>
  
  #include "gui.cmdline.h"
  #include "para.h"
@@@ -305,11 -306,18 +306,18 @@@ static void add_spaces(WINDOW* win, uns
  static int align_str(WINDOW* win, char *str, unsigned int len,
                unsigned int align)
  {
-       int i, num; /* of spaces */
+       int ret, i, num; /* of spaces */
+       size_t width;
  
        if (!win || !str)
-               return -1;
-       num = len - strlen(str);
+               return 0;
+       ret = strwidth(str, &width);
+       if (ret < 0) {
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+               width = 0;
+               str[0] = '\0';
+       }
+       num = len - width;
        if (num < 0) {
                str[len] = '\0';
                num = 0;
@@@ -391,11 -399,14 +399,14 @@@ static int first_visible_rbe(unsigned *
        return RINGBUFFER_SIZE - 1;
  }
  
+ /*
+ returns number of first visible rbe, *lines is the number of lines drawn.
+  */
  static int draw_top_rbe(unsigned *lines)
  {
-       unsigned len;
-       int offset, fvr = first_visible_rbe(lines);
+       int ret, fvr = first_visible_rbe(lines);
        struct rb_entry *rbe;
+       size_t bytes_to_skip, cells_to_skip, width;
  
        if (fvr < 0)
                return -1;
        rbe = ringbuffer_get(bot_win_rb, fvr);
        if (!rbe)
                return -1;
-       len = strlen(rbe->msg);
        if (*lines > bot.lines) {
-               /* first rbe is only partially visible */
-               offset = (*lines - bot.lines) * bot.cols;
-               assert(offset <= len);
-       } else
-               offset = 0;
+               /* rbe is partially visible multi-line */
+               cells_to_skip = (*lines - bot.lines) * bot.cols;
+               ret = skip_cells(rbe->msg, cells_to_skip, &bytes_to_skip);
+               if (ret < 0)
+                       return ret;
+               ret = strwidth(rbe->msg + bytes_to_skip, &width);
+               if (ret < 0)
+                       return ret;
+       } else {
+               bytes_to_skip = 0;
+               width = rbe->len;
+       }
        wattron(bot.win, COLOR_PAIR(rbe->color));
-       waddstr(bot.win, rbe->msg + offset);
-       *lines = NUM_LINES(len - offset);
+       waddstr(bot.win, rbe->msg + bytes_to_skip);
+       *lines = NUM_LINES(width);
        return fvr;
  }
  
@@@ -444,10 -461,15 +461,15 @@@ out
  
  static void rb_add_entry(int color, char *msg)
  {
-       struct rb_entry *old, *new = para_malloc(sizeof(struct rb_entry));
+       struct rb_entry *old, *new;
        int x, y;
+       size_t len;
+       if (strwidth(msg, &len) < 0)
+               return;
+       new = para_malloc(sizeof(struct rb_entry));
        new->color = color;
-       new->len = strlen(msg);
+       new->len = len;
        new->msg = msg;
        old = ringbuffer_add(bot_win_rb, new);
  //    fprintf(stderr, "added: %s\n", new->msg);
@@@ -1162,7 -1184,7 +1184,7 @@@ static void com_scroll_top(void
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                if (!rbe)
                        break;
-               lines += NUM_LINES(strlen(rbe->msg));
+               lines += NUM_LINES(rbe->len);
        }
        i++;
        if (lines > 0 && scroll_position != i) {
@@@ -1193,7 -1215,7 +1215,7 @@@ static void com_page_down(void
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                if (!rbe)
                        break;
-               lines += NUM_LINES(strlen(rbe->msg));
+               lines += NUM_LINES(rbe->len);
        }
        if (lines) {
                scroll_position = i;
@@@ -1514,6 -1536,7 +1536,7 @@@ int main(int argc, char *argv[]
        top.lines = theme.top_lines_default;
        setup_signal_handling();
        bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
+       setlocale(LC_CTYPE, "");
        initscr(); /* needed only once, always successful */
        init_curses();
        print_welcome();
diff --combined mp3_afh.c
index 647098210198225cd9df307c2bc969b7532bc69e,15ee27fe8873416d6f2813d8cabbf1dded2b20a2..ece13e1f925e857cc43acc9437b9b7547fa621f8
+++ b/mp3_afh.c
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 2003-2012 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 2003-2013 Andre Noll <maan@systemlinux.org>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
@@@ -72,11 -72,11 +72,11 @@@ static const char *mode_text[] = {"ster
  
  #include <id3tag.h>
  
- static char *get_latin1(id3_ucs4_t const *string)
+ static char *get_utf8(id3_ucs4_t const *string)
  {
        if (!string)
                return NULL;
-       return (char *)id3_ucs4_latin1duplicate(string);
+       return (char *)id3_ucs4_utf8duplicate(string);
  }
  
  static char *get_stringlist(union id3_field *field)
@@@ -85,7 -85,7 +85,7 @@@
        char *result = NULL;
  
        for (k = 0; k < nstrings; k++) {
-               char *tmp = (char *)get_latin1(id3_field_getstrings(field, k));
+               char *tmp = (char *)get_utf8(id3_field_getstrings(field, k));
                if (result) {
                        char *tmp2 = result;
                        result = make_message("%s %s", tmp2, tmp);
@@@ -101,7 -101,7 +101,7 @@@ static char *get_string(union id3_fiel
  {
        id3_ucs4_t const *string = id3_field_getfullstring(field);
  
-       return get_latin1(string);
+       return get_utf8(string);
  }
  
  #define FOR_EACH_FIELD(f, j, fr) for (j = 0; j < (fr)->nfields && \
diff --combined string.c
index c001b15d7dc8eca43d50af650a3dadf1693ed7ab,b844fb381dc25eb50c47fa04c1c60a9d91f6ff0c..a416dc80cce868563ef82ae6647c9a07651ec4ea
+++ b/string.c
@@@ -1,17 -1,24 +1,24 @@@
  /*
 - * Copyright (C) 2004-2012 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 2004-2013 Andre Noll <maan@systemlinux.org>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
  
  /** \file string.c Memory allocation and string handling functions. */
  
+ #define _GNU_SOURCE
  #include <sys/time.h> /* gettimeofday */
  #include <pwd.h>
  #include <sys/utsname.h> /* uname() */
  #include <string.h>
  #include <regex.h>
  
+ #include <langinfo.h>
+ #include <wchar.h>
+ #include <wctype.h>
  #include "para.h"
  #include "string.h"
  #include "error.h"
@@@ -982,3 -989,145 +989,145 @@@ char *key_value_copy(const char *src, s
                return NULL;
        return safe_strdup(src + keylen + 1, len - keylen - 1);
  }
+ static bool utf8_mode(void)
+ {
+       static bool initialized, have_utf8;
+       if (!initialized) {
+               char *info = nl_langinfo(CODESET);
+               have_utf8 = (info && strcmp(info, "UTF-8") == 0);
+               initialized = true;
+               PARA_INFO_LOG("%susing UTF-8 character encoding\n",
+                       have_utf8? "" : "not ");
+       }
+       return have_utf8;
+ }
+ /*
+  * glibc's wcswidth returns -1 if the string contains a tab character, which
+  * makes the function next to useless. The two functions below are taken from
+  * mutt.
+  */
+ #define IsWPrint(wc) (iswprint(wc) || wc >= 0xa0)
+ static int mutt_wcwidth(wchar_t wc, size_t pos)
+ {
+       int n;
+       if (wc == 0x09) /* tab */
+               return (pos | 7) + 1 - pos;
+       n = wcwidth(wc);
+       if (IsWPrint(wc) && n > 0)
+               return n;
+       if (!(wc & ~0x7f))
+               return 2;
+       if (!(wc & ~0xffff))
+               return 6;
+       return 10;
+ }
+ static size_t mutt_wcswidth(const wchar_t *s, size_t n)
+ {
+       size_t w = 0;
+       while (n--)
+               w += mutt_wcwidth(*s++, w);
+       return w;
+ }
+ /**
+  * Skip a given number of cells at the beginning of a string.
+  *
+  * \param s The input string.
+  * \param cells_to_skip Desired number of cells that should be skipped.
+  * \param bytes_to_skip Result.
+  *
+  * This function computes how many input bytes must be skipped to advance a
+  * string by the given width. If the current character encoding is not UTF-8,
+  * this is simply the given number of cells, i.e. \a cells_to_skip. Otherwise,
+  * \a s is treated as a multibyte string and on successful return, \a s +
+  * bytes_to_skip points to the start of a multibyte string such that the total
+  * width of the multibyte characters that are skipped by advancing \a s that
+  * many bytes equals at least \a cells_to_skip.
+  *
+  * \return Standard.
+  */
+ int skip_cells(const char *s, size_t cells_to_skip, size_t *bytes_to_skip)
+ {
+       wchar_t wc;
+       mbstate_t ps;
+       size_t n, bytes_parsed, cells_skipped;
+       *bytes_to_skip = 0;
+       if (cells_to_skip == 0)
+               return 0;
+       if (!utf8_mode()) {
+               *bytes_to_skip = cells_to_skip;
+               return 0;
+       }
+       bytes_parsed = cells_skipped = 0;
+       memset(&ps, 0, sizeof(ps));
+       n = strlen(s);
+       while (cells_to_skip > cells_skipped) {
+               size_t mbret;
+               mbret = mbrtowc(&wc, s + bytes_parsed, n - bytes_parsed, &ps);
+               assert(mbret != 0);
+               if (mbret == (size_t)-1 || mbret == (size_t)-2)
+                       return -ERRNO_TO_PARA_ERROR(EILSEQ);
+               bytes_parsed += mbret;
+               cells_skipped += mutt_wcwidth(wc, cells_skipped);
+       }
+       *bytes_to_skip = bytes_parsed;
+       return 1;
+ }
+ /**
+  * Compute the width of an UTF-8 string.
+  *
+  * \param s The string.
+  * \param result The width of \a s is returned here.
+  *
+  * If not in UTF8-mode. this function is just a wrapper for strlen(3).
+  * Otherwise \a s is treated as an UTF-8 string and its display width is
+  * computed. Note that this function may fail if the underlying call to
+  * mbsrtowcs(3) fails, so the caller must check the return value.
+  *
+  * \sa nl_langinfo(3), wcswidth(3).
+  *
+  * \return Standard.
+  */
+ __must_check int strwidth(const char *s, size_t *result)
+ {
+       const char *src = s;
+       mbstate_t state;
+       static wchar_t *dest;
+       size_t num_wchars;
+       /*
+        * Never call any log function here. This may result in an endless loop
+        * as para_gui's para_log() calls this function.
+        */
+       if (!utf8_mode()) {
+               *result = strlen(s);
+               return 0;
+       }
+       memset(&state, 0, sizeof(state));
+       *result = 0;
+       num_wchars = mbsrtowcs(NULL, &src, 0, &state);
+       if (num_wchars == (size_t)-1)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       if (num_wchars == 0)
+               return 0;
+       dest = para_malloc(num_wchars * sizeof(*dest));
+       src = s;
+       memset(&state, 0, sizeof(state));
+       num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
+       assert(num_wchars > 0 && num_wchars != (size_t)-1);
+       *result = mutt_wcswidth(dest, num_wchars);
+       free(dest);
+       return 1;
+ }
diff --combined string.h
index d4627f33f0d3ce07885deff483c847e57df5b32a,fc55451a32e56b7ff7cde9d402c6ac8b682df9c6..d45c9199822d993ab2594877a6cd45914a902f15
+++ b/string.h
@@@ -1,5 -1,5 +1,5 @@@
  /*
 - * Copyright (C) 2006-2012 Andre Noll <maan@systemlinux.org>
 + * Copyright (C) 2006-2013 Andre Noll <maan@systemlinux.org>
   *
   * Licensed under the GPL v2. For licencing details see COPYING.
   */
@@@ -91,3 -91,5 +91,5 @@@ 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);
+ int skip_cells(const char *s, size_t cells_to_skip, size_t *result);
+ __must_check int strwidth(const char *s, size_t *result);