/* Copyright (c) 2003, WebThing Ltd Author: Nick Kew 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef TRANSCODER #define TRANSCODER #define OLEN 8192 #include #include class Transcoder { //FILE* fd ; //request_rec* r ; apr_file_t* fd ; iconv_t conv ; bool err ; // char* save_buf ; char* obuf ; char save_buf[64] ; size_t save_ptr ; const char* enc ; public: /* NULL transcoder */ Transcoder(apr_file_t* f) : fd(f), conv(0), err(false), obuf(0), save_ptr(0), enc(0) { } ~Transcoder() { close() ; } void close() { //apr_file_close(fd) ; if ( conv ) { iconv_close(conv) ; conv = 0 ; } if ( obuf ) { delete [] obuf ; obuf = 0 ; } } const bool errors() const { return err ; } const char* set_encoding(const char* encoding) { static char encbuf[40] ; putenv("SP_CHARSET_FIXED=1") ; //putenv(apr_pstrcat(pool, "SP_ENCODING=", enc, NULL)) ; sprintf(encbuf,"SP_ENCODING=%s", enc) ; putenv(encbuf) ; return setEncoding(encoding) ; } const char* encoding() const { return enc ; } const char* setEncoding(const char* encoding) { enc = encoding ; if ( ! enc ) return enc ; if ( conv ) iconv_close(conv) ; /* Cases we definitely don't need to touch */ if ( !strcasecmp(encoding, "utf-8") || !strcasecmp(encoding, "utf8") || !strcasecmp(encoding, "ASCII") || !strcasecmp(encoding, "US-ASCII") ) { conv = 0 ; return encoding ; } /* Other cases transcode to utf-8 */ conv = iconv_open("utf-8", encoding) ; /* If we can't, then leave it alone; let Parser try & deal with it */ if ( conv == (iconv_t)(-1) ) { err = true ; conv = 0 ; } if ( conv ) { if ( ! obuf ) obuf = new char[OLEN] ; return "utf-8" ; } else return encoding ; } /* return bytes remaining (if any) */ void write(const char* buf, size_t count) { if ( conv ) { size_t ilen = count ; const char* ibuf = buf ; bool done = false ; size_t consumed = 0 ; while ( save_ptr > 0 ) { // consume saved bytes with the start of the new buffer ; if ( consumed >= count ) return ; save_buf[save_ptr++] = buf[consumed++] ; const char* s = save_buf ; size_t oleft = OLEN ; if ( iconv(conv, &s, &save_ptr, &obuf, &oleft) == (size_t)(-1)) { switch (errno) { case EILSEQ: // invalid char - discard err = true ; save_ptr = 0 ; break ; case EINVAL: // incomplete; need another byte break ; case E2BIG: // bug::can't happen; input too small to overflow output err = true ; save_ptr = 0 ; break ; } } size_t olen = OLEN - oleft ; obuf -= olen ; apr_file_write(fd, obuf, &olen) ; if ( ( olen > 0 ) && ( save_ptr > 0 ) ) { err = true ; save_ptr = 0 ; } if ( save_ptr > 16 ) { err = true ; save_ptr = 0 ; } } ibuf += consumed ; ilen -= consumed ; while ( ! done ) { // char obuf[OLEN] ; // char* obuf = (char*)malloc(OLEN) ; size_t oleft = OLEN ; size_t res = iconv(conv, &ibuf, &ilen, &obuf, &oleft) ; size_t olen = OLEN - oleft ; obuf -= olen ; apr_file_write(fd, obuf, &olen) ; if ( res == ( size_t)(-1) ) { switch ( errno ) { case EILSEQ: // step over invalid byte err = true ; ++ibuf ; --ilen ; break ; case EINVAL: // inbuf ended in the middle of something done = true ; break ; case E2BIG: // need to go round the loop again break ; default: err = true ; break ; } } else if ( olen == 0 ) done = true ; // free(obuf) ; } //if ( sbuf ) // free ( sbuf ) ; save_ptr = 0 ; if ( ilen > 0 ) { if ( ilen < 15 ) { save_ptr = ilen ; memmove(save_buf, buf+count-ilen, ilen) ; } else { err = true ; } } } else { apr_file_write(fd, buf, &count) ; } } } ; #endif