"Fossies" - the Fresh Open Source Software Archive

Member "whois/simple_recode.c" (26 Dec 2013, 5062 Bytes) of package /linux/privat/whois_5.5.9.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "simple_recode.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright 2009 by Marco d'Itri <md@linux.it>.
    3  *
    4  * simple_recode was inspired by a similar function found in Simon
    5  * Josefsson's libidn.
    6  *
    7  * This program is free software; you can redistribute it and/or modify
    8  * it under the terms of the GNU General Public License as published by
    9  * the Free Software Foundation; either version 2 of the License, or
   10  * (at your option) any later version.
   11  *
   12  * This program is distributed in the hope that it will be useful,
   13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15  * GNU General Public License for more details.
   16  *
   17  * You should have received a copy of the GNU General Public License along
   18  * with this program; if not, write to the Free Software Foundation, Inc.,
   19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   20  */
   21 
   22 #include <stdlib.h>
   23 #include <errno.h>
   24 #include <stdio.h>
   25 #include <string.h>
   26 #include <iconv.h>
   27 #include <langinfo.h>
   28 
   29 #include "utils.h"
   30 
   31 #include "simple_recode.h"
   32 
   33 /* Global variables */
   34 iconv_t simple_recode_iconv_handle;
   35 const char *simple_recode_input_charset;
   36 
   37 /*
   38  * These value should be tuned to an acceptable compromise between memory
   39  * usage and calling iconv(3) as few times as possible.
   40  */
   41 #define SIMPLE_RECODE_BUFFER_SIZE_1 256
   42 #define SIMPLE_RECODE_BUFFER_SIZE_2 1024
   43 #define SIMPLE_RECODE_BUFFER_INCREMENT 1
   44 
   45 /*
   46  * Convert a NULL-terminated string accordingly to the provided iconv(3)
   47  * handle. The returned string is allocated using malloc(3) and needs to be
   48  * deallocated by the caller.
   49  * Incomplete, invalid and impossible to recode sequences are copied as-is.
   50  * On failure, NULL is returned and errno is set.
   51  */
   52 char *simple_recode(const iconv_t handle, const char *str)
   53 {
   54     char *inp = (char *) str;
   55     char *outp, *result;
   56     size_t inbytes_remaining, outbytes_remaining, outbuf_size;
   57 
   58     inbytes_remaining = strlen(inp);
   59     if (inbytes_remaining + 1 <= SIMPLE_RECODE_BUFFER_SIZE_1
   60         - (SIMPLE_RECODE_BUFFER_SIZE_1 >> SIMPLE_RECODE_BUFFER_INCREMENT))
   61     outbuf_size = SIMPLE_RECODE_BUFFER_SIZE_1;
   62     else
   63     outbuf_size = inbytes_remaining + 1
   64         + (inbytes_remaining >> SIMPLE_RECODE_BUFFER_INCREMENT);
   65 
   66     outp = result = malloc(outbuf_size);
   67     if (!result)
   68     return NULL;
   69     outbytes_remaining = outbuf_size - 1;
   70 
   71     do {
   72     size_t err = iconv(handle, &inp, &inbytes_remaining, &outp,
   73         &outbytes_remaining);
   74 
   75     if (err != -1)
   76         break; /* success */
   77 
   78     switch (errno) {
   79     case EINVAL:        /* incomplete multibyte sequence */
   80     case EILSEQ:        /* invalid multibyte sequence */
   81 #ifdef SIMPLE_RECODE_SKIP_INVALID_SEQUENCES
   82         /* recover from invalid input by replacing it with a '?' */
   83         inp++;
   84         *outp++ = '?';  /* use U+FFFD for unicode output? how? */
   85 #else
   86         /* garbage in, garbage out */
   87         *outp++ = *inp++;
   88 #endif
   89         inbytes_remaining--;
   90         outbytes_remaining--;
   91         continue;
   92 
   93     case E2BIG:
   94         {
   95         size_t used = outp - result;
   96         size_t newsize;
   97         char *new_result;
   98 
   99         if (outbuf_size < SIMPLE_RECODE_BUFFER_SIZE_2)
  100             newsize = SIMPLE_RECODE_BUFFER_SIZE_2;
  101         else
  102             newsize = outbuf_size
  103             + (outbuf_size >> SIMPLE_RECODE_BUFFER_INCREMENT);
  104 
  105         /* check if the newsize variable has overflowed */
  106         if (newsize <= outbuf_size) {
  107             free(result);
  108             errno = ENOMEM;
  109             return NULL;
  110         }
  111         outbuf_size = newsize;
  112         new_result = realloc(result, outbuf_size);
  113         if (!new_result) {
  114             free(result);
  115             return NULL;
  116         }
  117         result = new_result;
  118 
  119         /* update the position in the new output stream */
  120         outp = result + used;
  121         outbytes_remaining = outbuf_size - used - 1;
  122 
  123         continue;
  124         }
  125 
  126     default:
  127         free(result);
  128         return NULL;
  129     }
  130     } while (inbytes_remaining > 0);
  131 
  132     *outp = '\0';
  133 
  134     return result;
  135 }
  136 
  137 /*
  138  * Like fputs(3), but transparently recodes s using the global variable
  139  * simple_recode_input_charset as the input charset and the current locale
  140  * as the output charset.
  141  * If simple_recode_input_charset is NULL it just calls fputs(3).
  142  * Exits with an error if iconv(3) or iconv_open(3) fail.
  143  *
  144  * Assumes that setlocale(3) has already been called.
  145  *
  146  * If appropriate, the iconv object referenced by the global variable
  147  * simple_recode_iconv_handle should be deallocated with iconv_close(3).
  148  */
  149 int recode_fputs(const char *s, FILE *stream)
  150 {
  151     char *out;
  152     int result;
  153 
  154     if (simple_recode_input_charset == NULL)    /* no conversion is needed */
  155     return fputs(s, stream);
  156 
  157     if (simple_recode_iconv_handle == NULL) {
  158     simple_recode_iconv_handle = iconv_open(nl_langinfo(CODESET),
  159                      simple_recode_input_charset);
  160     if (simple_recode_iconv_handle == (iconv_t) - 1)
  161         err_sys("iconv_open");
  162     }
  163 
  164     out = simple_recode(simple_recode_iconv_handle, s);
  165     if (!out)
  166     err_sys("iconv");
  167     result = fputs(out, stream);
  168     free(out);
  169 
  170     return result;
  171 }
  172 
  173 void simple_recode_iconv_close(void)
  174 {
  175     if (simple_recode_iconv_handle == NULL)
  176     return;
  177 
  178     iconv_close(simple_recode_iconv_handle);
  179     simple_recode_iconv_handle = NULL;
  180     simple_recode_input_charset = NULL;
  181 }
  182