805 lines
13 KiB
C
805 lines
13 KiB
C
/* $Id$
|
|
|
|
Part of SWI-Prolog
|
|
|
|
Author: Jan Wielemaker
|
|
E-mail: jan@swi.psy.uva.nl
|
|
WWW: http://www.swi-prolog.org
|
|
Copyright (C): 1985-2002, University of Amsterdam
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#define _ISOC99_SOURCE 1 /* fwprintf(), etc prototypes */
|
|
|
|
#define UTIL_H_IMPLEMENTATION
|
|
#include "util.h"
|
|
#include <ctype.h>
|
|
#include <wctype.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_MALLOC_H
|
|
#include <malloc.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_IO_H
|
|
#include <io.h>
|
|
#endif
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
#include "utf8.h"
|
|
|
|
size_t
|
|
istrlen(const ichar *s)
|
|
{ size_t len =0;
|
|
|
|
while(*s++)
|
|
len++;
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrdup(const ichar *s)
|
|
{ if ( s )
|
|
{ ichar *dup = sgml_malloc((istrlen(s)+1)*sizeof(ichar));
|
|
ichar *d = dup;
|
|
|
|
while(*s)
|
|
*d++ = *s++;
|
|
*d = 0;
|
|
|
|
return dup;
|
|
} else
|
|
{ return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrndup(const ichar *s, int len)
|
|
{ ichar *dup = sgml_malloc((len+1)*sizeof(ichar));
|
|
ichar *d = dup;
|
|
|
|
while(--len >= 0)
|
|
*d++ = *s++;
|
|
*d = 0;
|
|
|
|
return dup;
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrcpy(ichar *d, const ichar *s)
|
|
{ ichar *r = d;
|
|
|
|
while(*s)
|
|
*d++ = *s++;
|
|
*d = 0;
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrcat(ichar *d, const ichar *s)
|
|
{ ichar *r = d;
|
|
|
|
d += istrlen(d);
|
|
istrcpy(d, s);
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrncpy(ichar *d, const ichar *s, size_t len)
|
|
{ ichar *r = d;
|
|
|
|
while(*s && len-- > 0)
|
|
*d++ = *s++;
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
istrcaseeq(const ichar *s1, const ichar *s2)
|
|
{ ichar c;
|
|
|
|
while ((c = *s1++) != '\0')
|
|
{ if (towlower(*s2++) != towlower(c))
|
|
return FALSE;
|
|
}
|
|
|
|
return *s2 == '\0';
|
|
}
|
|
|
|
|
|
int
|
|
istreq(const ichar *s1, const ichar *s2)
|
|
{ while(*s1 && *s1 == *s2)
|
|
s1++, s2++;
|
|
|
|
if ( *s1 == 0 && *s2 == 0 )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
istrncaseeq(const ichar *s1, const ichar *s2, int len)
|
|
{ while(--len >= 0 && towlower(*s1) == towlower(*s2))
|
|
s1++, s2++;
|
|
|
|
if ( len < 0 )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
istrprefix(const ichar *pref, const ichar *s)
|
|
{ while(*pref && *pref == *s)
|
|
pref++, s++;
|
|
|
|
if ( *pref == 0 )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrchr(const ichar *s, int c)
|
|
{ for( ; *s; s++ )
|
|
{ if ( c == *s )
|
|
return (ichar *)s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrupper(ichar *s)
|
|
{ ichar *r = s;
|
|
|
|
for( ; *s; s++)
|
|
*s = toupper(*s);
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
ichar *
|
|
istrlower(ichar *s)
|
|
{ ichar *r = s;
|
|
|
|
for( ; *s; s++)
|
|
*s = towlower(*s);
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
int
|
|
istrhash(const ichar *t, int tsize)
|
|
{ unsigned int value = 0;
|
|
unsigned int shift = 5;
|
|
|
|
while(*t)
|
|
{ unsigned int c = *t++;
|
|
|
|
c -= 'a';
|
|
value ^= c << (shift & 0xf);
|
|
shift ^= c;
|
|
}
|
|
|
|
value = value ^ (value >> 16);
|
|
|
|
return value % tsize;
|
|
}
|
|
|
|
|
|
int
|
|
istrcasehash(const ichar *t, int tsize)
|
|
{ unsigned int value = 0;
|
|
unsigned int shift = 5;
|
|
|
|
while(*t)
|
|
{ unsigned int c = towlower(*t++); /* case insensitive */
|
|
|
|
c -= 'a';
|
|
value ^= c << (shift & 0xf);
|
|
shift ^= c;
|
|
}
|
|
|
|
value = value ^ (value >> 16);
|
|
|
|
return value % tsize;
|
|
}
|
|
|
|
|
|
int
|
|
istrtol(const ichar *s, long *val)
|
|
{ long v;
|
|
ichar *e;
|
|
|
|
if ( *s )
|
|
{ v = wcstol(s, &e, 10);
|
|
if ( !e[0] && errno != ERANGE )
|
|
{ *val = v;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*******************************
|
|
* INPUT CHARACTER BUFFER *
|
|
*******************************/
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Input character buffer is used to collect data between SGML markup, such
|
|
as <...>
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
icharbuf *
|
|
new_icharbuf()
|
|
{ icharbuf *buf = sgml_malloc(sizeof(*buf));
|
|
|
|
buf->allocated = 0;
|
|
buf->size = 0;
|
|
buf->data = NULL;
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
void
|
|
free_icharbuf(icharbuf *buf)
|
|
{ if ( buf->data )
|
|
sgml_free(buf->data);
|
|
|
|
sgml_free(buf);
|
|
}
|
|
|
|
|
|
void
|
|
__add_icharbuf(icharbuf *buf, int chr)
|
|
{ if ( buf->size == buf->allocated )
|
|
{ buf->allocated = (buf->allocated ? buf->allocated*2 : 128);
|
|
|
|
if ( buf->data )
|
|
buf->data = sgml_realloc(buf->data, buf->allocated*sizeof(ichar));
|
|
else
|
|
buf->data = sgml_malloc(buf->allocated*sizeof(ichar));
|
|
}
|
|
|
|
buf->data[buf->size++] = chr;
|
|
}
|
|
|
|
|
|
void
|
|
del_icharbuf(icharbuf *buf)
|
|
{ if ( buf->size > 0 )
|
|
buf->size--;
|
|
}
|
|
|
|
|
|
void
|
|
terminate_icharbuf(icharbuf *buf)
|
|
{ add_icharbuf(buf, '\0');
|
|
buf->size--;
|
|
}
|
|
|
|
|
|
void
|
|
empty_icharbuf(icharbuf *buf)
|
|
{ buf->size = 0;
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* OUTPUT CHARACTER BUFFER *
|
|
*******************************/
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Output character buffer deals with two representations: ISO Latin-1 and
|
|
UCS. It starts life as ISO Latin-1 and is upgraded to UCS as the first
|
|
character that doesn't fit ISO Latin-1 is added to the buffer.
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
ocharbuf *
|
|
init_ocharbuf(ocharbuf *buf)
|
|
{ buf->size = 0;
|
|
buf->allocated = sizeof(buf->localbuf)/sizeof(wchar_t);
|
|
buf->data.w = buf->localbuf;
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
ocharbuf *
|
|
new_ocharbuf()
|
|
{ ocharbuf *buf = sgml_malloc(sizeof(*buf));
|
|
|
|
return init_ocharbuf(buf);
|
|
}
|
|
|
|
|
|
void
|
|
free_ocharbuf(ocharbuf *buf)
|
|
{ if ( buf->data.w && buf->data.w != buf->localbuf )
|
|
sgml_free(buf->data.w);
|
|
|
|
sgml_free(buf);
|
|
}
|
|
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Make sure the data of the buffer is malloc'ed and nul-terminated.
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
ocharbuf *
|
|
malloc_ocharbuf(ocharbuf *buf)
|
|
{ if ( buf->data.w == buf->localbuf )
|
|
{ int bytes = (buf->size+1) * sizeof(wchar_t);
|
|
|
|
buf->data.w = sgml_malloc(bytes);
|
|
memcpy(buf->data.w, buf->localbuf, bytes);
|
|
buf->data.w[buf->size] = 0;
|
|
} else
|
|
terminate_ocharbuf(buf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
void
|
|
add_ocharbuf(ocharbuf *buf, int chr)
|
|
{ if ( buf->size == buf->allocated )
|
|
{ buf->allocated *= 2;
|
|
|
|
if ( buf->data.w != (wchar_t*)buf->localbuf )
|
|
{ buf->data.w = sgml_realloc(buf->data.w, buf->allocated*sizeof(wchar_t));
|
|
} else
|
|
{ buf->data.w = sgml_malloc(buf->allocated*sizeof(wchar_t));
|
|
memcpy(buf->data.w, buf->localbuf, sizeof(buf->localbuf));
|
|
}
|
|
}
|
|
buf->data.w[buf->size++] = chr;
|
|
}
|
|
|
|
|
|
void
|
|
del_ocharbuf(ocharbuf *buf)
|
|
{ if ( buf->size > 0 )
|
|
buf->size--;
|
|
}
|
|
|
|
|
|
void
|
|
terminate_ocharbuf(ocharbuf *buf)
|
|
{ add_ocharbuf(buf, '\0');
|
|
buf->size--;
|
|
}
|
|
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
empty_ocharbuf() frees the associated buffer after a big lump has been
|
|
in it. Otherwise it simply sets the size to 0.
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
void
|
|
empty_ocharbuf(ocharbuf *buf)
|
|
{ buf->size = 0;
|
|
|
|
if ( buf->allocated > 8192 )
|
|
{ assert(buf->data.w != buf->localbuf);
|
|
sgml_free(buf->data.w);
|
|
|
|
buf->allocated = sizeof(buf->localbuf)/sizeof(wchar_t);
|
|
buf->data.w = buf->localbuf;
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* BUFFER RING *
|
|
*******************************/
|
|
|
|
#define RINGSIZE 16
|
|
|
|
typedef struct ring
|
|
{ void *ring[RINGSIZE];
|
|
int ringp;
|
|
} ring;
|
|
|
|
#ifdef _REENTRANT
|
|
#include <pthread.h>
|
|
static pthread_key_t ring_key;
|
|
|
|
static void
|
|
free_ring(void *ptr)
|
|
{ ring *r = ptr;
|
|
int i;
|
|
void **bp;
|
|
|
|
for(i=0, bp=r->ring; i<RINGSIZE; i++, bp++)
|
|
{ if ( *bp )
|
|
{ sgml_free(*bp);
|
|
*bp = NULL;
|
|
}
|
|
}
|
|
|
|
sgml_free(r);
|
|
}
|
|
|
|
|
|
static ring *
|
|
my_ring()
|
|
{ ring *r;
|
|
|
|
if ( (r=pthread_getspecific(ring_key)) )
|
|
return r;
|
|
|
|
if ( (r = sgml_calloc(1, sizeof(*r))) )
|
|
pthread_setspecific(ring_key, r);
|
|
|
|
return r;
|
|
}
|
|
|
|
void
|
|
init_ring(void)
|
|
{ pthread_key_create(&ring_key, free_ring);
|
|
}
|
|
|
|
#else
|
|
static ring ring_store;
|
|
#define my_ring() (&ring_store)
|
|
|
|
void init_ring(void) {}
|
|
#endif
|
|
|
|
|
|
wchar_t *
|
|
str2ring(const wchar_t *in)
|
|
{ ring *r;
|
|
wchar_t *copy;
|
|
|
|
if ( !(r=my_ring()) ||
|
|
!(copy = sgml_malloc((wcslen(in)+1)*sizeof(wchar_t))) )
|
|
{ sgml_nomem();
|
|
return NULL;
|
|
}
|
|
|
|
wcscpy(copy, in);
|
|
if ( r->ring[r->ringp] )
|
|
sgml_free(r->ring[r->ringp]);
|
|
r->ring[r->ringp++] = copy;
|
|
if ( r->ringp == RINGSIZE )
|
|
r->ringp = 0;
|
|
|
|
return copy;
|
|
}
|
|
|
|
|
|
void *
|
|
ringallo(size_t size)
|
|
{ ring *r;
|
|
char *result;
|
|
|
|
if ( !(r=my_ring()) || !(result = sgml_malloc(size)) )
|
|
{ sgml_nomem();
|
|
return NULL;
|
|
}
|
|
|
|
if ( r->ring[r->ringp] )
|
|
sgml_free(r->ring[r->ringp]);
|
|
r->ring[r->ringp++] = result;
|
|
if ( r->ringp == RINGSIZE )
|
|
r->ringp = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* MISC *
|
|
*******************************/
|
|
|
|
wchar_t const *
|
|
str_summary(wchar_t const *s, int len)
|
|
{ wchar_t *buf;
|
|
size_t l = wcslen(s);
|
|
|
|
if ( l < (size_t)len )
|
|
return s;
|
|
buf = ringallo((len + 10)*sizeof(wchar_t));
|
|
wcsncpy(buf, s, len-5);
|
|
wcscpy(&buf[len-5], L" ... ");
|
|
wcscpy(&buf[len], &s[l-5]);
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
wchar_t *
|
|
utf8towcs(const char *in)
|
|
{ size_t sl = strlen(in);
|
|
size_t len = utf8_strlen(in, sl);
|
|
wchar_t *buf = sgml_malloc((len + 1)*sizeof(wchar_t));
|
|
const char *e = in+sl;
|
|
int i;
|
|
|
|
for(i=0; in < e;)
|
|
{ int chr;
|
|
|
|
in = utf8_get_char(in, &chr);
|
|
buf[i++] = chr;
|
|
}
|
|
|
|
buf[i] = 0;
|
|
return buf;
|
|
}
|
|
|
|
|
|
char *
|
|
wcstoutf8(const wchar_t *in)
|
|
{ size_t size = 0;
|
|
const wchar_t *s;
|
|
char *rc, *o;
|
|
|
|
for(s=in; *s; s++)
|
|
{ char buf[6];
|
|
|
|
if ( *s >= 0x80 )
|
|
{ char *o2 = utf8_put_char(buf, *s);
|
|
size += o2-buf;
|
|
} else
|
|
{ size++;
|
|
}
|
|
}
|
|
|
|
rc = sgml_malloc(size+1);
|
|
for(o=rc, s=in; *s; s++)
|
|
{ o = utf8_put_char(o, *s);
|
|
}
|
|
*o = '\0';
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* FILES *
|
|
*******************************/
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Load a file into memory. This would be so easy if we didn't had to deal
|
|
with &#RE/&#RS handling that forces us to create the proper record start
|
|
and end.
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
FILE *
|
|
wfopen(const wchar_t *name, const char *mode)
|
|
{ size_t mbl = wcstombs(NULL, name, 0);
|
|
|
|
if ( mbl > 0 )
|
|
{ char *mbs = sgml_malloc(mbl+1);
|
|
FILE *f;
|
|
|
|
wcstombs(mbs, name, mbl+1);
|
|
f = fopen(mbs, mode);
|
|
sgml_free(mbs);
|
|
|
|
return f;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
wopen(const wchar_t *name, int flags)
|
|
{ size_t mbl = wcstombs(NULL, name, 0);
|
|
|
|
if ( mbl > 0 )
|
|
{ char *mbs = sgml_malloc(mbl+1);
|
|
int fd;
|
|
|
|
wcstombs(mbs, name, mbl+1);
|
|
fd = open(mbs, flags);
|
|
sgml_free(mbs);
|
|
|
|
return fd;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
ichar *
|
|
load_sgml_file_to_charp(const ichar *file, int normalise_rsre, size_t *length)
|
|
{ int fd;
|
|
|
|
if ( (fd = wopen(file, O_RDONLY|O_BINARY)) >= 0 )
|
|
{ struct stat buf;
|
|
|
|
if ( fstat(fd, &buf) == 0 )
|
|
{ size_t len = buf.st_size;
|
|
char *r = sgml_malloc(len+1);
|
|
|
|
if ( r )
|
|
{ char *s = r;
|
|
|
|
while(len>0)
|
|
{ int n;
|
|
|
|
if ( (n=(int)read(fd, s, (unsigned int)len)) < 0 )
|
|
{ close(fd); /* I/O error */
|
|
sgml_free(r);
|
|
return NULL;
|
|
} else if ( n == 0 )
|
|
break;
|
|
len -= n;
|
|
s += n;
|
|
}
|
|
|
|
len = s-r;
|
|
*s = '\0'; /* ensure closing EOS */
|
|
close(fd);
|
|
|
|
{ int nl;
|
|
int last_is_lf;
|
|
ichar *r2, *t;
|
|
|
|
if ( normalise_rsre )
|
|
{ last_is_lf = (len > 0 && s[-1] == '\n');
|
|
for(s=r, nl=0; *s; s++)
|
|
{ if ( *s == '\n' && s>r && s[-1] != '\r' )
|
|
nl++;
|
|
}
|
|
} else
|
|
{ nl = 0;
|
|
last_is_lf = 0;
|
|
}
|
|
|
|
r2 = sgml_malloc((len+nl+1)*sizeof(ichar));
|
|
for(s=r, t=r2; *s; s++)
|
|
{ if ( *s == '\n' )
|
|
{ if ( s>r && s[-1] != '\r' )
|
|
*t++ = CR;
|
|
*t++ = LF;
|
|
} else
|
|
*t++ = *s;
|
|
}
|
|
len = t-r2;
|
|
*t = '\0';
|
|
|
|
if ( last_is_lf )
|
|
r2[--len] = '\0'; /* delete last LF */
|
|
|
|
if ( length )
|
|
*length = len;
|
|
sgml_free(r);
|
|
return r2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* ALLOCATION *
|
|
*******************************/
|
|
|
|
#ifdef _WINDOWS
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
void
|
|
sgml_nomem()
|
|
{ fprintf(stderr, "SGML: Fatal: out of memory\n");
|
|
|
|
#ifdef _WINDOWS
|
|
MessageBox(NULL, "SGML: Fatal: out of memory", "SGML", MB_OK|MB_TASKMODAL);
|
|
#endif
|
|
|
|
exit(1);
|
|
}
|
|
|
|
|
|
void *
|
|
sgml_malloc(size_t size)
|
|
{ void *mem;
|
|
|
|
if ( size == 0 )
|
|
return NULL;
|
|
|
|
if ( (mem = malloc(size)) )
|
|
return mem;
|
|
|
|
sgml_nomem();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void *
|
|
sgml_realloc(void *old, size_t size)
|
|
{ void *mem;
|
|
|
|
if ( old )
|
|
{ if ( (mem = realloc(old, size)) )
|
|
return mem;
|
|
} else
|
|
{ if ( (mem = malloc(size)) )
|
|
return mem;
|
|
}
|
|
|
|
sgml_nomem();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void *
|
|
sgml_calloc(size_t n, size_t size)
|
|
{ void *mem;
|
|
|
|
if ( (mem=calloc(n, size)) )
|
|
return mem;
|
|
|
|
sgml_nomem();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
sgml_free(void *mem)
|
|
{ if ( mem )
|
|
free(mem);
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* DEBUG *
|
|
*******************************/
|
|
|
|
void
|
|
wputs(ichar *s)
|
|
{ fwprintf(stderr, L"%ls", s);
|
|
}
|