netscape-revival
lib/libi18n/cvchcode.c
/* -*- Mode: C; tab-width: 4 -*- */
/* cvchcode.c */
#include "intlpriv.h"
#include "xp.h"
#include "libi18n.h"
/*
This function return csid if it finds Meta charset info
Currently there are two way to specify Meta charset
1. <META HTTP-EQUIV=Content-Type CONTENT="text/html; charset=iso-8859-1">
2. <META charset=iso-8859-1>
Right now, it scans for charset
*/
PRIVATE int PeekMetaCharsetTag(CCCDataObject *obj, char *pSrc, int len)
{
char *p;
char charset[MAX_CSNAME+1];
int i, k;
int n;
char ch;
if (len > 1000) /* if block is big, we only try first 1000 byte */
len = 1000;
for (i = 0; i < len; i++, pSrc++)
{
if (*pSrc != '<')
continue;
pSrc ++ ;
i ++ ;
if ((i+13) > len) /* at least need more than 13 bytes */
break;
switch (*pSrc) {
case 'm':
case 'M':
if (strncasecomp(pSrc, "meta", 4) == 0)
{
pSrc = pSrc + 4;
i += 4;
n = len - i; /* set n here to make sure it won't go
out of block boundary */
for (p = pSrc; n > 0; p ++, n--)
{
if (*p == '>' || *p == '<')
break ;
if (n < 9)
{
n = 0;
break;
}
if ((n > 9) && (*p == 'c' || *p == 'C') && strncasecomp(p, "charset", 7) == 0)
{
p += 7;
n -= 7;
while ((n > 0) && (*p == ' ' || *p == '\t')) /* skip spaces */
{
p ++ ;
n --;
}
if (n <= 0 || *p != '=')
break;
p ++;
n --;
while ((n > 0) && (*p == ' ' || *p == '\t')) /* skip spaces */
{
n --;
p ++ ;
}
if (n <= 0)
break;
/* now we go to find real charset name and convert it to csid */
if (*p == '\"' || *p == '\'')
{
p ++ ;
n -- ;
for (k = 0; n > 0 && k < MAX_CSNAME && *p != '\'' && *p != '\"'; p++, k++, n--)
charset[k] = *p ;
charset[k] = '\0';
}
else
{
for (k = 0; n > 0 && k < MAX_CSNAME && *p != '\'' && *p != '\"' && *p != '>'; p++, k++, n--)
charset[k] = *p ;
charset[k] = '\0';
}
if (*charset && (n >= 0)) /* ftang : change from if (*charset && (n > 0)) */
{
int16 doc_csid = INTL_CharSetNameToID(charset);
if (doc_csid == CS_UNKNOWN)
{
doc_csid = CS_DEFAULT;
}
return doc_csid;
}
}
}
if (n == 0) /* no more data left */
return 0;
}
break;
case 'b':
case 'B': /* quit if we see <BODY ...> tag */
if (strncasecomp(pSrc, "body", 4) == 0)
return 0;
break;
case 'p':
case 'P': /* quit if we see <P ...> tag */
ch = *(pSrc + 1);
if (ch == '>' || ch == ' ' || ch == '\t')
return 0;
break;
}
}
return 0;
}
PRIVATE int
net_CharCodeConv( CCCDataObject *obj,
const unsigned char *buf, /* buffer for conversion */
int32 bufsz) /* buffer size in bytes */
{
unsigned char *tobuf;
int rv;
tobuf = (unsigned char *)obj->cvtfunc(obj, buf, bufsz);
if (tobuf) {
rv = (*(obj)->next_stream->put_block) (obj->next_stream->data_object,
(const char *)tobuf, obj->len);
if (tobuf != buf)
XP_FREE(tobuf);
return(rv);
} else {
return(obj->retval);
}
}
/* Null Char Code Conversion module -- pass unconverted data downstream */
PRIVATE int
net_NoCharCodeConv (CCCDataObject *obj, const char *s, int32 l)
{
return((*obj->next_stream->put_block)(obj->next_stream->data_object,s,l));
}
PRIVATE int
net_AutoCharCodeConv (CCCDataObject *obj, const char *s, int32 l)
{
int16 doc_csid;
unsigned char *tobuf = NULL;
int rv;
/* for debugging -- erik */
#if 0
{
static FILE *f = NULL;
if (!f)
{
f = fopen("/tmp/zzz", "w");
}
if (f && s && (l > 0))
{
(void) fwrite(s, 1, l, f);
}
}
#endif /* 0 */
if (obj->cvtfunc != NULL)
tobuf = (unsigned char *)obj->cvtfunc(obj, (unsigned char *)s, l);
else
{
doc_csid = PeekMetaCharsetTag(obj, (char *)s, l) ;
/* Use 1st stream data block to guess doc Japanese CSID. */
if (doc_csid == CS_ASCII)
{
obj->current_stream->put_block = (MKStreamWriteFunc) net_NoCharCodeConv;
return((*obj->next_stream->put_block)(obj->next_stream->data_object,s,l));
}
#ifndef XP_UNIX
if (doc_csid == CS_DEFAULT)
(void) INTL_GetCharCodeConverter(INTL_DefaultDocCharSetID(obj->current_stream->window_id), 0, obj);
else
(void) INTL_GetCharCodeConverter(doc_csid, 0, obj);
obj->current_stream->window_id->win_csid = obj->to_csid;
obj->current_stream->window_id->doc_csid = doc_csid;
#else /* !XP_UNIX */
if (doc_csid == CS_DEFAULT)
(void) INTL_GetCharCodeConverter(INTL_DefaultDocCharSetID(obj->current_stream->window_id),
obj->current_stream->window_id->win_csid, obj);
else
(void) INTL_GetCharCodeConverter(doc_csid,
obj->current_stream->window_id->win_csid, obj);
obj->current_stream->window_id->win_csid = obj->to_csid;
obj->current_stream->window_id->doc_csid = doc_csid;
#endif /* !XP_UNIX */
/* If no conversion needed, change put_block module for successive
* data blocks. For current data block, return unmodified buffer.
*/
if (obj->cvtfunc == NULL)
{
obj->current_stream->put_block = (MKStreamWriteFunc) net_NoCharCodeConv;
return((*obj->next_stream->put_block)(obj->next_stream->data_object,s,l));
}
/* For initial block, must call converter directly. Success calls
* to the converter will be called directly from net_CharCodeConv()
*/
}
if (tobuf == NULL)
tobuf = (unsigned char *)obj->cvtfunc(obj, (unsigned char *)s, l);
if (tobuf) {
rv = (*(obj)->next_stream->put_block) (obj->next_stream->data_object,
(const char *)tobuf, obj->len);
if (tobuf != (unsigned char*)s)
XP_FREE(tobuf);
return(rv);
} else {
return(obj->retval);
}
}
/* Auto Detect Japanese Char Code Conversion */
unsigned char *
autoJCCC (CCCDataObject *obj, const unsigned char *s, int32 l)
{
int doc_csid = 0;
/* Use 1st stream data block to guess doc Japanese CSID. */
doc_csid = detect_JCSID (obj->current_stream->window_id,(const unsigned char *) s, l);
if (doc_csid == CS_ASCII) { /* return s unconverted and */
obj->len = l;
return (unsigned char *)s; /* autodetect next block of stream data */
}
if (doc_csid == CS_DEFAULT) {
doc_csid = INTL_DefaultDocCharSetID(obj->current_stream->window_id) & ~CS_AUTO;
obj->current_stream->window_id->doc_csid = CS_DEFAULT;
} else {
obj->current_stream->window_id->doc_csid = (doc_csid | CS_AUTO);
}
/* Setup converter function for success streams data blocks */
(void) INTL_GetCharCodeConverter(doc_csid, obj->to_csid, obj);
/* If no conversion needed, change put_block module for successive
* data blocks. For current data block, return unmodified buffer.
*/
if (obj->cvtfunc == NULL) {
obj->current_stream->put_block = (MKStreamWriteFunc) net_NoCharCodeConv;
obj->len = l;
return((unsigned char *) s);
}
/* For initial block, must call converter directly. Success calls
* to the converter will be called directly from net_CharCodeConv()
*/
return (unsigned char *)obj->cvtfunc (obj, (const unsigned char *)s, l);
}
/* One-byte-to-one-byte Char Code Conversion module.
* Table driven. Table provided by FE.
*/
int
net_1to1CCC (CCCDataObject *obj, const unsigned char *s, int32 l)
{
(void) One2OneCCC (obj, s, l);
return((*obj->next_stream->put_block)(obj->next_stream->data_object,
(const char *)s, obj->len));
}
unsigned char *
One2OneCCC (CCCDataObject *obj, const unsigned char *s, int32 l)
{
char **cvthdl;
register unsigned char *cp;
char *pTable;
cvthdl = (char **)INTL_GetSingleByteTable(obj->from_csid, obj->to_csid,obj->cvtflag);
if (cvthdl != NULL)
{
pTable = INTL_LockTable(cvthdl);
for (cp = (unsigned char *)s; cp < (unsigned char *)s + l; cp++)
*cp = pTable[*cp];
}
obj->len = l;
INTL_FreeSingleByteTable(cvthdl);
return((unsigned char *)s);
}
/* is the stream ready for writing?
*/
PRIVATE unsigned int net_CvtCharCodeWriteReady (CCCDataObject * obj)
{
return(MAX_WRITE_READY); /* always ready for writing */
}
PRIVATE void net_CvtCharCodeComplete (CCCDataObject * obj)
{
/* pass downstream any uncoverted characters */
if (obj->uncvtbuf[0] != '\0')
(*obj->next_stream->put_block)(obj->next_stream->data_object,
(const char *)obj->uncvtbuf, strlen((char *)obj->uncvtbuf));
(*obj->next_stream->complete)(obj->next_stream->data_object);
XP_FREE(obj->next_stream);
XP_FREE(obj);
return;
}
PRIVATE void net_CvtCharCodeAbort (CCCDataObject * obj, int status)
{
(*obj->next_stream->abort)(obj->next_stream->data_object, status);
XP_FREE(obj->next_stream);
XP_FREE(obj);
return;
}
PUBLIC NET_StreamClass *
INTL_ConvCharCode (int format_out,
void *data_obj,
URL_Struct *URL_s,
MWContext *window_id)
{
CCCDataObject *obj;
NET_StreamClass *stream;
register char *cp1, *cp2;
TRACEMSG(("Setting up display stream. Have URL: %s\n", URL_s->address));
stream = XP_NEW_ZAP(NET_StreamClass);
if(stream == NULL)
return(NULL);
stream->name = "CharCodeConverter";
stream->complete = (MKStreamCompleteFunc) net_CvtCharCodeComplete;
stream->abort = (MKStreamAbortFunc) net_CvtCharCodeAbort;
stream->is_write_ready = (MKStreamWriteReadyFunc) net_CvtCharCodeWriteReady;
stream->window_id = window_id;
obj = XP_NEW_ZAP(CCCDataObject);
if (obj == NULL)
return(NULL);
stream->data_object = obj; /* document info object */
obj->current_stream = stream;
/* Legitimate charset names must be in ASCII and case insensitive */
cp1 = URL_s->charset;
if (cp1)
{
cp2 = (char *)XP_ALLOC(XP_STRLEN((char *)cp1)+1);
if (cp2 == NULL)
{
window_id->mime_charset = NULL;
window_id->doc_csid = INTL_CharSetNameToID(URL_s->charset);
}
else
{
window_id->mime_charset = cp2;
while (*cp1 != '\0') {
*cp2 = tolower(*cp1);
cp1++; cp2++;
}
*cp2 = '\0';
window_id->doc_csid = INTL_CharSetNameToID(window_id->mime_charset);
}
/*
* if this is an unknown charset, set doc_csid to CS_DEFAULT
* so that the user can override using charset menu -- erik
*/
if (window_id->doc_csid == CS_UNKNOWN)
{
window_id->doc_csid = CS_DEFAULT;
}
/* for debugging -- erik */
#if 0
{
extern FILE *real_stderr;
(void) fprintf(real_stderr, "url charset %s\n",
window_id->mime_charset);
}
#endif /* 0 */
}
else if (window_id->relayout == METACHARSET_FORCERELAYOUT && window_id->doc_csid != CS_DEFAULT)
{
window_id->relayout = METACHARSET_RELAYOUTDONE;
}
else
{
window_id->relayout = METACHARSET_NONE;
window_id->mime_charset = NULL;
#ifdef XP_UNIX
/*
* The PostScript and Text FEs inherit the doc_csid from
* the parent context in order to pick up the per-window
* default. Apparently, this already works for some
* reason on Windows. Don't know about Mac. -- erik
*/
if ((window_id->type != MWContextPostScript) &&
(window_id->type != MWContextText))
#endif /* XP_UNIX */
window_id->doc_csid = CS_DEFAULT;
}
#ifdef XP_UNIX
{
#ifdef OLD_CODE
extern void XFE_getlocale (int16 charset, MWContext *context);
XFE_getlocale(0, window_id);
window_id->win_csid = INTL_DefaultWinCharSetID();
#else /* OLD_CODE */
window_id->win_csid = INTL_DocToWinCharSetID(window_id->doc_csid ?
window_id->doc_csid : INTL_DefaultDocCharSetID(window_id));
#endif /* OLD_CODE */
}
#endif /* XP_UNIX */
if (window_id->doc_csid == CS_DEFAULT || window_id->doc_csid == CS_UNKNOWN)
{
stream->put_block = (MKStreamWriteFunc) net_AutoCharCodeConv;
}
else
{
#ifndef XP_UNIX
(void) INTL_GetCharCodeConverter(window_id->doc_csid, 0, obj);
window_id->win_csid = obj->to_csid;
#else /* !XP_UNIX */
(void) INTL_GetCharCodeConverter(window_id->doc_csid,
window_id->win_csid, obj);
window_id->win_csid = obj->to_csid;
#endif /* !XP_UNIX */
if (obj->cvtfunc == NULL)
stream->put_block = (MKStreamWriteFunc) net_NoCharCodeConv;
else if (obj->cvtfunc == (CCCFunc)One2OneCCC)
stream->put_block = (MKStreamWriteFunc) net_1to1CCC;
else
stream->put_block = (MKStreamWriteFunc) net_CharCodeConv;
}
TRACEMSG(("Returning stream from NET_CvtCharCodeConverter\n"));
/* remap content type to be to INTERNAL_PARSER
*/
StrAllocCopy(URL_s->content_type, INTERNAL_PARSER);
obj->next_stream = NET_StreamBuilder(format_out, URL_s, window_id);
if(!obj->next_stream)
{
XP_FREE(obj);
XP_FREE(stream);
return(NULL);
}
return stream;
}
/*
INTL_ConvWinToMailCharCode
Converts 8bit encoding to 7bit mail encoding. It decides which 7bit and 8bit encoding
to use based on current default language.
input:
char *pSrc; // Source display buffer
output:
char *pDest; // Destination buffer
// pDest == NULL means either conversion fail
// or does OneToOne conversion
*/
PUBLIC
unsigned char *INTL_ConvWinToMailCharCode(MWContext *context, unsigned char *pSrc, uint32 block_size)
{
CCCDataObject *obj;
unsigned char *pDest;
int16 wincsid;
obj = XP_NEW_ZAP(CCCDataObject);
if (obj == NULL)
return NULL;
wincsid = INTL_DefaultWinCharSetID(context);
/* Converts 8bit Window codeset to 7bit Mail Codeset. */
(void) INTL_GetCharCodeConverter(wincsid,
INTL_DefaultMailCharSetID(wincsid),
obj);
if (obj->cvtfunc)
pDest = (unsigned char *)obj->cvtfunc(obj, pSrc, block_size);
else
pDest = NULL ;
XP_FREE(obj);
if (pDest == pSrc) /* converted to input buffer */
{ /* no additional memory been allocated */
return NULL;
}
return pDest ;
}
/*
INTL_ConvMailToWinCharCode
Converts mail encoding to display charset which is used by current window.
It decides which Display charset to use based on current default language.
input:
MWContext *context; the context (window ID)
char *pSrc; // Source buffer
uint32 block_size; the length of the source buffer
output:
char *pDest; // Destination buffer
// pDest == NULL means either conversion fail
// or does OneToOne conversion
*/
PUBLIC
unsigned char *INTL_ConvMailToWinCharCode(MWContext *context,
unsigned char *pSrc, uint32 block_size)
{
CCCDataObject *obj;
unsigned char *pDest;
NET_StreamClass stream;
obj = XP_NEW_ZAP(CCCDataObject);
if (obj == NULL)
return 0;
(void) memset(&stream, 0, sizeof(stream));
stream.window_id = context;
obj->current_stream = &stream;
/* Converts 7bit news codeset to 8bit windows Codeset. */
(void) INTL_GetCharCodeConverter(INTL_DefaultDocCharSetID(context), 0, obj);
if (obj->cvtfunc)
pDest = (unsigned char *)obj->cvtfunc(obj, pSrc, block_size);
else
pDest = NULL ;
XP_FREE(obj);
if (pSrc == pDest)
return NULL ;
return pDest ;
}
PUBLIC
CCCDataObject *
INTL_CreateDocToMailConverter(MWContext *context, XP_Bool isHTML, unsigned char *buffer, uint32 buffer_size)
{
CCCDataObject* selfObj;
int16 p_doc_csid = CS_DEFAULT;
/* Ok!! let's create the object here
We need the conversion obj to keep the state information */
if((selfObj = XP_NEW_ZAP(CCCDataObject))==NULL)
return NULL;
/* First, let's determine the from_csid and to_csid */
/* It is a TEXT_HTML !!! Let's use our PeekMetaCharsetTag to get a csid */
if(isHTML)
p_doc_csid = PeekMetaCharsetTag(selfObj, (char*) buffer, buffer_size);
if(p_doc_csid == CS_DEFAULT)
{
/* got default, try to get the doc_csid from context */
if((context == 0 ) || (context->doc_csid == CS_DEFAULT))
p_doc_csid = INTL_DefaultDocCharSetID(context);
else
p_doc_csid = context->doc_csid;
/* The doc_csid from the context (or default) has CS_AUTO bit. */
/* So let's try to call the auto detection function */
if(p_doc_csid & CS_AUTO)
p_doc_csid = detect_JCSID (context, buffer, buffer_size);
}
/* Now, we get the converter */
(void) INTL_GetCharCodeConverter(p_doc_csid,
INTL_DefaultMailCharSetID(p_doc_csid),
selfObj);
/* If the cvtfunc == NULL, we don't need to do conversion */
if(! (selfObj->cvtfunc) )
{
XP_FREE(selfObj);
return NULL;
}
else
return selfObj;
}