diff options
Diffstat (limited to 'ldap/clients/dsgw/emitf.c')
-rw-r--r-- | ldap/clients/dsgw/emitf.c | 860 |
1 files changed, 860 insertions, 0 deletions
diff --git a/ldap/clients/dsgw/emitf.c b/ldap/clients/dsgw/emitf.c new file mode 100644 index 00000000..b4e6c709 --- /dev/null +++ b/ldap/clients/dsgw/emitf.c @@ -0,0 +1,860 @@ +/** + * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to + * license terms. Copyright © 2001 Sun Microsystems, Inc. + * Some preexisting portions Copyright © 2001 Netscape Communications Corp. + * All rights reserved. + */ +#include <stdarg.h> /* va_list etc. */ +#include <stdio.h> /* sprintf */ +#include <stdlib.h> /* malloc, realloc, free */ +#include <string.h> /* strchr, strpbrk etc. */ +#include "dsgw.h" /* dsgw_ch_malloc, dsgw_ch_strdup */ + +typedef void* (*dsgw_producer) (void*, const char*, size_t); + +static size_t +produce_fill (dsgw_producer produce, void** parm, + size_t fill, unsigned zero) +{ + static const char* zeroes = "00000000"; + static const char* blanks = " "; + size_t result = 0; + while (fill > 0) { + long n = fill; + if (n > 8) n = 8; + if (zero) { + *parm = produce (*parm, zeroes, n); + } else { + *parm = produce (*parm, blanks, n); + } + if (*parm == NULL) return result; + result += n; + fill -= n; + } + return result; +} + +#define FLAG_LEFT 1 /* align left */ +#define FLAG_ZERO 2 /* zero fill */ +#define FLAG_CONST 4 + +static size_t +produce_string (dsgw_producer produce, void** parm, + const char* str, unsigned flags, int width, int precision) +{ + size_t fill; + size_t bytes; + size_t result = 0; + if (*parm == NULL) return result; + if (width < 0) { + width = - width; + flags ^= FLAG_LEFT; + } + if (width == 0 && precision < 0) { + fill = 0; + bytes = strlen (str); + } else { + char* s = (char*)str; /* cast away const (for LDAP_UTF8INC) */ + size_t chars = 0; + while (*s && ((precision < 0) || (chars < precision))) { + LDAP_UTF8INC(s); + ++chars; + } + fill = (width > chars) ? (width - chars) : 0; + bytes = (s - str); + } + if (fill && ! (flags & FLAG_LEFT)) { + result += produce_fill (produce, parm, fill, flags & FLAG_ZERO); + } + if (bytes) { + *parm = produce (*parm, str, bytes); + if (*parm == NULL) return result; + result += bytes; + } + if (fill && (flags & FLAG_LEFT)) { + result += produce_fill (produce, parm, fill, flags & FLAG_ZERO); + } + return result; +} + +static const char* type_chars = "%dioxXueEgGfcsp"; + +static size_t +count_slots (const char* s) +{ + size_t n = 0; + while ((s = strchr (s, '%')) != NULL) { + const char* l = strpbrk (s+1, type_chars); + const char* c; + if (l == NULL) { + n += 3; + break; + } + ++n; + for (c = s+1; c != l; ++c) { + if (*c == '*') ++n; + } + s = *l ? l+1 : l; + } + return n; +} + +typedef struct { + char type; +#define TYPE_I 0 +#define TYPE_U 1 +#define TYPE_F 2 +#define TYPE_LI 3 +#define TYPE_LU 4 +#define TYPE_LF 5 +#define TYPE_S 6 +#define TYPE_P 7 +#define TYPE_PERCENT 8 /* e.g. %% */ +#define TYPE_WIDTH 9 +#define TYPE_PRECISION 10 + + unsigned char flags; + int arg; /* An index into an array of dsgw_arg_t, + or (if flags & FLAG_CONST) the width or precision value. */ +} dsgw_slot_t; + +typedef union { + int i; + unsigned int u; + double f; + long li; + unsigned long lu; + long double lf; + const char* s; + void* p; +} dsgw_arg_t; + +#define DEFSLOTC 8 /* A format string rarely contains more slots. */ +#define DEFFMTC 16 /* A single format rarely contains more chars. */ + +static size_t +dsgw_vxprintf (dsgw_producer produce, void* parm, + const char* format, va_list argl) + /* This function works like vsprintf(), except it: + - supports parameter reordering, using %posp$. + - is UTF8-aware. + - delivers output by calling the function 'produce'. + - returns the total number of bytes produced. + This function interprets all string parameters as UTF8. + */ +{ + size_t result = 0; /* total number of bytes produced */ + + /* Each place that 'format' refers to an argument is called a 'slot'. */ + dsgw_slot_t defslot[DEFSLOTC]; + dsgw_slot_t* slot = defslot; /* in order of their appearance in format */ + dsgw_slot_t* islot = NULL; /* next slot to process */ + dsgw_slot_t* aslot = NULL; /* another cursor */ + + dsgw_arg_t defargv[DEFSLOTC]; + dsgw_arg_t* argv = defargv; /* in order of their appearance in argl */ + size_t argi = 0; /* index of next argument (in argl/argv) */ + + char deffmt[DEFFMTC]; + char* fmt = deffmt; + size_t fmtc = DEFFMTC; + + const char* next; + const char* f; + + char buf [1024]; + int i; + + i = count_slots (format); +/*fprintf (stderr, "slots: %i\n", i);*/ + if (i > DEFSLOTC) { /* defslot isn't big enough. */ + slot = (dsgw_slot_t*) malloc (i * sizeof(dsgw_slot_t)); + } + + /* get slot types from format: */ + islot = slot; + next = format; + while ((f = strchr (next, '%')) != NULL) { + const char* l = f+1; + unsigned flags = 0; + int number = -1; + char size; + + if (*l >= '1' && *l <= '9') { + number = 0; + do { number = (number * 10) + (*l++ - '0'); + } while (*l >= '0' && *l <= '9'); + } + if (*l == '$') { + ++l; + if (number > 0) { + argi = number - 1; + } + number = -1; + } + if (number >= 0) { /* width */ + islot->arg = number; + flags |= FLAG_CONST; + } else { + while (1) { /* flags */ + switch (*l) { + case '-': flags |= FLAG_LEFT; ++l; continue; + case '0': flags |= FLAG_ZERO; ++l; continue; + case '+': + case ' ': + case '#': ++l; continue; + default: break; + } + break; + } + if (*l == '*') { /* width */ + number = 0; + ++l; + islot->arg = argi++; + } else if (*l >= '1' && *l <= '9') { /* width */ + number = 0; + do { number = (number * 10) + (*l++ - '0'); + } while (*l >= '0' && *l <= '9'); + islot->arg = number; + flags |= FLAG_CONST; + } + } + if (number >= 0) { + islot->type = TYPE_WIDTH; + islot->flags = flags; + flags &= ~ FLAG_CONST; + ++islot; + } + if (*l == '.') { + islot->type = TYPE_PRECISION; + ++l; + if (*l == '*') { + ++l; + islot->arg = argi++; + islot->flags = 0; + } else { + number = 0; + while (*l >= '0' && *l <= '9') + number = (number * 10) + (*l++ - '0'); + islot->arg = number; + islot->flags = FLAG_CONST; + } + ++islot; + } + switch (*l) { /* size modifier */ + case 'h': + case 'l': + case 'L': size = *l++; break; + default: size = '\0'; + } + islot->flags = 0; + switch (*l) { /* type */ + case 'd': + case 'i': islot->type = (size == 'l') ? TYPE_LI : TYPE_I; break; + case 'o': + case 'x': case 'X': + case 'u': islot->type = (size == 'l') ? TYPE_LU : TYPE_U; break; + case 'e': case 'E': + case 'g': case 'G': + case 'f': islot->type = (size == 'L') ? TYPE_LF : TYPE_F; break; + case 'c': islot->type = TYPE_I; break; + case 's': islot->type = TYPE_S; break; + case 'p': islot->type = TYPE_P; break; + case '%': islot->type = TYPE_PERCENT; + islot->flags = FLAG_CONST; break; + default: /* unknown type */ + goto bail; /* don't produce anything. */ + /* It might be more helpful to produce the slots up to + this one, and maybe output this format substring, too. + That way, someone reading the output might get a clue + what went wrong. + */ + } + if (islot->type != TYPE_PERCENT) { + islot->arg = argi++; + } + ++islot; + next = *l ? l+1 : l; + } + + /* argi = the length of argl/argv: */ + argi = 0; + for (aslot = slot; aslot != islot; ++aslot) { + if (argi <= aslot->arg && ! (aslot->flags & FLAG_CONST)) { + argi = aslot->arg + 1; + } + } + if (argi > DEFSLOTC) { /* defargv isn't big enough */ + argv = (dsgw_arg_t*) malloc (argi * sizeof(dsgw_arg_t)); + } + + /* copy arguments from argl to argv: */ +/*fprintf (stderr, "slot:type:value:");*/ + for (i = 0; i < argi; ++i) { + for (aslot = slot; aslot != islot; ++aslot) { + if ( ! (aslot->flags & FLAG_CONST) && aslot->arg == i) { + break; + } + } + if (aslot == islot) { /* No slot refers to this arg. */ + if (va_arg (argl, const char*)); /* Skip over it. */ + } else { +/*fprintf (stderr, " %i:%i", (int)(aslot-slot), aslot->type);*/ + switch (aslot->type) { + case TYPE_U: argv[i].u = va_arg (argl, unsigned); break; + case TYPE_F: argv[i].f = va_arg (argl, double); break; + case TYPE_LI: argv[i].li = va_arg (argl, long); break; + case TYPE_LU: argv[i].lu = va_arg (argl, unsigned long); break; + case TYPE_LF: argv[i].lf = va_arg (argl, long double); break; + case TYPE_P: argv[i].p = va_arg (argl, void*); break; + case TYPE_S: argv[i].s = va_arg (argl, const char*); +/*fprintf (stderr, ":\"%s\"", argv[i].s);*/ + break; + case TYPE_PERCENT: break; /* no arg */ + case TYPE_WIDTH: + case TYPE_PRECISION: + case TYPE_I: argv[i].i = va_arg (argl, int); +/*fprintf (stderr, ":%i", argv[i].i);*/ + do { + switch (aslot->type) { + case TYPE_WIDTH: + case TYPE_PRECISION: + if ( ! (aslot->flags & FLAG_CONST) && aslot->arg == i) { + aslot->arg = argv[i].i; + aslot->flags |= FLAG_CONST; + } + break; + default: break; + } + } while (++aslot != islot); + break; + } + } + } +/*fprintf (stderr, "\n");*/ + + /* produce output: */ + islot = slot; + next = format; + while (parm && (f = strchr (next, '%'))) { + const char* l = strpbrk (f+1, type_chars); + if (l == NULL) { + break; + } + if (parm && f != next) { /* produce the substring next..f-1 */ + const size_t n = (f - next); + parm = produce (parm, next, n); + if (parm) result += n; + } + next = l + 1; + { /* fmt = f..l */ + const char* dollar; + const size_t fc = (next - f); + if (fmtc <= fc) { + fmtc = fc + 1; + if (fmt == deffmt) fmt = malloc (fmtc); + else fmt = realloc (fmt, fmtc); + } + memcpy (fmt, f, fc); + fmt[fc] = '\0'; + if ((dollar = strchr (fmt, '$')) != NULL) { + /* remove posp$ from the beginning of fmt */ + memmove (fmt + 1, dollar + 1, fc - (dollar - fmt)); + } +/*fprintf (stderr, "fmt: \"%s\"\n", fmt);*/ + } + /* produce a single argument */ + switch (islot->type) { + case TYPE_I: PR_snprintf (buf, 1024, fmt, argv[islot->arg].i); break; + case TYPE_U: PR_snprintf (buf, 1024, fmt, argv[islot->arg].u); break; + case TYPE_F: PR_snprintf (buf, 1024, fmt, argv[islot->arg].f); break; + case TYPE_LI: PR_snprintf (buf, 1024, fmt, argv[islot->arg].li); break; + case TYPE_LU: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lu); break; + case TYPE_LF: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lf); break; + case TYPE_P: PR_snprintf (buf, 1024, fmt, argv[islot->arg].p); break; + case TYPE_WIDTH: + case TYPE_PRECISION: + switch ((++islot)->type) { + case TYPE_I: PR_snprintf (buf, 1024, fmt, argv[islot->arg].i); break; + case TYPE_U: PR_snprintf (buf, 1024, fmt, argv[islot->arg].u); break; + case TYPE_F: PR_snprintf (buf, 1024, fmt, argv[islot->arg].f); break; + case TYPE_LI: PR_snprintf (buf, 1024, fmt, argv[islot->arg].li); break; + case TYPE_LU: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lu); break; + case TYPE_LF: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lf); break; + case TYPE_P: PR_snprintf (buf, 1024, fmt, argv[islot->arg].p); break; + case TYPE_WIDTH: + case TYPE_PRECISION: + switch ((++islot)->type) { + case TYPE_I: PR_snprintf (buf, 1024, fmt, argv[islot->arg].i); break; + case TYPE_U: PR_snprintf (buf, 1024, fmt, argv[islot->arg].u); break; + case TYPE_F: PR_snprintf (buf, 1024, fmt, argv[islot->arg].f); break; + case TYPE_LI: PR_snprintf (buf, 1024, fmt, argv[islot->arg].li); break; + case TYPE_LU: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lu); break; + case TYPE_LF: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lf); break; + case TYPE_P: PR_snprintf (buf, 1024, fmt, argv[islot->arg].p); break; + case TYPE_WIDTH: + case TYPE_PRECISION: goto bail; /* how did this happen? */ + case TYPE_PERCENT: + case TYPE_S: /* with width and precision */ + result += produce_string (produce, &parm, + (islot->type == TYPE_S) ? argv[islot->arg].s : "%", + islot[-2].flags, islot[-2].arg, islot[-1].arg); + goto skip_buf; + } + break; + case TYPE_PERCENT: + case TYPE_S: /* with width or precision (not both) */ + if (islot[-1].type == TYPE_WIDTH) { + result += produce_string (produce, &parm, + (islot->type == TYPE_S) ? argv[islot->arg].s : "%", + islot[-1].flags, islot[-1].arg, -1); + } else { + result += produce_string (produce, &parm, + (islot->type == TYPE_S) ? argv[islot->arg].s : "%", + 0, 0, islot[-1].arg); + } + goto skip_buf; + } + break; + case TYPE_PERCENT: + case TYPE_S: /* with neither width nor precision */ + result += produce_string (produce, &parm, + (islot->type == TYPE_S) ? argv[islot->arg].s : "%", + 0, 0, -1); + goto skip_buf; + } + if (parm && *buf) { /* produce buf */ + const size_t n = strlen (buf); + parm = produce (parm, buf, n); + if (parm) result += n; + } + skip_buf: + ++islot; + } + if (parm && *next) { /* produce the remainder of format */ + const size_t n = strlen (next); + parm = produce (parm, next, n); + if (parm) result += n; + } + + bail: + if (fmt != deffmt) free (fmt); + if (argv != defargv) free (argv); + if (slot != defslot) free (slot); +/*fprintf (stderr, "------\n");*/ + return result; +} + +size_t +dsgw_fputn (FILE* f, const char* s, size_t n) +{ + auto const size_t result = + fwrite (s, sizeof(char), n, f); + dsgw_log_out (s, result); + return result; +} + +static const char* +strnbrk (const char* str, size_t n, const char* brk) +{ + for (; n > 0; ++str, --n) { + if (strchr (brk, *str)) { + return str; + } + } + return NULL; +} + +static int quotation_depth = 0; +static int quotation_type[4]; /* maximum depth */ +#define QUOTATION_JAVASCRIPT_ENDOFLINE 1 + +static size_t +dsgw_emitr (int depth, const char* s, size_t n) +{ + static const char* linebreak = "' +\n'"; + static const size_t linebreak_len = 5; + auto size_t result = 0; + if (n == 0) { + return 0; + } else if (depth == 0) { + return dsgw_fputn (stdout, s, n); + } + --depth; + switch (quotation_type[depth]) { + case QUOTATION_JAVASCRIPT: + case QUOTATION_JAVASCRIPT_MULTILINE: + case QUOTATION_JAVASCRIPT_ENDOFLINE: + { + auto const char* t; + for (t = s; (t = strnbrk (t, n, "'\\\n")) != NULL; ++t) { + switch (*t) { + case '\n': /* output \n */ + if (t != s) { + if (quotation_type[depth] == QUOTATION_JAVASCRIPT_ENDOFLINE) { + dsgw_emitr (depth, linebreak, linebreak_len); + } + result += dsgw_emitr (depth, s, t - s); + } + if (dsgw_emitr (depth, "\\n", 2) > 1) ++result; + if (quotation_type[depth] == QUOTATION_JAVASCRIPT_MULTILINE) { + quotation_type[depth] = QUOTATION_JAVASCRIPT_ENDOFLINE; + } + break; + default: /* insert \ */ + if (quotation_type[depth] == QUOTATION_JAVASCRIPT_ENDOFLINE) { + quotation_type[depth] = QUOTATION_JAVASCRIPT_MULTILINE; + dsgw_emitr (depth, linebreak, linebreak_len); + } + result += dsgw_emitr (depth, s, t - s); + dsgw_emitr (depth, "\\", 1); + result += dsgw_emitr (depth, t, 1); + break; + } + n -= (t - s) + 1; + s = t + 1; + } + } + if (n > 0 && + quotation_type[depth] == QUOTATION_JAVASCRIPT_ENDOFLINE) { + quotation_type[depth] = QUOTATION_JAVASCRIPT_MULTILINE; + dsgw_emitr (depth, linebreak, linebreak_len); + } + break; + default: + break; + } + if (n > 0) { + result += dsgw_emitr (depth, s, n); + } + return result; +} + +static size_t +dsgw_emitq (FILE* f, const char* s, size_t n) +{ + if (f == stdout && quotation_depth > 0) { + return dsgw_emitr (quotation_depth, s, n); + } + return dsgw_fputn (f, s, n); +} + +void +dsgw_quotation_begin (int kind) +{ + if (quotation_depth >= 4) exit (4); + switch (kind) { + case QUOTATION_JAVASCRIPT: + case QUOTATION_JAVASCRIPT_MULTILINE: + dsgw_emitq (stdout, "'", 1); + break; + default: + break; + } + quotation_type[quotation_depth++] = kind; +} + +void +dsgw_quotation_end() +{ + if (quotation_depth > 0) switch (quotation_type[--quotation_depth]) { + case QUOTATION_JAVASCRIPT: + case QUOTATION_JAVASCRIPT_MULTILINE: + case QUOTATION_JAVASCRIPT_ENDOFLINE: + dsgw_emitq (stdout, "'", 1); + break; + default: + break; + } +} + +int +dsgw_quote_emits (int kind, const char* s) +{ + int result; + dsgw_quotation_begin (kind); + result = dsgw_emits (s); + dsgw_quotation_end(); + return result; +} + +int +dsgw_quote_emitf (int kind, const char* format, ...) +{ + int result; + va_list argl; + va_start (argl, format); + dsgw_quotation_begin (kind); + result = dsgw_emitfv (format, argl); + dsgw_quotation_end(); + va_end (argl); + return result; +} + +static UConverter* emit_converter = NULL; + +/* given string is utf8 - emit_converter converts given string + to some natural language encoding requested by the client */ +void* +dsgw_emitn (void* parm, const char* s, size_t n) +{ + if (emit_converter == NULL) { + if (dsgw_emitq ((FILE*)parm, s, n) != n) { + return NULL; + } + } else { +#define CONVERT_BUFSIZE 2048 + char buf [CONVERT_BUFSIZE]; /* faster than malloc/free */ + char *bufptr = buf; + size_t len = 0; + size_t slen = 0; + UErrorCode err = U_ZERO_ERROR; + int result; + + do { + bufptr = buf; /* reset to beginning of buf */ + s += slen; /* advance pointer to next unconverted chars */ + /* convert as many chars from s as will fit in buf */ + result = dsgw_convert(DSGW_FROM_UTF8, emit_converter, + &bufptr, CONVERT_BUFSIZE, &len, + s, n, &slen, &err); + /* write the converted chars to the output */ + n = dsgw_emitq ((FILE*)parm, buf, len); + } while ((result == 0) && (n == len)); + + ucnv_reset (emit_converter); + if (n != len) { + return NULL; + } + } + return parm; +} + +#if 0 +static void +dsgw_convert (void* parm, const char* s, size_t n) + /* Transform the output, in a visually distinctive way. + This function is intended for testing, only. + */ +{ + while (parm && n > 0) { + const size_t len = LDAP_UTF8LEN(s); + if (len == 1 && *s >= '!' && *s <= '~') { /* ASCII */ + /* output the double-width variant of this character */ + unsigned c = (unsigned)*s - '!' + 0xFF01; + unsigned char buf[3]; + buf[2] = 0x80 | (c & 0x3F); c >>= 6; + buf[1] = 0x80 | (c & 0x3F); c >>= 6; + buf[0] = 0xE0 | (c & 0x0F); + parm = dsgw_emitn (parm, (char*)buf, 3); + } else { + parm = dsgw_emitn (parm, s, len); + } + if (parm) { + n -= len; + s += len; + } + } +} +#endif + +int +dsgw_emits (const char* s) + /* This function works like fputs(s, stdout), except it + converts from UTF8 to the client's preferred charset. + */ +{ + size_t n = strlen (s); + if (n > 0 && dsgw_emitn (stdout, s, n) == NULL) { + return EOF; + } + return n; +} + +int +dsgw_emitfv (const char* format, va_list argl) + /* This function works like vprintf(), except it: + - supports parameter reordering, using %posp$. + - is UTF8-aware. + - converts to the client's preferred charset. + This function interprets all string parameters as UTF8. + */ +{ + return( dsgw_vxprintf (dsgw_emitn, stdout, format, argl)); +} + +int +dsgw_emitf (const char* format, ...) +{ + int rc; + + va_list argl; + va_start (argl, format); + rc = dsgw_emitfv (format, argl); + va_end (argl); + + return( rc ); +} + +typedef struct struct_item_t { + char* i_val; + double i_q; +} item_t; + +static size_t +list_count (const char* list) +{ + const char* s; + size_t n = 1; + if (list == NULL || *list == '\0') return 0; + for (s = list - 1; (s = strchr (s + 1, ',')) != NULL; ++n); + return n; +} + +static item_t* +list_parse (char* slist, size_t items) +{ + char* s = slist; + item_t* item; + size_t i = 0; + if (items <= 0) return NULL; + item = (item_t*) dsgw_ch_malloc (items * sizeof(item_t)); + while (ldap_utf8isspace (s)) LDAP_UTF8INC(s); + while (s && *s) { + if (i >= items) exit (1); + item[i].i_q = 1.0; + item[i++].i_val = s; + if ((s = strchr (s, ',')) != NULL) { + *s = '\0'; + while (ldap_utf8isspace (LDAP_UTF8INC(s))); + } + } + if (i != items) exit (1); + for (i = 0; i < items; ++i) { + if ((s = strchr (item[i].i_val, ';')) != NULL) { + *s = '\0'; + do { + while (ldap_utf8isspace (LDAP_UTF8INC(s))); + if (*s == 'q' || *s == 'Q') { + while (ldap_utf8isspace (LDAP_UTF8INC(s))); + if (*s == '=') { + item[i].i_q = strtod(++s, &s); + } + } + } while ((s = strchr (s, ';')) != NULL); + } + /* Remove trailing whitespace from item[i].i_val: */ + s = item[i].i_val; + s += strlen (s); + while (ldap_utf8isspace (LDAP_UTF8DEC(s))); + s[1] = '\0'; +/*printf("%s;q=%.2f\n", item[i].i_val, item[i].i_q);*/ + } + return item; +} + +static void +list_sort (item_t item[], size_t items) +{ + /* This implementation is suboptimal, but adequate. */ + int sorted; + size_t i; + do { + sorted = 1; + for (i = 0; i+1 < items ; ++i) { + if (item[i].i_q < item[i+1].i_q) { /* swap i & i+1 */ + auto item_t temp; + memcpy (&temp, &item[i], sizeof(item_t)); + memcpy (&item[i], &item[i+1], sizeof(item_t)); + memcpy (&item[i+1], &temp, sizeof(item_t)); + sorted = 0; + } + } + } while ( ! sorted); +} + +int +is_UTF_8 (const char* charset) +{ + return charset != NULL && + (!strcasecmp (charset, UNICODE_ENCODING_UTF_8) || + !strcasecmp (charset, "UNICODE-1-1-UTF-8")); +} + +static int +charset_is_supported (char* s) +{ + UConverter* converter; + UErrorCode err = U_ZERO_ERROR; + if (is_UTF_8 (s)) { + return 1; + } + converter = ucnv_open (s, &err); + if (err == U_ZERO_ERROR) { + ucnv_close (converter); + return 1; + } + return 0; +} + +static char* +choose_charset (char* slist) + /* Return the best charset from the given list. */ +{ + const size_t items = list_count (slist); + char* sbuf; + item_t* item; + size_t i; + + if (items <= 0) return slist; + sbuf = dsgw_ch_strdup (slist); + item = list_parse (sbuf, items); + for (i = 0; i < items; ++i) { + if (is_UTF_8 (item[i].i_val)) { + break; /* choose this one */ + } + } + if (i >= items) { + list_sort (item, items); + for (i = 0; i < items; ++i) { + auto char* charset = item[i].i_val; + if (!strcmp ("*", charset)) { + i = items; /* choose UTF_8 */ + } else if (charset_is_supported (charset)) { + break; /* choose this one */ + } + } + } + if (i >= items) { + strcpy (sbuf, UNICODE_ENCODING_UTF_8); + } else if (sbuf != item[i].i_val) { + memmove (sbuf, item[i].i_val, strlen(item[i].i_val) + 1); + } + free (item); + return sbuf; +} + +char* +dsgw_emit_converts_to (char* charset) +{ + const char* target; + if (emit_converter != NULL) { + ucnv_close (emit_converter); + emit_converter = NULL; + } + if (charset) charset = choose_charset (charset); + if (charset && *charset) { + target = charset; + } else { + target = ISO_8859_1_ENCODING; + } + if ( ! is_UTF_8 (target)) { + UErrorCode err = U_ZERO_ERROR; + emit_converter = ucnv_open(target, &err); + if (err != U_ZERO_ERROR) { + emit_converter = NULL; + charset = UNICODE_ENCODING_UTF_8; + } + } + return charset; +} |