From 93a49b82538b47d287c6e7024b43e5ebc5754c01 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Fri, 16 May 2014 23:16:46 +0200 Subject: [PATCH 1/6] rename uuid_t to tvh_uuid_t --- src/config.c | 8 ++++---- src/idnode.c | 4 ++-- src/input/mpegts/linuxdvb/linuxdvb_adapter.c | 2 +- src/input/mpegts/satip/satip.c | 4 ++-- src/uuid.c | 16 ++++++++-------- src/uuid.h | 10 +++++----- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/config.c b/src/config.c index 87dd43f6..f7e97bd3 100644 --- a/src/config.c +++ b/src/config.c @@ -89,7 +89,7 @@ static void config_migrate_v1_dvb_svcs ( const char *name, const char *netu, const char *muxu, htsmsg_t *channels ) { - uuid_t svcu; + tvh_uuid_t svcu; htsmsg_t *c, *e, *svc; htsmsg_field_t *f; const char *str; @@ -139,7 +139,7 @@ config_migrate_v1_dvb_network ( const char *name, htsmsg_t *c, htsmsg_t *channels ) { int i; - uuid_t netu, muxu; + tvh_uuid_t netu, muxu; htsmsg_t *e, *net, *mux, *tun; htsmsg_field_t *f; const char *str, *type; @@ -321,7 +321,7 @@ config_migrate_v1_epggrab ( const char *path, htsmsg_t *channels ) static void config_migrate_v1 ( void ) { - uuid_t netu, muxu, svcu, chnu; + tvh_uuid_t netu, muxu, svcu, chnu; htsmsg_t *c, *m, *e, *l; htsmsg_field_t *f; uint32_t u32; @@ -450,7 +450,7 @@ static void config_migrate_v2 ( void ) { htsmsg_t *m; - uuid_t u; + tvh_uuid_t u; char src[1024], dst[1024]; /* Do we have IPTV config to migrate ? */ diff --git a/src/idnode.c b/src/idnode.c index e3ea6aba..00ff1c5c 100644 --- a/src/idnode.c +++ b/src/idnode.c @@ -102,7 +102,7 @@ idnode_insert(idnode_t *in, const char *uuid, const idclass_t *class) { idnode_t *c; lock_assert(&global_lock); - uuid_t u; + tvh_uuid_t u; if (uuid_init_bin(&u, uuid)) return -1; memcpy(in->in_uuid, u.bin, sizeof(in->in_uuid)); @@ -192,7 +192,7 @@ idnode_get_short_uuid (const idnode_t *in) const char * idnode_uuid_as_str(const idnode_t *in) { - static uuid_t ret[16]; + static tvh_uuid_t ret[16]; static uint8_t p = 0; bin2hex(ret[p].hex, sizeof(ret[p].hex), in->in_uuid, sizeof(in->in_uuid)); const char *s = ret[p].hex; diff --git a/src/input/mpegts/linuxdvb/linuxdvb_adapter.c b/src/input/mpegts/linuxdvb/linuxdvb_adapter.c index 1ee9ba8e..2430b40c 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_adapter.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_adapter.c @@ -187,7 +187,7 @@ linuxdvb_adapter_add ( const char *path ) extern int linuxdvb_adapter_mask; int a, i, j, r, fd; char fe_path[512], dmx_path[512], dvr_path[512]; - uuid_t uuid; + tvh_uuid_t uuid; linuxdvb_adapter_t *la = NULL; struct dvb_frontend_info dfi; SHA_CTX sha1; diff --git a/src/input/mpegts/satip/satip.c b/src/input/mpegts/satip/satip.c index d1c88b01..f87dca4c 100644 --- a/src/input/mpegts/satip/satip.c +++ b/src/input/mpegts/satip/satip.c @@ -256,7 +256,7 @@ satip_device_calc_bin_uuid( uint8_t *uuid, const char *satip_uuid ) } static void -satip_device_calc_uuid( uuid_t *uuid, const char *satip_uuid ) +satip_device_calc_uuid( tvh_uuid_t *uuid, const char *satip_uuid ) { uint8_t uuidbin[20]; @@ -287,7 +287,7 @@ static satip_device_t * satip_device_create( satip_device_info_t *info ) { satip_device_t *sd = calloc(1, sizeof(satip_device_t)); - uuid_t uuid; + tvh_uuid_t uuid; htsmsg_t *conf = NULL, *feconf = NULL; char *argv[10]; int i, j, n, m, fenum, t2, save = 0; diff --git a/src/uuid.c b/src/uuid.c index 54f343da..1fd6a540 100644 --- a/src/uuid.c +++ b/src/uuid.c @@ -103,9 +103,9 @@ uuid_init ( void ) /* Initialise binary */ int -uuid_init_bin ( uuid_t *u, const char *str ) +uuid_init_bin ( tvh_uuid_t *u, const char *str ) { - memset(u, 0, sizeof(uuid_t)); + memset(u, 0, sizeof(tvh_uuid_t)); if (str) { return hex2bin(u->bin, sizeof(u->bin), str); } else if (read(fd, u->bin, sizeof(u->bin)) != sizeof(u->bin)) { @@ -117,9 +117,9 @@ uuid_init_bin ( uuid_t *u, const char *str ) /* Initialise hex string */ int -uuid_init_hex ( uuid_t *u, const char *str ) +uuid_init_hex ( tvh_uuid_t *u, const char *str ) { - uuid_t tmp; + tvh_uuid_t tmp; if (uuid_init_bin(&tmp, str)) return 1; return uuid_bin2hex(&tmp, u); @@ -127,9 +127,9 @@ uuid_init_hex ( uuid_t *u, const char *str ) /* Convert bin to hex string */ int -uuid_bin2hex ( const uuid_t *a, uuid_t *b ) +uuid_bin2hex ( const tvh_uuid_t *a, tvh_uuid_t *b ) { - uuid_t tmp; + tvh_uuid_t tmp; memset(&tmp, 0, sizeof(tmp)); bin2hex(tmp.hex, sizeof(tmp.hex), a->bin, sizeof(a->bin)); memcpy(b, &tmp, sizeof(tmp)); @@ -138,9 +138,9 @@ uuid_bin2hex ( const uuid_t *a, uuid_t *b ) /* Convert hex string to bin (in place) */ int -uuid_hex2bin ( const uuid_t *a, uuid_t *b ) +uuid_hex2bin ( const tvh_uuid_t *a, tvh_uuid_t *b ) { - uuid_t tmp; + tvh_uuid_t tmp; memset(&tmp, 0, sizeof(tmp)); if (hex2bin(tmp.bin, sizeof(tmp.bin), a->hex)) return 1; diff --git a/src/uuid.h b/src/uuid.h index 81c09757..45269d21 100644 --- a/src/uuid.h +++ b/src/uuid.h @@ -31,30 +31,30 @@ typedef struct uuid { uint8_t bin[UUID_BIN_SIZE]; char hex[UUID_HEX_SIZE]; }; -} uuid_t; +} tvh_uuid_t; /* Initialise subsystem */ void uuid_init ( void ); /* Initialise binary */ -int uuid_init_bin ( uuid_t *u, const char *str ); +int uuid_init_bin ( tvh_uuid_t *u, const char *str ); /* Initialise hex string */ -int uuid_init_hex ( uuid_t *u, const char *str ); +int uuid_init_hex ( tvh_uuid_t *u, const char *str ); /** * Convert bin to hex string * * Note: conversion is done such that a and b can be the same */ -int uuid_bin2hex ( const uuid_t *a, uuid_t *b ); +int uuid_bin2hex ( const tvh_uuid_t *a, tvh_uuid_t *b ); /** * Convert hex string to bin (in place) * * Note: conversion is done such that a and b can be the same */ -int uuid_hex2bin ( const uuid_t *a, uuid_t *b ); +int uuid_hex2bin ( const tvh_uuid_t *a, tvh_uuid_t *b ); /** * Hex string to binary From 5a8b5e7fe2093be94a71fa7d6b9a6fb964a4edcb Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Fri, 16 May 2014 23:18:53 +0200 Subject: [PATCH 2/6] osx: initial support for OS X --- Makefile | 11 ++++++++++- configure | 19 ++++++++++++++++++- src/httpc.c | 6 +++--- src/muxer.c | 8 ++++++++ src/trap.c | 22 +++++++++++++++++++++- src/tvheadend.h | 19 +++++++++++++++++++ src/upnp.c | 2 +- src/webui/webui.c | 5 ++++- src/wrappers.c | 2 ++ support/configure.inc | 2 ++ 10 files changed, 88 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index dee8c412..1bf97c33 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,16 @@ CFLAGS += -Wmissing-prototypes -fms-extensions CFLAGS += -g -funsigned-char -O2 CFLAGS += -D_FILE_OFFSET_BITS=64 CFLAGS += -I${BUILDDIR} -I${ROOTDIR}/src -I${ROOTDIR} -LDFLAGS += -lrt -ldl -lpthread -lm +LDFLAGS += -ldl -lpthread -lm +ifneq ($(PLATFORM), darwin) +LDFLAGS += -lrt +endif + +ifeq ($(COMPILER), clang) +CFLAGS += -Wno-microsoft -Qunused-arguments -Wno-unused-function +CFLAGS += -Wno-unused-value -Wno-tautological-constant-out-of-range-compare +CFLAGS += -Wno-parentheses-equality -Wno-incompatible-pointer-types +endif vpath %.c $(ROOTDIR) vpath %.h $(ROOTDIR) diff --git a/configure b/configure index cdcd469d..0fbe95d0 100755 --- a/configure +++ b/configure @@ -72,6 +72,16 @@ check_cc_header execinfo check_cc_option mmx check_cc_option sse2 +if check_cc ' +#if !defined(__clang__) +#error this is not clang +#endif +'; then + COMPILER=clang +else + COMPILER=gcc +fi + check_cc_snippet getloadavg '#include void test() { getloadavg(NULL,0); }' @@ -147,6 +157,13 @@ else die "SSL development support not found" fi +# +# OS X +# +if [ ${PLATFORM} = "darwin" ]; then + disable linuxdvb +fi + # # DVB API # @@ -296,7 +313,7 @@ fi # # kqueue # -if [ ${PLATFORM} = "freebsd" ]; then +if [ ${PLATFORM} = "freebsd" ] || [ ${PLATFORM} = "darwin" ]; then enable kqueue fi diff --git a/src/httpc.c b/src/httpc.c index 4efa047d..21cb6771 100644 --- a/src/httpc.c +++ b/src/httpc.c @@ -766,9 +766,9 @@ http_client_data_received( http_client_t *hc, char *buf, ssize_t len, int hdr ) return 0; } - csize = hc->hc_csize < 0 ? 0 : hc->hc_csize; + csize = hc->hc_csize == (size_t) -1 ? 0 : hc->hc_csize; l = len; - if (hc->hc_csize && hc->hc_csize != -1 && hc->hc_rpos > csize) { + if (hc->hc_csize && hc->hc_csize != (size_t) -1 && hc->hc_rpos > csize) { l2 = hc->hc_rpos - csize; if (l2 < l) l = l2; @@ -801,7 +801,7 @@ int http_client_run( http_client_t *hc ) { char *buf, *saveptr, *argv[3], *d, *p; - http_ver_t ver; + int ver; ssize_t r; size_t len; int res; diff --git a/src/muxer.c b/src/muxer.c index d8d76e6b..03150d39 100644 --- a/src/muxer.c +++ b/src/muxer.c @@ -28,6 +28,10 @@ #include "muxer/muxer_libav.h" #endif +#if defined(PLATFORM_DARWIN) +#define fdatasync(fd) fcntl(fd, F_FULLFSYNC) +#endif + /** * Mime type for containers containing only audio */ @@ -458,7 +462,11 @@ muxer_cache_update(muxer_t *m, int fd, off_t pos, size_t size) fdatasync(fd); /* fall through */ case MC_CACHE_DONTKEEP: +#if defined(PLATFORM_DARWIN) + fcntl(fd, F_NOCACHE, 1); +#else posix_fadvise(fd, pos, size, POSIX_FADV_DONTNEED); +#endif break; default: abort(); diff --git a/src/trap.c b/src/trap.c index 5481792d..2bb99c27 100644 --- a/src/trap.c +++ b/src/trap.c @@ -20,7 +20,7 @@ char tvh_binshasum[20]; -#if defined(__i386__) || defined(__x86_64__) +#if (defined(__i386__) || defined(__x86_64__)) && !defined(PLATFORM_DARWIN) // Only do this on x86 for now @@ -315,6 +315,26 @@ trap_init(const char *ver) sigprocmask(SIG_UNBLOCK, &m, NULL); } +#elif defined(PLATFORM_DARWIN) + +#include +#include + +void +trap_init(const char *ver) +{ + sigset_t m; + + sigemptyset(&m); + sigaddset(&m, SIGSEGV); + sigaddset(&m, SIGBUS); + sigaddset(&m, SIGILL); + sigaddset(&m, SIGABRT); + sigaddset(&m, SIGFPE); + + sigprocmask(SIG_UNBLOCK, &m, NULL); +} + #else void diff --git a/src/tvheadend.h b/src/tvheadend.h index 91b0d831..e5910f8d 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -500,6 +500,20 @@ int tvh_str_update(char **strp, const char *src); #define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC #endif +#ifdef PLATFORM_DARWIN +#define CLOCK_MONOTONIC 0 +#define CLOCK_REALTIME 0 + +static inline int clock_gettime(int clk_id, struct timespec* t) { + struct timeval now; + int rv = gettimeofday(&now, NULL); + if (rv) return rv; + t->tv_sec = now.tv_sec; + t->tv_nsec = now.tv_usec * 1000; + return 0; +} +#endif + static inline int64_t getmonoclock(void) { @@ -677,8 +691,13 @@ void tvh_qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void #endif #define PRIslongword_t "ld" #define PRIulongword_t "lu" +#if defined(PLATFORM_DARWIN) +#define PRIsize_t PRIulongword_t +#define PRIssize_t PRIslongword_t +#else #define PRIsize_t PRIuword_t #define PRIssize_t PRIsword_t +#endif #if __WORDSIZE == 32 && defined(PLATFORM_FREEBSD) #define PRItime_t PRIsword_t #else diff --git a/src/upnp.c b/src/upnp.c index 4c9435c4..0c6f5ee8 100644 --- a/src/upnp.c +++ b/src/upnp.c @@ -147,7 +147,7 @@ upnp_thread( void *aux ) inet_ntop(ip.ss_family, IP_IN_ADDR(ip), tbuf, sizeof(tbuf)); tvhtrace("upnp", "%s - received data from %s:%hu [size=%zi]", conn == multicast ? "multicast" : "unicast", - tbuf, IP_PORT(ip), size); + tbuf, (unsigned short) IP_PORT(ip), size); tvhlog_hexdump("upnp", buf, size); } #endif diff --git a/src/webui/webui.c b/src/webui/webui.c index d56c79cf..7929ad29 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -930,7 +930,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) off_t content_len, file_start, file_end, chunk; #if defined(PLATFORM_LINUX) ssize_t r; -#elif defined(PLATFORM_FREEBSD) +#elif defined(PLATFORM_FREEBSD) || defined(PLATFORM_DARWIN) off_t r; #endif @@ -1017,6 +1017,9 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) r = sendfile(hc->hc_fd, fd, NULL, chunk); #elif defined(PLATFORM_FREEBSD) sendfile(fd, hc->hc_fd, 0, chunk, NULL, &r, 0); +#elif defined(PLATFORM_DARWIN) + r = chunk; + sendfile(fd, hc->hc_fd, 0, NULL, &r, 0); #endif if(r == -1) { close(fd); diff --git a/src/wrappers.c b/src/wrappers.c index 82535abf..db3b5a16 100644 --- a/src/wrappers.c +++ b/src/wrappers.c @@ -109,6 +109,8 @@ thread_wrapper ( void *p ) #elif defined(PLATFORM_FREEBSD) /* Set name of thread */ pthread_set_name_np(pthread_self(), ts->name); +#elif defined(PLATFORM_DARWIN) + pthread_setname_np(ts->name); #endif sigemptyset(&set); diff --git a/support/configure.inc b/support/configure.inc index 8ddae982..96f1d68d 100755 --- a/support/configure.inc +++ b/support/configure.inc @@ -470,9 +470,11 @@ function write_config CONFIGURE_ARGS = ${CONFIGURE_ARGS} ROOTDIR ?= ${ROOTDIR} BUILDDIR ?= ${BUILDDIR} +PLATFORM ?= ${PLATFORM} OSENV ?= ${OSENV} ARCH ?= ${ARCH} CPU ?= ${CPU} +COMPILER ?= ${COMPILER} ifeq (\$(origin CC),default) CC = ${CC} endif From d07071bf908137f0718bacbc46ae8e6663167794 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 20 May 2014 21:25:44 +0200 Subject: [PATCH 3/6] intlconv: use iconv for utf8 conversions --- Makefile | 6 +- src/api.c | 1 + src/api.h | 1 + src/api/api_intlconv.c | 68 ++ src/intlconv.c | 1336 ++++++++++++++++++++++++++++++++++++++++ src/intlconv.h | 39 ++ src/main.c | 4 + 7 files changed, 1453 insertions(+), 2 deletions(-) create mode 100644 src/api/api_intlconv.c create mode 100644 src/intlconv.c create mode 100644 src/intlconv.h diff --git a/Makefile b/Makefile index dee8c412..0be59cfe 100644 --- a/Makefile +++ b/Makefile @@ -117,7 +117,8 @@ SRCS = src/version.c \ src/rtsp.c \ src/fsmonitor.c \ src/cron.c \ - src/esfilter.c + src/esfilter.c \ + src/intlconv.c SRCS-${CONFIG_UPNP} += \ src/upnp.c @@ -133,7 +134,8 @@ SRCS += \ src/api/api_epg.c \ src/api/api_epggrab.c \ src/api/api_imagecache.c \ - src/api/api_esfilter.c + src/api/api_esfilter.c \ + src/api/api_intlconv.c SRCS += \ src/parsers/parsers.c \ diff --git a/src/api.c b/src/api.c index 83392622..f88c2095 100644 --- a/src/api.c +++ b/src/api.c @@ -126,6 +126,7 @@ void api_init ( void ) api_status_init(); api_imagecache_init(); api_esfilter_init(); + api_intlconv_init(); } void api_done ( void ) diff --git a/src/api.h b/src/api.h index f51f7ebe..ce5db1d9 100644 --- a/src/api.h +++ b/src/api.h @@ -68,6 +68,7 @@ void api_epggrab_init ( void ); void api_status_init ( void ); void api_imagecache_init ( void ); void api_esfilter_init ( void ); +void api_intlconv_init ( void ); /* * IDnode diff --git a/src/api/api_intlconv.c b/src/api/api_intlconv.c new file mode 100644 index 00000000..dfe0c426 --- /dev/null +++ b/src/api/api_intlconv.c @@ -0,0 +1,68 @@ +/* + * API - international character conversions + * + * Copyright (C) 2014 Jaroslav Kysela + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TVH_API_INTLCONV_H__ +#define __TVH_API_INTLCONV_H__ + +#include "tvheadend.h" +#include "access.h" +#include "api.h" +#include "intlconv.h" + +static int +api_intlconv_charset_enum + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + const char **chrst; + htsmsg_t *l, *e; + + int _enum = htsmsg_get_bool_or_default(args, "enum", 0); + + if (_enum) { + l = htsmsg_create_list(); + chrst = intlconv_charsets; + while (*chrst) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "key", *chrst); + htsmsg_add_str(e, "val", *chrst); + htsmsg_add_msg(l, NULL, e); + chrst++; + } + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + } else { + // TODO: support full listing v enum + } + return 0; +} + +void api_intlconv_init ( void ) +{ + static api_hook_t ah[] = { + + { "intlconv/charsets", ACCESS_ANONYMOUS, api_intlconv_charset_enum, NULL }, + + { NULL }, + }; + + api_register_all(ah); +} + + +#endif /* __TVH_API_INTLCONV_H__ */ diff --git a/src/intlconv.c b/src/intlconv.c new file mode 100644 index 00000000..cfea4f21 --- /dev/null +++ b/src/intlconv.c @@ -0,0 +1,1336 @@ +#include +#include "tvheadend.h" +#include "intlconv.h" + +typedef struct intlconv_cache { + RB_ENTRY(intlconv_cache) ic_link; + char *ic_charset_id; + iconv_t *ic_handle; +} intlconv_cache_t; + +static RB_HEAD(,intlconv_cache) intlconv_all; +static intlconv_cache_t *intlconv_last_ic; +pthread_mutex_t intlconv_lock; + +void +intlconv_init( void ) +{ + pthread_mutex_init(&intlconv_lock, NULL); +} + +void +intlconv_done( void ) +{ + intlconv_cache_t *ic; + + pthread_mutex_lock(&intlconv_lock); + intlconv_last_ic = NULL; + while ((ic = RB_FIRST(&intlconv_all)) != NULL) { + iconv_close(ic->ic_handle); + free(ic->ic_charset_id); + RB_REMOVE(&intlconv_all, ic, ic_link); + free(ic); + } + pthread_mutex_unlock(&intlconv_lock); +} + +static int +intlconv_cmp ( intlconv_cache_t *a, intlconv_cache_t *b ) +{ + return strcmp(a->ic_charset_id, b->ic_charset_id); +} + +char * +intlconv_charset_id( const char *charset, + int transil, + int ignore_bad_chars ) +{ + static char __thread buf[128]; + const char *delim; + + if (charset == NULL || charset[0] == '\0' || + strcasecmp(charset, "UTF8") == 0 || + strcasecmp(charset, "UTF-8") == 0) + return NULL; + delim = index(charset, '/') ? + (charset[strlen(charset)-1] != '/' ? "/" : "") : "//"; + if (transil && ignore_bad_chars) + snprintf(buf, sizeof(buf), "%s%sTRANSLIT//IGNORE", charset, delim); + else if (transil) + snprintf(buf, sizeof(buf), "%s%sTRANSLIT", charset, delim); + else if (ignore_bad_chars) + snprintf(buf, sizeof(buf), "%s%sIGNORE", charset, delim); + else { + strncpy(buf, charset, sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + } + return buf; +} + +ssize_t +intlconv_utf8( char *dst, size_t dst_size, + const char *dst_charset_id, + const char *src_utf8 ) +{ + intlconv_cache_t templ, *ic; + char **inbuf, **outbuf; + size_t inbuf_left, outbuf_left; + ssize_t res; + + if (dst_charset_id == NULL) { + strncpy(dst, src_utf8, dst_size); + dst[dst_size - 1] = '\0'; + return strlen(dst); + } + templ.ic_charset_id = (char *)dst_charset_id; + pthread_mutex_lock(&intlconv_lock); + if (intlconv_last_ic && + strcmp(intlconv_last_ic->ic_charset_id, dst_charset_id) == 0) { + ic = intlconv_last_ic; + goto found; + } + ic = RB_FIND(&intlconv_all, &templ, ic_link, intlconv_cmp); + if (!ic) { + iconv_t c = iconv_open(dst_charset_id, "UTF-8"); + if ((iconv_t)-1 == c) { + pthread_mutex_unlock(&intlconv_lock); + return -EIO; + } + ic = malloc(sizeof(*ic)); + if (ic == NULL) { + pthread_mutex_unlock(&intlconv_lock); + return -ENOMEM; + } + ic->ic_charset_id = strdup(dst_charset_id); + if (ic->ic_charset_id == NULL) { + pthread_mutex_unlock(&intlconv_lock); + free(ic); + iconv_close(c); + return -ENOMEM; + } + ic->ic_handle = c; + RB_INSERT_SORTED(&intlconv_all, ic, ic_link, intlconv_cmp); + } +found: + inbuf = (char **)&src_utf8; + inbuf_left = strlen(src_utf8); + outbuf = &dst; + outbuf_left = dst_size; + res = iconv(ic->ic_handle, inbuf, &inbuf_left, outbuf, &outbuf_left); + if (res == -1) { + res = -errno; + } else { + intlconv_last_ic = ic; + } + pthread_mutex_unlock(&intlconv_lock); + if (res >= 0) + res = dst_size - outbuf_left; + return res; +} + +char * +intlconv_utf8safestr( const char *dst_charset_id, + const char *src_utf8, + size_t max_size ) +{ + char *str = alloca(max_size), *res; + ssize_t r = intlconv_utf8(str, max_size, dst_charset_id, src_utf8); + size_t i; + if (r <= 0) + return NULL; + if (r >= max_size) + r--; + str[r++] = '\0'; + res = strdup(str); + if (res == NULL) { + free(str); + } else { + /* don't terminate string */ + for (i = 0; i < r - 1; i++) + if (str[i] == '\0') + str[i] = ' '; + } + return res; +} + +/* + * + */ + +const char *intlconv_charsets[] = { + "437", + "500", + "500V1", + "850", + "851", + "852", + "855", + "856", + "857", + "860", + "861", + "862", + "863", + "864", + "865", + "866", + "866NAV", + "869", + "874", + "904", + "1026", + "1046", + "1047", + "8859_1", + "8859_2", + "8859_3", + "8859_4", + "8859_5", + "8859_6", + "8859_7", + "8859_8", + "8859_9", + "10646-1:1993", + "10646-1:1993/UCS4", + "ANSI_X3.4-1968", + "ANSI_X3.4-1986", + "ANSI_X3.4", + "ANSI_X3.110-1983", + "ANSI_X3.110", + "ARABIC", + "ARABIC7", + "ARMSCII-8", + "ASCII", + "ASMO-708", + "ASMO_449", + "BALTIC", + "BIG-5", + "BIG-FIVE", + "BIG5-HKSCS", + "BIG5", + "BIG5HKSCS", + "BIGFIVE", + "BRF", + "BS_4730", + "CA", + "CN-BIG5", + "CN-GB", + "CN", + "CP-AR", + "CP-GR", + "CP-HU", + "CP037", + "CP038", + "CP273", + "CP274", + "CP275", + "CP278", + "CP280", + "CP281", + "CP282", + "CP284", + "CP285", + "CP290", + "CP297", + "CP367", + "CP420", + "CP423", + "CP424", + "CP437", + "CP500", + "CP737", + "CP770", + "CP771", + "CP772", + "CP773", + "CP774", + "CP775", + "CP803", + "CP813", + "CP819", + "CP850", + "CP851", + "CP852", + "CP855", + "CP856", + "CP857", + "CP860", + "CP861", + "CP862", + "CP863", + "CP864", + "CP865", + "CP866", + "CP866NAV", + "CP868", + "CP869", + "CP870", + "CP871", + "CP874", + "CP875", + "CP880", + "CP891", + "CP901", + "CP902", + "CP903", + "CP904", + "CP905", + "CP912", + "CP915", + "CP916", + "CP918", + "CP920", + "CP921", + "CP922", + "CP930", + "CP932", + "CP933", + "CP935", + "CP936", + "CP937", + "CP939", + "CP949", + "CP950", + "CP1004", + "CP1008", + "CP1025", + "CP1026", + "CP1046", + "CP1047", + "CP1070", + "CP1079", + "CP1081", + "CP1084", + "CP1089", + "CP1097", + "CP1112", + "CP1122", + "CP1123", + "CP1124", + "CP1125", + "CP1129", + "CP1130", + "CP1132", + "CP1133", + "CP1137", + "CP1140", + "CP1141", + "CP1142", + "CP1143", + "CP1144", + "CP1145", + "CP1146", + "CP1147", + "CP1148", + "CP1149", + "CP1153", + "CP1154", + "CP1155", + "CP1156", + "CP1157", + "CP1158", + "CP1160", + "CP1161", + "CP1162", + "CP1163", + "CP1164", + "CP1166", + "CP1167", + "CP1250", + "CP1251", + "CP1252", + "CP1253", + "CP1254", + "CP1255", + "CP1256", + "CP1257", + "CP1258", + "CP1282", + "CP1361", + "CP1364", + "CP1371", + "CP1388", + "CP1390", + "CP1399", + "CP4517", + "CP4899", + "CP4909", + "CP4971", + "CP5347", + "CP9030", + "CP9066", + "CP9448", + "CP10007", + "CP12712", + "CP16804", + "CPIBM861", + "CSA7-1", + "CSA7-2", + "CSASCII", + "CSA_T500-1983", + "CSA_T500", + "CSA_Z243.4-1985-1", + "CSA_Z243.4-1985-2", + "CSA_Z243.419851", + "CSA_Z243.419852", + "CSDECMCS", + "CSEBCDICATDE", + "CSEBCDICATDEA", + "CSEBCDICCAFR", + "CSEBCDICDKNO", + "CSEBCDICDKNOA", + "CSEBCDICES", + "CSEBCDICESA", + "CSEBCDICESS", + "CSEBCDICFISE", + "CSEBCDICFISEA", + "CSEBCDICFR", + "CSEBCDICIT", + "CSEBCDICPT", + "CSEBCDICUK", + "CSEBCDICUS", + "CSEUCKR", + "CSEUCPKDFMTJAPANESE", + "CSGB2312", + "CSHPROMAN8", + "CSIBM037", + "CSIBM038", + "CSIBM273", + "CSIBM274", + "CSIBM275", + "CSIBM277", + "CSIBM278", + "CSIBM280", + "CSIBM281", + "CSIBM284", + "CSIBM285", + "CSIBM290", + "CSIBM297", + "CSIBM420", + "CSIBM423", + "CSIBM424", + "CSIBM500", + "CSIBM803", + "CSIBM851", + "CSIBM855", + "CSIBM856", + "CSIBM857", + "CSIBM860", + "CSIBM863", + "CSIBM864", + "CSIBM865", + "CSIBM866", + "CSIBM868", + "CSIBM869", + "CSIBM870", + "CSIBM871", + "CSIBM880", + "CSIBM891", + "CSIBM901", + "CSIBM902", + "CSIBM903", + "CSIBM904", + "CSIBM905", + "CSIBM918", + "CSIBM921", + "CSIBM922", + "CSIBM930", + "CSIBM932", + "CSIBM933", + "CSIBM935", + "CSIBM937", + "CSIBM939", + "CSIBM943", + "CSIBM1008", + "CSIBM1025", + "CSIBM1026", + "CSIBM1097", + "CSIBM1112", + "CSIBM1122", + "CSIBM1123", + "CSIBM1124", + "CSIBM1129", + "CSIBM1130", + "CSIBM1132", + "CSIBM1133", + "CSIBM1137", + "CSIBM1140", + "CSIBM1141", + "CSIBM1142", + "CSIBM1143", + "CSIBM1144", + "CSIBM1145", + "CSIBM1146", + "CSIBM1147", + "CSIBM1148", + "CSIBM1149", + "CSIBM1153", + "CSIBM1154", + "CSIBM1155", + "CSIBM1156", + "CSIBM1157", + "CSIBM1158", + "CSIBM1160", + "CSIBM1161", + "CSIBM1163", + "CSIBM1164", + "CSIBM1166", + "CSIBM1167", + "CSIBM1364", + "CSIBM1371", + "CSIBM1388", + "CSIBM1390", + "CSIBM1399", + "CSIBM4517", + "CSIBM4899", + "CSIBM4909", + "CSIBM4971", + "CSIBM5347", + "CSIBM9030", + "CSIBM9066", + "CSIBM9448", + "CSIBM12712", + "CSIBM16804", + "CSIBM11621162", + "CSISO4UNITEDKINGDOM", + "CSISO10SWEDISH", + "CSISO11SWEDISHFORNAMES", + "CSISO14JISC6220RO", + "CSISO15ITALIAN", + "CSISO16PORTUGESE", + "CSISO17SPANISH", + "CSISO18GREEK7OLD", + "CSISO19LATINGREEK", + "CSISO21GERMAN", + "CSISO25FRENCH", + "CSISO27LATINGREEK1", + "CSISO49INIS", + "CSISO50INIS8", + "CSISO51INISCYRILLIC", + "CSISO58GB1988", + "CSISO60DANISHNORWEGIAN", + "CSISO60NORWEGIAN1", + "CSISO61NORWEGIAN2", + "CSISO69FRENCH", + "CSISO84PORTUGUESE2", + "CSISO85SPANISH2", + "CSISO86HUNGARIAN", + "CSISO88GREEK7", + "CSISO89ASMO449", + "CSISO90", + "CSISO92JISC62991984B", + "CSISO99NAPLPS", + "CSISO103T618BIT", + "CSISO111ECMACYRILLIC", + "CSISO121CANADIAN1", + "CSISO122CANADIAN2", + "CSISO139CSN369103", + "CSISO141JUSIB1002", + "CSISO143IECP271", + "CSISO150", + "CSISO150GREEKCCITT", + "CSISO151CUBA", + "CSISO153GOST1976874", + "CSISO646DANISH", + "CSISO2022CN", + "CSISO2022JP", + "CSISO2022JP2", + "CSISO2022KR", + "CSISO2033", + "CSISO5427CYRILLIC", + "CSISO5427CYRILLIC1981", + "CSISO5428GREEK", + "CSISO10367BOX", + "CSISOLATIN1", + "CSISOLATIN2", + "CSISOLATIN3", + "CSISOLATIN4", + "CSISOLATIN5", + "CSISOLATIN6", + "CSISOLATINARABIC", + "CSISOLATINCYRILLIC", + "CSISOLATINGREEK", + "CSISOLATINHEBREW", + "CSKOI8R", + "CSKSC5636", + "CSMACINTOSH", + "CSNATSDANO", + "CSNATSSEFI", + "CSN_369103", + "CSPC8CODEPAGE437", + "CSPC775BALTIC", + "CSPC850MULTILINGUAL", + "CSPC862LATINHEBREW", + "CSPCP852", + "CSSHIFTJIS", + "CSUCS4", + "CSUNICODE", + "CSWINDOWS31J", + "CUBA", + "CWI-2", + "CWI", + "CYRILLIC", + "DE", + "DEC-MCS", + "DEC", + "DECMCS", + "DIN_66003", + "DK", + "DS2089", + "DS_2089", + "E13B", + "EBCDIC-AT-DE-A", + "EBCDIC-AT-DE", + "EBCDIC-BE", + "EBCDIC-BR", + "EBCDIC-CA-FR", + "EBCDIC-CP-AR1", + "EBCDIC-CP-AR2", + "EBCDIC-CP-BE", + "EBCDIC-CP-CA", + "EBCDIC-CP-CH", + "EBCDIC-CP-DK", + "EBCDIC-CP-ES", + "EBCDIC-CP-FI", + "EBCDIC-CP-FR", + "EBCDIC-CP-GB", + "EBCDIC-CP-GR", + "EBCDIC-CP-HE", + "EBCDIC-CP-IS", + "EBCDIC-CP-IT", + "EBCDIC-CP-NL", + "EBCDIC-CP-NO", + "EBCDIC-CP-ROECE", + "EBCDIC-CP-SE", + "EBCDIC-CP-TR", + "EBCDIC-CP-US", + "EBCDIC-CP-WT", + "EBCDIC-CP-YU", + "EBCDIC-CYRILLIC", + "EBCDIC-DK-NO-A", + "EBCDIC-DK-NO", + "EBCDIC-ES-A", + "EBCDIC-ES-S", + "EBCDIC-ES", + "EBCDIC-FI-SE-A", + "EBCDIC-FI-SE", + "EBCDIC-FR", + "EBCDIC-GREEK", + "EBCDIC-INT", + "EBCDIC-INT1", + "EBCDIC-IS-FRISS", + "EBCDIC-IT", + "EBCDIC-JP-E", + "EBCDIC-JP-KANA", + "EBCDIC-PT", + "EBCDIC-UK", + "EBCDIC-US", + "EBCDICATDE", + "EBCDICATDEA", + "EBCDICCAFR", + "EBCDICDKNO", + "EBCDICDKNOA", + "EBCDICES", + "EBCDICESA", + "EBCDICESS", + "EBCDICFISE", + "EBCDICFISEA", + "EBCDICFR", + "EBCDICISFRISS", + "EBCDICIT", + "EBCDICPT", + "EBCDICUK", + "EBCDICUS", + "ECMA-114", + "ECMA-118", + "ECMA-128", + "ECMA-CYRILLIC", + "ECMACYRILLIC", + "ELOT_928", + "ES", + "ES2", + "EUC-CN", + "EUC-JISX0213", + "EUC-JP-MS", + "EUC-JP", + "EUC-KR", + "EUC-TW", + "EUCCN", + "EUCJP-MS", + "EUCJP-OPEN", + "EUCJP-WIN", + "EUCJP", + "EUCKR", + "EUCTW", + "FI", + "FR", + "GB", + "GB2312", + "GB13000", + "GB18030", + "GBK", + "GB_1988-80", + "GB_198880", + "GEORGIAN-ACADEMY", + "GEORGIAN-PS", + "GOST_19768-74", + "GOST_19768", + "GOST_1976874", + "GREEK-CCITT", + "GREEK", + "GREEK7-OLD", + "GREEK7", + "GREEK7OLD", + "GREEK8", + "GREEKCCITT", + "HEBREW", + "HP-GREEK8", + "HP-ROMAN8", + "HP-ROMAN9", + "HP-THAI8", + "HP-TURKISH8", + "HPGREEK8", + "HPROMAN8", + "HPROMAN9", + "HPTHAI8", + "HPTURKISH8", + "HU", + "IBM-803", + "IBM-856", + "IBM-901", + "IBM-902", + "IBM-921", + "IBM-922", + "IBM-930", + "IBM-932", + "IBM-933", + "IBM-935", + "IBM-937", + "IBM-939", + "IBM-943", + "IBM-1008", + "IBM-1025", + "IBM-1046", + "IBM-1047", + "IBM-1097", + "IBM-1112", + "IBM-1122", + "IBM-1123", + "IBM-1124", + "IBM-1129", + "IBM-1130", + "IBM-1132", + "IBM-1133", + "IBM-1137", + "IBM-1140", + "IBM-1141", + "IBM-1142", + "IBM-1143", + "IBM-1144", + "IBM-1145", + "IBM-1146", + "IBM-1147", + "IBM-1148", + "IBM-1149", + "IBM-1153", + "IBM-1154", + "IBM-1155", + "IBM-1156", + "IBM-1157", + "IBM-1158", + "IBM-1160", + "IBM-1161", + "IBM-1162", + "IBM-1163", + "IBM-1164", + "IBM-1166", + "IBM-1167", + "IBM-1364", + "IBM-1371", + "IBM-1388", + "IBM-1390", + "IBM-1399", + "IBM-4517", + "IBM-4899", + "IBM-4909", + "IBM-4971", + "IBM-5347", + "IBM-9030", + "IBM-9066", + "IBM-9448", + "IBM-12712", + "IBM-16804", + "IBM037", + "IBM038", + "IBM256", + "IBM273", + "IBM274", + "IBM275", + "IBM277", + "IBM278", + "IBM280", + "IBM281", + "IBM284", + "IBM285", + "IBM290", + "IBM297", + "IBM367", + "IBM420", + "IBM423", + "IBM424", + "IBM437", + "IBM500", + "IBM775", + "IBM803", + "IBM813", + "IBM819", + "IBM848", + "IBM850", + "IBM851", + "IBM852", + "IBM855", + "IBM856", + "IBM857", + "IBM860", + "IBM861", + "IBM862", + "IBM863", + "IBM864", + "IBM865", + "IBM866", + "IBM866NAV", + "IBM868", + "IBM869", + "IBM870", + "IBM871", + "IBM874", + "IBM875", + "IBM880", + "IBM891", + "IBM901", + "IBM902", + "IBM903", + "IBM904", + "IBM905", + "IBM912", + "IBM915", + "IBM916", + "IBM918", + "IBM920", + "IBM921", + "IBM922", + "IBM930", + "IBM932", + "IBM933", + "IBM935", + "IBM937", + "IBM939", + "IBM943", + "IBM1004", + "IBM1008", + "IBM1025", + "IBM1026", + "IBM1046", + "IBM1047", + "IBM1089", + "IBM1097", + "IBM1112", + "IBM1122", + "IBM1123", + "IBM1124", + "IBM1129", + "IBM1130", + "IBM1132", + "IBM1133", + "IBM1137", + "IBM1140", + "IBM1141", + "IBM1142", + "IBM1143", + "IBM1144", + "IBM1145", + "IBM1146", + "IBM1147", + "IBM1148", + "IBM1149", + "IBM1153", + "IBM1154", + "IBM1155", + "IBM1156", + "IBM1157", + "IBM1158", + "IBM1160", + "IBM1161", + "IBM1162", + "IBM1163", + "IBM1164", + "IBM1166", + "IBM1167", + "IBM1364", + "IBM1371", + "IBM1388", + "IBM1390", + "IBM1399", + "IBM4517", + "IBM4899", + "IBM4909", + "IBM4971", + "IBM5347", + "IBM9030", + "IBM9066", + "IBM9448", + "IBM12712", + "IBM16804", + "IEC_P27-1", + "IEC_P271", + "INIS-8", + "INIS-CYRILLIC", + "INIS", + "INIS8", + "INISCYRILLIC", + "ISIRI-3342", + "ISIRI3342", + "ISO-2022-CN-EXT", + "ISO-2022-CN", + "ISO-2022-JP-2", + "ISO-2022-JP-3", + "ISO-2022-JP", + "ISO-2022-KR", + "ISO-8859-1", + "ISO-8859-2", + "ISO-8859-3", + "ISO-8859-4", + "ISO-8859-5", + "ISO-8859-6", + "ISO-8859-7", + "ISO-8859-8", + "ISO-8859-9", + "ISO-8859-9E", + "ISO-8859-10", + "ISO-8859-11", + "ISO-8859-13", + "ISO-8859-14", + "ISO-8859-15", + "ISO-8859-16", + "ISO-10646-UCS-2", + "ISO-10646", + "ISO-10646/UCS2", + "ISO-10646/UCS4", + "ISO-10646/UTF-8", + "ISO-10646/UTF8", + "ISO-CELTIC", + "ISO-IR-4", + "ISO-IR-6", + "ISO-IR-8-1", + "ISO-IR-9-1", + "ISO-IR-10", + "ISO-IR-11", + "ISO-IR-14", + "ISO-IR-15", + "ISO-IR-16", + "ISO-IR-17", + "ISO-IR-18", + "ISO-IR-19", + "ISO-IR-21", + "ISO-IR-25", + "ISO-IR-27", + "ISO-IR-37", + "ISO-IR-49", + "ISO-IR-50", + "ISO-IR-51", + "ISO-IR-54", + "ISO-IR-55", + "ISO-IR-57", + "ISO-IR-60", + "ISO-IR-61", + "ISO-IR-69", + "ISO-IR-84", + "ISO-IR-85", + "ISO-IR-86", + "ISO-IR-88", + "ISO-IR-89", + "ISO-IR-90", + "ISO-IR-92", + "ISO-IR-98", + "ISO-IR-99", + "ISO-IR-100", + "ISO-IR-101", + "ISO-IR-103", + "ISO-IR-109", + "ISO-IR-110", + "ISO-IR-111", + "ISO-IR-121", + "ISO-IR-122", + "ISO-IR-126", + "ISO-IR-127", + "ISO-IR-138", + "ISO-IR-139", + "ISO-IR-141", + "ISO-IR-143", + "ISO-IR-144", + "ISO-IR-148", + "ISO-IR-150", + "ISO-IR-151", + "ISO-IR-153", + "ISO-IR-155", + "ISO-IR-156", + "ISO-IR-157", + "ISO-IR-166", + "ISO-IR-179", + "ISO-IR-193", + "ISO-IR-197", + "ISO-IR-199", + "ISO-IR-203", + "ISO-IR-209", + "ISO-IR-226", + "ISO/TR_11548-1", + "ISO646-CA", + "ISO646-CA2", + "ISO646-CN", + "ISO646-CU", + "ISO646-DE", + "ISO646-DK", + "ISO646-ES", + "ISO646-ES2", + "ISO646-FI", + "ISO646-FR", + "ISO646-FR1", + "ISO646-GB", + "ISO646-HU", + "ISO646-IT", + "ISO646-JP-OCR-B", + "ISO646-JP", + "ISO646-KR", + "ISO646-NO", + "ISO646-NO2", + "ISO646-PT", + "ISO646-PT2", + "ISO646-SE", + "ISO646-SE2", + "ISO646-US", + "ISO646-YU", + "ISO2022CN", + "ISO2022CNEXT", + "ISO2022JP", + "ISO2022JP2", + "ISO2022KR", + "ISO6937", + "ISO8859-1", + "ISO8859-2", + "ISO8859-3", + "ISO8859-4", + "ISO8859-5", + "ISO8859-6", + "ISO8859-7", + "ISO8859-8", + "ISO8859-9", + "ISO8859-9E", + "ISO8859-10", + "ISO8859-11", + "ISO8859-13", + "ISO8859-14", + "ISO8859-15", + "ISO8859-16", + "ISO11548-1", + "ISO88591", + "ISO88592", + "ISO88593", + "ISO88594", + "ISO88595", + "ISO88596", + "ISO88597", + "ISO88598", + "ISO88599", + "ISO88599E", + "ISO885910", + "ISO885911", + "ISO885913", + "ISO885914", + "ISO885915", + "ISO885916", + "ISO_646.IRV:1991", + "ISO_2033-1983", + "ISO_2033", + "ISO_5427-EXT", + "ISO_5427", + "ISO_5427:1981", + "ISO_5427EXT", + "ISO_5428", + "ISO_5428:1980", + "ISO_6937-2", + "ISO_6937-2:1983", + "ISO_6937", + "ISO_6937:1992", + "ISO_8859-1", + "ISO_8859-1:1987", + "ISO_8859-2", + "ISO_8859-2:1987", + "ISO_8859-3", + "ISO_8859-3:1988", + "ISO_8859-4", + "ISO_8859-4:1988", + "ISO_8859-5", + "ISO_8859-5:1988", + "ISO_8859-6", + "ISO_8859-6:1987", + "ISO_8859-7", + "ISO_8859-7:1987", + "ISO_8859-7:2003", + "ISO_8859-8", + "ISO_8859-8:1988", + "ISO_8859-9", + "ISO_8859-9:1989", + "ISO_8859-9E", + "ISO_8859-10", + "ISO_8859-10:1992", + "ISO_8859-14", + "ISO_8859-14:1998", + "ISO_8859-15", + "ISO_8859-15:1998", + "ISO_8859-16", + "ISO_8859-16:2001", + "ISO_9036", + "ISO_10367-BOX", + "ISO_10367BOX", + "ISO_11548-1", + "ISO_69372", + "IT", + "JIS_C6220-1969-RO", + "JIS_C6229-1984-B", + "JIS_C62201969RO", + "JIS_C62291984B", + "JOHAB", + "JP-OCR-B", + "JP", + "JS", + "JUS_I.B1.002", + "KOI-7", + "KOI-8", + "KOI8-R", + "KOI8-RU", + "KOI8-T", + "KOI8-U", + "KOI8", + "KOI8R", + "KOI8U", + "KSC5636", + "L1", + "L2", + "L3", + "L4", + "L5", + "L6", + "L7", + "L8", + "L10", + "LATIN-9", + "LATIN-GREEK-1", + "LATIN-GREEK", + "LATIN1", + "LATIN2", + "LATIN3", + "LATIN4", + "LATIN5", + "LATIN6", + "LATIN7", + "LATIN8", + "LATIN9", + "LATIN10", + "LATINGREEK", + "LATINGREEK1", + "MAC-CENTRALEUROPE", + "MAC-CYRILLIC", + "MAC-IS", + "MAC-SAMI", + "MAC-UK", + "MAC", + "MACCYRILLIC", + "MACINTOSH", + "MACIS", + "MACUK", + "MACUKRAINIAN", + "MIK", + "MS-ANSI", + "MS-ARAB", + "MS-CYRL", + "MS-EE", + "MS-GREEK", + "MS-HEBR", + "MS-MAC-CYRILLIC", + "MS-TURK", + "MS932", + "MS936", + "MSCP949", + "MSCP1361", + "MSMACCYRILLIC", + "MSZ_7795.3", + "MS_KANJI", + "NAPLPS", + "NATS-DANO", + "NATS-SEFI", + "NATSDANO", + "NATSSEFI", + "NC_NC0010", + "NC_NC00-10", + "NC_NC00-10:81", + "NF_Z_62-010", + "NF_Z_62-010_(1973)", + "NF_Z_62-010_1973", + "NF_Z_62010", + "NF_Z_62010_1973", + "NO", + "NO2", + "NS_4551-1", + "NS_4551-2", + "NS_45511", + "NS_45512", + "OS2LATIN1", + "OSF00010001", + "OSF00010002", + "OSF00010003", + "OSF00010004", + "OSF00010005", + "OSF00010006", + "OSF00010007", + "OSF00010008", + "OSF00010009", + "OSF0001000A", + "OSF00010020", + "OSF00010100", + "OSF00010101", + "OSF00010102", + "OSF00010104", + "OSF00010105", + "OSF00010106", + "OSF00030010", + "OSF0004000A", + "OSF0005000A", + "OSF05010001", + "OSF100201A4", + "OSF100201A8", + "OSF100201B5", + "OSF100201F4", + "OSF100203B5", + "OSF1002011C", + "OSF1002011D", + "OSF1002035D", + "OSF1002035E", + "OSF1002035F", + "OSF1002036B", + "OSF1002037B", + "OSF10010001", + "OSF10010004", + "OSF10010006", + "OSF10020025", + "OSF10020111", + "OSF10020115", + "OSF10020116", + "OSF10020118", + "OSF10020122", + "OSF10020129", + "OSF10020352", + "OSF10020354", + "OSF10020357", + "OSF10020359", + "OSF10020360", + "OSF10020364", + "OSF10020365", + "OSF10020366", + "OSF10020367", + "OSF10020370", + "OSF10020387", + "OSF10020388", + "OSF10020396", + "OSF10020402", + "OSF10020417", + "PT", + "PT2", + "PT154", + "R8", + "R9", + "RK1048", + "ROMAN8", + "ROMAN9", + "RUSCII", + "SE", + "SE2", + "SEN_850200_B", + "SEN_850200_C", + "SHIFT-JIS", + "SHIFT_JIS", + "SHIFT_JISX0213", + "SJIS-OPEN", + "SJIS-WIN", + "SJIS", + "SS636127", + "STRK1048-2002", + "ST_SEV_358-88", + "T.61-8BIT", + "T.61", + "T.618BIT", + "TCVN-5712", + "TCVN", + "TCVN5712-1", + "TCVN5712-1:1993", + "THAI8", + "TIS-620", + "TIS620-0", + "TIS620.2529-1", + "TIS620.2533-0", + "TIS620", + "TS-5881", + "TSCII", + "TURKISH8", + "UCS-2", + "UCS-2BE", + "UCS-2LE", + "UCS-4", + "UCS-4BE", + "UCS-4LE", + "UCS2", + "UCS4", + "UHC", + "UJIS", + "UK", + "UNICODE", + "UNICODEBIG", + "UNICODELITTLE", + "US-ASCII", + "US", + "UTF-7", + "UTF-8", + "UTF-16", + "UTF-16BE", + "UTF-16LE", + "UTF-32", + "UTF-32BE", + "UTF-32LE", + "UTF7", + "UTF8", + "UTF16", + "UTF16BE", + "UTF16LE", + "UTF32", + "UTF32BE", + "UTF32LE", + "VISCII", + "WCHAR_T", + "WIN-SAMI-2", + "WINBALTRIM", + "WINDOWS-31J", + "WINDOWS-874", + "WINDOWS-936", + "WINDOWS-1250", + "WINDOWS-1251", + "WINDOWS-1252", + "WINDOWS-1253", + "WINDOWS-1254", + "WINDOWS-1255", + "WINDOWS-1256", + "WINDOWS-1257", + "WINDOWS-1258", + "WINSAMI2", + "WS2", + "YU", + NULL +}; diff --git a/src/intlconv.h b/src/intlconv.h new file mode 100644 index 00000000..54dd987a --- /dev/null +++ b/src/intlconv.h @@ -0,0 +1,39 @@ +/* + * tvheadend, iconv interface + * Copyright (C) 2014 Jaroslav Kysela + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef INTLCONV_H_ +#define INTLCONV_H_ + +extern const char *intlconv_charsets[]; + +void intlconv_init( void ); +void intlconv_done( void ); +char * +intlconv_charset_id( const char *charset, + int transil, + int ignore_bad_chars ); +ssize_t +intlconv_utf8( char *dst, size_t dst_size, + const char *dst_charset_id, + const char *src_utf8 ); +char * +intlconv_utf8safestr( const char *dst_charset_id, + const char *src_utf8, + size_t max_size ); + +#endif /* INTLCONV_H_ */ diff --git a/src/main.c b/src/main.c index 7602838c..0eda2218 100644 --- a/src/main.c +++ b/src/main.c @@ -62,6 +62,7 @@ #include "fsmonitor.h" #include "lang_codes.h" #include "esfilter.h" +#include "intlconv.h" #if ENABLE_LIBAV #include "libav.h" #include "plumbing/transcoding.h" @@ -755,6 +756,8 @@ main(int argc, char **argv) /** * Initialize subsystems */ + + intlconv_init(); api_init(); @@ -880,6 +883,7 @@ main(int argc, char **argv) tvhftrace("main", dvb_done); tvhftrace("main", lang_str_done); tvhftrace("main", esfilter_done); + tvhftrace("main", intlconv_done); tvhftrace("main", urlparse_done); tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend"); From eddc6ea844fd827b7a875ba16b6e94adbc5fc24f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 26 May 2014 21:14:48 +0200 Subject: [PATCH 4/6] dvr: add the filename charset conversion from UTF-8 --- docs/html/config_dvr.html | 3 ++ src/dvr/dvr.h | 4 ++ src/dvr/dvr_db.c | 44 ++++++++++++++++++- src/dvr/dvr_rec.c | 85 +++++++++++++++++++++++-------------- src/webui/extjs.c | 20 +++++---- src/webui/static/app/dvr.js | 27 +++++++++++- 6 files changed, 138 insertions(+), 45 deletions(-) diff --git a/docs/html/config_dvr.html b/docs/html/config_dvr.html index ef265ebd..fd82ab2a 100644 --- a/docs/html/config_dvr.html +++ b/docs/html/config_dvr.html @@ -95,6 +95,9 @@ See also Directory permissions in Subdirectory Options. +
Filename charset +
Character set for the created filename. Tvheadend will try to approximate characters to similarly looking ones. +
Rewrite PAT in passthrough mode
Rewrite the original Program Association Table to only include the active service. When this option is disabled, Tvheadend will write the original PAT as broadcast, which lists all services from the original multiplex. diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 16f027ff..fac68f1b 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -31,6 +31,8 @@ typedef struct dvr_config { char *dvr_storage; uint32_t dvr_retention_days; int dvr_flags; + char *dvr_charset; + char *dvr_charset_id; char *dvr_postproc; int dvr_extra_time_pre; int dvr_extra_time_post; @@ -330,6 +332,8 @@ void dvr_entry_dec_ref(dvr_entry_t *de); void dvr_storage_set(dvr_config_t *cfg, const char *storage); +void dvr_charset_set(dvr_config_t *cfg, const char *charset); + void dvr_container_set(dvr_config_t *cfg, const char *container); void dvr_file_permissions_set(dvr_config_t *cfg, int permissions); diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 0649ede9..147b6750 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -29,6 +29,7 @@ #include "notify.h" #include "htsp_server.h" #include "streaming.h" +#include "intlconv.h" static int de_tally; @@ -163,6 +164,22 @@ dvr_entry_notify(dvr_entry_t *de) } +/** + * + */ +static void +dvr_charset_update(dvr_config_t *cfg, const char *charset) +{ + const char *s, *id; + + free(cfg->dvr_charset); + free(cfg->dvr_charset_id); + s = charset ? charset : "ASCII"; + id = intlconv_charset_id(s, 1, 1); + cfg->dvr_charset = strdup(s); + cfg->dvr_charset_id = id ? strdup(id) : NULL; +} + /** * */ @@ -1190,7 +1207,9 @@ dvr_init(void) if(!htsmsg_get_u32(m, "episode-before-date", &u32) && u32) cfg->dvr_flags |= DVR_EPISODE_BEFORE_DATE; - tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc")); + dvr_charset_update(cfg, htsmsg_get_str(m, "charset")); + + tvh_str_set(&cfg->dvr_postproc, htsmsg_get_str(m, "postproc")); } htsmsg_destroy(l); @@ -1245,6 +1264,8 @@ dvr_done(void) pthread_mutex_lock(&global_lock); while ((cfg = LIST_FIRST(&dvrconfigs)) != NULL) { LIST_REMOVE(cfg, config_link); + free(cfg->dvr_charset_id); + free(cfg->dvr_charset); free(cfg->dvr_storage); free(cfg->dvr_config_name); free(cfg); @@ -1314,6 +1335,7 @@ dvr_config_create(const char *name) cfg->dvr_retention_days = 31; cfg->dvr_mc = MC_MATROSKA; cfg->dvr_flags = DVR_TAG_FILES | DVR_SKIP_COMMERCIALS; + dvr_charset_update(cfg, "ASCII"); /* series link support */ cfg->dvr_sl_brand_lock = 1; // use brand linking @@ -1359,6 +1381,9 @@ dvr_config_delete(const char *name) cfg->dvr_config_name); hts_settings_remove("dvr/config%s", cfg->dvr_config_name); LIST_REMOVE(cfg, config_link); + free(cfg->dvr_charset_id); + free(cfg->dvr_charset); + free(cfg->dvr_storage); free(cfg->dvr_config_name); free(cfg); @@ -1409,7 +1434,9 @@ dvr_save(dvr_config_t *cfg) htsmsg_add_u32(m, "skip-commercials", !!(cfg->dvr_flags & DVR_SKIP_COMMERCIALS)); htsmsg_add_u32(m, "subtitle-in-title", !!(cfg->dvr_flags & DVR_SUBTITLE_IN_TITLE)); htsmsg_add_u32(m, "episode-before-date", !!(cfg->dvr_flags & DVR_EPISODE_BEFORE_DATE)); - if(cfg->dvr_postproc != NULL) + if (cfg->dvr_charset != NULL) + htsmsg_add_str(m, "charset", cfg->dvr_charset); + if (cfg->dvr_postproc != NULL) htsmsg_add_str(m, "postproc", cfg->dvr_postproc); hts_settings_save(m, "dvr/config%s", cfg->dvr_config_name); @@ -1431,6 +1458,19 @@ dvr_storage_set(dvr_config_t *cfg, const char *storage) dvr_save(cfg); } +/** + * + */ +void +dvr_charset_set(dvr_config_t *cfg, const char *charset) +{ + if(cfg->dvr_charset != NULL && !strcmp(cfg->dvr_charset, charset)) + return; + + dvr_charset_update(cfg, charset); + dvr_save(cfg); +} + /** * */ diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 25814e81..a227f709 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -34,6 +34,7 @@ #include "plumbing/globalheaders.h" #include "htsp_server.h" #include "atomic.h" +#include "intlconv.h" #include "muxer.h" @@ -122,11 +123,27 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode) /** * Replace various chars with a dash */ -static void -cleanupfilename(char *s, int dvr_flags) +static char * +cleanup_filename(char *s, dvr_config_t *cfg) { - int i, len = strlen(s); - for(i = 0; i < len; i++) { + int i, len = strlen(s), dvr_flags = cfg->dvr_flags; + char *s1; + + s1 = intlconv_utf8safestr(cfg->dvr_charset_id, s, len * 2); + if (s1 == NULL) { + tvherror("dvr", "Unsupported charset %s using ASCII", cfg->dvr_charset); + s1 = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1), + s, len * 2); + if (s1 == NULL) + return NULL; + } + s = s1; + + /* Do not create hidden files */ + if (s[0] == '.') + s[0] = '-'; + + for (i = 0, len = strlen(s); i < len; i++) { if(s[i] == '/') s[i] = '-'; @@ -140,6 +157,8 @@ cleanupfilename(char *s, int dvr_flags) (strchr("/:\\<>|*?'\"", s[i]) != NULL))) s[i] = '-'; } + + return s; } /** @@ -152,65 +171,64 @@ cleanupfilename(char *s, int dvr_flags) static int pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss) { - char fullname[1000]; - char path[500]; + char fullname[PATH_MAX]; + char path[PATH_MAX]; int tally = 0; struct stat st; - char filename[1000]; + char *filename, *s; struct tm tm; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); - dvr_make_title(filename, sizeof(filename), de); - cleanupfilename(filename,cfg->dvr_flags); - - snprintf(path, sizeof(path), "%s", cfg->dvr_storage); + strncpy(path, cfg->dvr_storage, sizeof(path)); + path[sizeof(path)-1] = '\0'; /* Remove trailing slash */ - if (path[strlen(path)-1] == '/') path[strlen(path)-1] = '\0'; /* Append per-day directory */ - - if(cfg->dvr_flags & DVR_DIR_PER_DAY) { + if (cfg->dvr_flags & DVR_DIR_PER_DAY) { localtime_r(&de->de_start, &tm); strftime(fullname, sizeof(fullname), "%F", &tm); - cleanupfilename(fullname,cfg->dvr_flags); - snprintf(path + strlen(path), sizeof(path) - strlen(path), - "/%s", fullname); + s = cleanup_filename(fullname, cfg); + if (s == NULL) + return -1; + snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s); + free(s); } /* Append per-channel directory */ - - if(cfg->dvr_flags & DVR_DIR_PER_CHANNEL) { - + if (cfg->dvr_flags & DVR_DIR_PER_CHANNEL) { char *chname = strdup(DVR_CH_NAME(de)); - cleanupfilename(chname,cfg->dvr_flags); - snprintf(path + strlen(path), sizeof(path) - strlen(path), - "/%s", chname); + s = cleanup_filename(chname, cfg); free(chname); + if (s == NULL) + return -1; + snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s); + free(s); } // TODO: per-brand, per-season /* Append per-title directory */ - - if(cfg->dvr_flags & DVR_DIR_PER_TITLE) { - + if (cfg->dvr_flags & DVR_DIR_PER_TITLE) { char *title = strdup(lang_str_get(de->de_title, NULL)); - cleanupfilename(title,cfg->dvr_flags); - snprintf(path + strlen(path), sizeof(path) - strlen(path), - "/%s", title); + s = cleanup_filename(title, cfg); free(title); + if (s == NULL) + return -1; + snprintf(path + strlen(path), sizeof(path) - strlen(path), "/%s", s); + free(s); } - if(makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0) { + if (makedirs(path, cfg->dvr_muxcnf.m_directory_permissions) != 0) return -1; - } - /* Construct final name */ - + dvr_make_title(fullname, sizeof(fullname), de); + filename = cleanup_filename(fullname, cfg); + if (filename == NULL) + return -1; snprintf(fullname, sizeof(fullname), "%s/%s.%s", path, filename, muxer_suffix(de->de_mux, ss)); @@ -229,6 +247,7 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss) snprintf(fullname, sizeof(fullname), "%s/%s-%d.%s", path, filename, tally, muxer_suffix(de->de_mux, ss)); } + free(filename); tvh_str_set(&de->de_filename, fullname); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 3a3af8d3..550cfbbb 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1127,6 +1127,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) r = htsmsg_create_map(); htsmsg_add_str(r, "storage", cfg->dvr_storage); + htsmsg_add_str(r, "charset", cfg->dvr_charset); htsmsg_add_str(r, "container", muxer_container_type2txt(cfg->dvr_mc)); /* Convert integer permissions to an octal-format 0xxx string and store it in the config file */ @@ -1174,7 +1175,10 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) if((s = http_arg_get(&hc->hc_req_args, "storage")) != NULL) dvr_storage_set(cfg,s); - if((s = http_arg_get(&hc->hc_req_args, "container")) != NULL) + if((s = http_arg_get(&hc->hc_req_args, "charset")) != NULL) + dvr_charset_set(cfg,s); + + if((s = http_arg_get(&hc->hc_req_args, "container")) != NULL) dvr_container_set(cfg,s); /* @@ -1182,13 +1186,13 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) * Note no checking that strtol won't overflow int - this should never happen with three-digit numbers */ - if((s = http_arg_get(&hc->hc_req_args, "filePermissions")) != NULL) + if((s = http_arg_get(&hc->hc_req_args, "filePermissions")) != NULL) dvr_file_permissions_set(cfg,(int)strtol(s,NULL,0)); - if((s = http_arg_get(&hc->hc_req_args, "dirPermissions")) != NULL) + if((s = http_arg_get(&hc->hc_req_args, "dirPermissions")) != NULL) dvr_directory_permissions_set(cfg,(int)strtol(s,NULL,0)); - if((s = http_arg_get(&hc->hc_req_args, "cache")) != NULL) + if((s = http_arg_get(&hc->hc_req_args, "cache")) != NULL) dvr_mux_cache_set(cfg,atoi(s)); if((s = http_arg_get(&hc->hc_req_args, "postproc")) != NULL) @@ -1197,11 +1201,11 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque) if((s = http_arg_get(&hc->hc_req_args, "retention")) != NULL) dvr_retention_set(cfg,atoi(s)); - if((s = http_arg_get(&hc->hc_req_args, "preExtraTime")) != NULL) - dvr_extra_time_pre_set(cfg,atoi(s)); + if((s = http_arg_get(&hc->hc_req_args, "preExtraTime")) != NULL) + dvr_extra_time_pre_set(cfg,atoi(s)); - if((s = http_arg_get(&hc->hc_req_args, "postExtraTime")) != NULL) - dvr_extra_time_post_set(cfg,atoi(s)); + if((s = http_arg_get(&hc->hc_req_args, "postExtraTime")) != NULL) + dvr_extra_time_post_set(cfg,atoi(s)); if(http_arg_get(&hc->hc_req_args, "dayDirs") != NULL) flags |= DVR_DIR_PER_DAY; diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index a4fc108c..2e4c83bf 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -27,6 +27,18 @@ tvheadend.containers = new Ext.data.JsonStore({ }); //For the cache configuration +tvheadend.charsets = new Ext.data.JsonStore({ + autoLoad: true, + root: 'entries', + fields: ['key', 'val'], + id: 'key', + url: 'api/intlconv/charsets', + baseParams: { + enum: 1 + } +}); + +//For the charset configuration tvheadend.caches = new Ext.data.JsonStore({ autoLoad: true, root: 'entries', @@ -784,7 +796,7 @@ tvheadend.dvrsettings = function() { var confreader = new Ext.data.JsonReader({ root: 'dvrSettings' }, ['storage', 'filePermissions', 'dirPermissions', 'postproc', 'retention', 'dayDirs', 'channelDirs', - 'channelInTitle', 'container', 'cache', 'dateInTitle', 'timeInTitle', + 'channelInTitle', 'container', 'cache', 'charset', 'dateInTitle', 'timeInTitle', 'preExtraTime', 'postExtraTime', 'whitespaceInTitle', 'titleDirs', 'episodeInTitle', 'cleanTitle', 'tagFiles', 'commSkip', 'subtitleInTitle', 'episodeBeforeDate', 'rewritePAT', 'rewritePMT']); @@ -880,6 +892,17 @@ tvheadend.dvrsettings = function() { name: 'filePermissions' }); + var charset = new Ext.form.ComboBox({ + store: tvheadend.charsets, + fieldLabel: 'Filename charset', + triggerAction: 'all', + displayField: 'val', + valueField: 'key', + editable: false, + width: 200, + hiddenName: 'charset' + }); + /* TO DO - Add 'override user umask?' option, then trigger fchmod in mkmux.c, muxer_pass.c after file created */ var PATrewrite = new Ext.form.Checkbox({ @@ -992,7 +1015,7 @@ tvheadend.dvrsettings = function() { autoHeight: true, collapsible: true, animCollapse: true, - items: [recordingPath, recordingPermissions, PATrewrite, PMTrewrite, tagMetadata, skipCommercials] + items: [recordingPath, recordingPermissions, charset, PATrewrite, PMTrewrite, tagMetadata, skipCommercials] }); /* Sub-Panel - Directory operations */ From 04d684687c849474b0c8109c8809a4bcac3d61e6 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 26 May 2014 21:48:51 +0200 Subject: [PATCH 5/6] dvr: remove duplicate clean title code, use '_' for the unknown chars --- src/dvr/dvr_db.c | 14 -------------- src/dvr/dvr_rec.c | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 147b6750..359c010f 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -188,7 +188,6 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) { struct tm tm; char buf[40]; - int i; dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name); if(cfg->dvr_flags & DVR_CHANNEL_IN_TITLE) @@ -236,19 +235,6 @@ dvr_make_title(char *output, size_t outlen, dvr_entry_t *de) ".", "S%02d", NULL, "E%02d", NULL); } } - - if(cfg->dvr_flags & DVR_CLEAN_TITLE) { - for (i=0;i122 || - output[i]==34 || - output[i]==39 || - output[i]==92 || - output[i]==58 - ) output[i]='_'; - } - } } static void diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index a227f709..6bbd4640 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -141,7 +141,7 @@ cleanup_filename(char *s, dvr_config_t *cfg) /* Do not create hidden files */ if (s[0] == '.') - s[0] = '-'; + s[0] = '_'; for (i = 0, len = strlen(s); i < len; i++) { @@ -155,7 +155,7 @@ cleanup_filename(char *s, dvr_config_t *cfg) else if((dvr_flags & DVR_CLEAN_TITLE) && ((s[i] < 32) || (s[i] > 122) || (strchr("/:\\<>|*?'\"", s[i]) != NULL))) - s[i] = '-'; + s[i] = '_'; } return s; From 52b1099f707c4e7cddff2dc065633acbd4a9f417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Karri=C3=A9?= Date: Mon, 26 May 2014 17:00:00 +0200 Subject: [PATCH 6/6] Add channel UUID to JSON response --- src/webui/extjs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 550cfbbb..907fe901 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1323,6 +1323,7 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque, htsmsg_add_str(m, "channel", DVR_CH_NAME(de)); if(de->de_channel != NULL) { + htsmsg_add_str(m, "channelid", channel_get_uuid(de->de_channel)); if (de->de_channel->ch_icon) htsmsg_add_imageurl(m, "chicon", "imagecache/%d", de->de_channel->ch_icon);