609 lines
12 KiB
C
609 lines
12 KiB
C
/* $Id$
|
|
|
|
Part of SWI-Prolog
|
|
|
|
Author: Jan Wielemaker
|
|
E-mail: J.Wielemaker@cs.vu.nl
|
|
WWW: http://www.swi-prolog.org
|
|
Copyright (C): 1985-2009, VU University 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
|
|
*/
|
|
|
|
#include <SWI-Stream.h>
|
|
#include <SWI-Prolog.h>
|
|
#include <string.h>
|
|
#ifdef __WINDOWS__
|
|
#define inline __inline
|
|
#endif
|
|
|
|
#include "turtle_chars.c"
|
|
|
|
/*******************************
|
|
* ERRORS *
|
|
*******************************/
|
|
|
|
static atom_t ATOM_;
|
|
static functor_t FUNCTOR_error2;
|
|
static functor_t FUNCTOR_type_error2;
|
|
static functor_t FUNCTOR_syntax_error1;
|
|
static functor_t FUNCTOR_representation_error1;
|
|
|
|
static int
|
|
type_error(term_t actual, const char *expected)
|
|
{ term_t ex;
|
|
|
|
if ( (ex = PL_new_term_ref()) &&
|
|
PL_unify_term(ex,
|
|
PL_FUNCTOR, FUNCTOR_error2,
|
|
PL_FUNCTOR, FUNCTOR_type_error2,
|
|
PL_CHARS, expected,
|
|
PL_TERM, actual,
|
|
PL_VARIABLE) )
|
|
return PL_raise_exception(ex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static int
|
|
syntax_error(const char *culprit)
|
|
{ term_t ex;
|
|
|
|
if ( (ex = PL_new_term_ref()) &&
|
|
PL_unify_term(ex,
|
|
PL_FUNCTOR, FUNCTOR_error2,
|
|
PL_FUNCTOR, FUNCTOR_syntax_error1,
|
|
PL_CHARS, culprit,
|
|
PL_VARIABLE) )
|
|
return PL_raise_exception(ex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static int
|
|
representation_error(const char *culprit)
|
|
{ term_t ex;
|
|
|
|
if ( (ex = PL_new_term_ref()) &&
|
|
PL_unify_term(ex,
|
|
PL_FUNCTOR, FUNCTOR_error2,
|
|
PL_FUNCTOR, FUNCTOR_representation_error1,
|
|
PL_CHARS, culprit,
|
|
PL_VARIABLE) )
|
|
return PL_raise_exception(ex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* PROLOG *
|
|
*******************************/
|
|
|
|
/** turtle_name(+Atom) is semidet.
|
|
|
|
True if Atom is a valid Turtle identifier
|
|
*/
|
|
|
|
static inline int
|
|
wcis_name_char(int c)
|
|
{ return wcis_name_start_char(c) ||
|
|
wcis_name_extender_char(c);
|
|
}
|
|
|
|
/** turtle_name_start_char(+Int) is semidet.
|
|
*/
|
|
|
|
static foreign_t
|
|
turtle_name_start_char(term_t Code)
|
|
{ int c;
|
|
|
|
if ( !PL_get_integer(Code, &c) )
|
|
return type_error(Code, "code");
|
|
if ( !wcis_name_start_char(c) )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/** turtle_name(+Atom) is semidet.
|
|
*/
|
|
|
|
static foreign_t
|
|
turtle_name(term_t name)
|
|
{ char *s;
|
|
pl_wchar_t *w;
|
|
size_t len;
|
|
|
|
if ( PL_get_nchars(name, &len, &s, CVT_ATOM) )
|
|
{ const char *e = &s[len];
|
|
|
|
if ( !wcis_name_start_char(s[0]&0xff) )
|
|
return FALSE;
|
|
for(s++; s<e; s++)
|
|
{ if ( !wcis_name_char(s[0]&0xff) )
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
} else if ( PL_get_wchars(name, &len, &w, CVT_ATOM|CVT_EXCEPTION) )
|
|
{ const pl_wchar_t *e = &w[len];
|
|
|
|
if ( !wcis_name_start_char(w[0]) )
|
|
return FALSE;
|
|
for(w++; w<e; w++)
|
|
{ if ( !wcis_name_char(w[0]) )
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
typedef struct charbuf
|
|
{ pl_wchar_t *base;
|
|
pl_wchar_t *here;
|
|
pl_wchar_t *end;
|
|
pl_wchar_t tmp[256];
|
|
} charbuf;
|
|
|
|
|
|
static void
|
|
init_charbuf(charbuf *cb)
|
|
{ cb->base = cb->here = cb->tmp;
|
|
cb->end = &cb->tmp[sizeof(cb->tmp)/sizeof(pl_wchar_t)];
|
|
}
|
|
|
|
|
|
static int
|
|
add_charbuf(charbuf *cb, int c)
|
|
{ if ( cb->here < cb->end )
|
|
{ *cb->here++ = c;
|
|
} else
|
|
{ size_t len = (cb->end-cb->base);
|
|
|
|
if ( cb->base == cb->tmp )
|
|
{ pl_wchar_t *n = PL_malloc(len*2*sizeof(pl_wchar_t));
|
|
memcpy(n, cb->base, sizeof(cb->tmp));
|
|
cb->base = n;
|
|
} else
|
|
{ cb->base = PL_realloc(cb->base, len*2*sizeof(pl_wchar_t));
|
|
}
|
|
cb->here = &cb->base[len];
|
|
cb->end = &cb->base[len*2];
|
|
*cb->here++ = c;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
free_charbuf(charbuf *cb)
|
|
{ if ( cb->base != cb->tmp )
|
|
PL_free(cb->base);
|
|
}
|
|
|
|
|
|
/** turtle_read_name(+C0, +Stream, -C, -Name) is semidet.
|
|
*/
|
|
|
|
static foreign_t
|
|
turtle_read_name(term_t C0, term_t Stream, term_t C, term_t Name)
|
|
{ int c;
|
|
charbuf b;
|
|
IOSTREAM *in;
|
|
|
|
if ( !PL_get_integer(C0, &c) )
|
|
return type_error(C0, "code");
|
|
if ( !wcis_name_start_char(c) )
|
|
return FALSE;
|
|
|
|
if ( !PL_get_stream_handle(Stream, &in) )
|
|
return FALSE;
|
|
|
|
init_charbuf(&b);
|
|
add_charbuf(&b, c);
|
|
|
|
for(;;)
|
|
{ int c = Sgetcode(in);
|
|
|
|
if ( wcis_name_char(c) )
|
|
{ add_charbuf(&b, c);
|
|
} else
|
|
{ int rc = ( PL_unify_integer(C, c) &&
|
|
PL_unify_wchars(Name, PL_ATOM, b.here-b.base, b.base) );
|
|
|
|
free_charbuf(&b);
|
|
PL_release_stream(in);
|
|
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
read_hN(IOSTREAM *in, int digits, int *value)
|
|
{ int d = digits;
|
|
int v = 0;
|
|
|
|
while ( d-- > 0 )
|
|
{ int c = Sgetcode(in);
|
|
|
|
if ( c >= '0' && c <= '9' )
|
|
v = (v<<4) + c - '0';
|
|
else if ( c >= 'A' && c <= 'F' )
|
|
v = (v<<4) + c + 10 - 'A';
|
|
else if ( c >= 'a' && c <= 'f' )
|
|
v = (v<<4) + c + 10 - 'a';
|
|
else
|
|
{ if ( digits == 4 )
|
|
return syntax_error("Illegal \\uNNNN in string");
|
|
else
|
|
return syntax_error("Illegal \\UNNNNNNNN in string");
|
|
}
|
|
}
|
|
|
|
*value = v;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static int
|
|
string_escape(IOSTREAM *in, int c, int *value)
|
|
{ int esc;
|
|
|
|
switch(c)
|
|
{ case 'n': esc = '\n'; break;
|
|
case '"': esc = '"'; break;
|
|
case '\\':esc = '\\'; break;
|
|
case 't': esc = '\t'; break;
|
|
case 'r': esc = '\r'; break;
|
|
case 'u':
|
|
if ( !read_hN(in, 4, &esc) )
|
|
return FALSE;
|
|
break;
|
|
case 'U':
|
|
if ( !read_hN(in, 8, &esc) )
|
|
return FALSE;
|
|
break;
|
|
default:
|
|
return syntax_error("illegal escape in string");
|
|
}
|
|
|
|
*value = esc;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/** turtle_read_string(+C0, +Stream, -C, -Value:atom) is semidet.
|
|
*/
|
|
|
|
static foreign_t
|
|
turtle_read_string(term_t C0, term_t Stream, term_t C, term_t Value)
|
|
{ int c;
|
|
charbuf b;
|
|
IOSTREAM *in;
|
|
int endlen = 1;
|
|
|
|
if ( !PL_get_integer(C0, &c) )
|
|
return type_error(C0, "code");
|
|
if ( c != '"' )
|
|
return FALSE;
|
|
|
|
if ( !PL_get_stream_handle(Stream, &in) )
|
|
return FALSE;
|
|
|
|
init_charbuf(&b);
|
|
|
|
c = Sgetcode(in);
|
|
if ( c == '"' )
|
|
{ c = Sgetcode(in);
|
|
if ( c == '"' ) /* """...""" */
|
|
{ endlen = 3;
|
|
c = Sgetcode(in);
|
|
} else
|
|
{ PL_release_stream(in);
|
|
return (PL_unify_integer(C, c) &&
|
|
PL_unify_atom(Value, ATOM_));
|
|
}
|
|
}
|
|
|
|
for(;;c = Sgetcode(in))
|
|
{ if ( c == -1 )
|
|
{ free_charbuf(&b);
|
|
PL_release_stream(in);
|
|
return syntax_error("eof_in_string");
|
|
} else if ( c == '"' )
|
|
{ int count = 1;
|
|
|
|
for(count=1; count<endlen; )
|
|
{ if ( (c=Sgetcode(in)) == '"' )
|
|
count++;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if ( count == endlen )
|
|
{ int rc;
|
|
|
|
c = Sgetcode(in);
|
|
rc = (PL_unify_integer(C, c) &&
|
|
PL_unify_wchars(Value, PL_ATOM, b.here-b.base, b.base));
|
|
free_charbuf(&b);
|
|
PL_release_stream(in);
|
|
return rc;
|
|
}
|
|
|
|
while(count-- > 0)
|
|
add_charbuf(&b, '"');
|
|
add_charbuf(&b, c);
|
|
} else if ( c == '\\' )
|
|
{ int esc;
|
|
|
|
c = Sgetcode(in);
|
|
if ( !string_escape(in, c, &esc) )
|
|
{ free_charbuf(&b);
|
|
PL_release_stream(in);
|
|
return FALSE;
|
|
}
|
|
add_charbuf(&b, esc);
|
|
} else
|
|
{ add_charbuf(&b, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** turtle_read_relative_uri(+C0, +Stream, -C, -Value:atom) is semidet.
|
|
*/
|
|
|
|
static foreign_t
|
|
turtle_read_relative_uri(term_t C0, term_t Stream, term_t C, term_t Value)
|
|
{ int c;
|
|
charbuf b;
|
|
IOSTREAM *in;
|
|
|
|
if ( !PL_get_integer(C0, &c) )
|
|
return type_error(C0, "code");
|
|
if ( c != '<' )
|
|
return FALSE;
|
|
|
|
if ( !PL_get_stream_handle(Stream, &in) )
|
|
return FALSE;
|
|
|
|
init_charbuf(&b);
|
|
c = Sgetcode(in);
|
|
for(; ; c = Sgetcode(in))
|
|
{ if ( c == '>' )
|
|
{ int rc;
|
|
|
|
c = Sgetcode(in);
|
|
rc = (PL_unify_integer(C, c) &&
|
|
PL_unify_wchars(Value, PL_ATOM, b.here-b.base, b.base));
|
|
PL_release_stream(in);
|
|
free_charbuf(&b);
|
|
return rc;
|
|
} else if ( c == '\\' )
|
|
{ int esc;
|
|
|
|
c = Sgetcode(in);
|
|
if ( c == '>' )
|
|
{ add_charbuf(&b, c);
|
|
} else if ( string_escape(in, c, &esc) )
|
|
{ add_charbuf(&b, esc);
|
|
} else
|
|
{ free_charbuf(&b);
|
|
PL_release_stream(in);
|
|
return FALSE;
|
|
}
|
|
} else if ( c == -1 )
|
|
{ free_charbuf(&b);
|
|
PL_release_stream(in);
|
|
return syntax_error("eof_in_uri");
|
|
} else
|
|
{ add_charbuf(&b, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************
|
|
* WRITING *
|
|
*******************************/
|
|
|
|
static int
|
|
ttl_put_uesc(IOSTREAM *s, int c)
|
|
{ if ( c <= 0xffff )
|
|
return Sfprintf(s, "\\u%04x", (unsigned)c);
|
|
else
|
|
return Sfprintf(s, "\\U%08x", (unsigned)c);
|
|
}
|
|
|
|
|
|
static int
|
|
ttl_put_character(IOSTREAM *s, int c)
|
|
{ if ( c >= 32 && c <= 126 )
|
|
return Sputcode(c, s);
|
|
if ( c <= 31 )
|
|
return ttl_put_uesc(s, c);
|
|
if ( c >= 127 && c < 0x10ffff )
|
|
{ if ( s->encoding == ENC_ASCII )
|
|
return ttl_put_uesc(s, c);
|
|
if ( s->encoding == ENC_ISO_LATIN_1 && c > 255 )
|
|
return ttl_put_uesc(s, c);
|
|
return Sputcode(c, s);
|
|
}
|
|
|
|
representation_error("turtle_character");
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
ttl_put_echaracter(IOSTREAM *s, int c)
|
|
{ int c2;
|
|
|
|
switch(c)
|
|
{ case '\t': c2 = 't'; break;
|
|
case '\n': c2 = 'n'; break;
|
|
case '\r': c2 = 'r'; break;
|
|
default:
|
|
return ttl_put_character(s, c);
|
|
}
|
|
|
|
Sputcode('\\', s);
|
|
|
|
return Sputcode(c2, s);
|
|
}
|
|
|
|
|
|
static int
|
|
ttl_put_scharacter(IOSTREAM *s, int c)
|
|
{ switch(c)
|
|
{ case '"':
|
|
Sputcode('\\', s);
|
|
return Sputcode('"', s);
|
|
case '\\':
|
|
Sputcode('\\', s);
|
|
return Sputcode('\\', s);
|
|
default:
|
|
return ttl_put_echaracter(s, c);
|
|
}
|
|
}
|
|
|
|
|
|
static foreign_t
|
|
turtle_write_quoted_string(term_t Stream, term_t Value)
|
|
{ size_t len;
|
|
char *s;
|
|
pl_wchar_t *w;
|
|
IOSTREAM *out;
|
|
|
|
if ( !PL_get_stream_handle(Stream, &out) )
|
|
return FALSE;
|
|
|
|
if ( PL_get_nchars(Value, &len, &s, CVT_ATOM|CVT_STRING) )
|
|
{ const char *e = &s[len];
|
|
|
|
Sputcode('"', out);
|
|
for(; s<e; s++)
|
|
{ if ( ttl_put_scharacter(out, s[0]&0xff) < 0 )
|
|
break;
|
|
}
|
|
Sputcode('"', out);
|
|
return PL_release_stream(out);
|
|
} else if ( PL_get_wchars(Value, &len, &w, CVT_ATOM|CVT_EXCEPTION) )
|
|
{ const pl_wchar_t *e = &w[len];
|
|
|
|
Sputcode('"', out);
|
|
for(; w<e; w++)
|
|
{ if ( ttl_put_scharacter(out, w[0]) < 0 )
|
|
break;
|
|
}
|
|
Sputcode('"', out);
|
|
return PL_release_stream(out);
|
|
} else
|
|
{ PL_release_stream(out);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
ttl_put_ucharacter(IOSTREAM *s, int c)
|
|
{ switch(c)
|
|
{ case '>':
|
|
Sputcode('\\', s);
|
|
return Sputcode('>', s);
|
|
default:
|
|
return ttl_put_character(s, c);
|
|
}
|
|
}
|
|
|
|
|
|
/** turtle_write_uri(+Stream, +URI) is det.
|
|
*/
|
|
|
|
static foreign_t
|
|
turtle_write_uri(term_t Stream, term_t Value)
|
|
{ size_t len;
|
|
char *s;
|
|
pl_wchar_t *w;
|
|
IOSTREAM *out;
|
|
|
|
if ( !PL_get_stream_handle(Stream, &out) )
|
|
return FALSE;
|
|
|
|
if ( PL_get_nchars(Value, &len, &s, CVT_ATOM|CVT_STRING) )
|
|
{ const char *e = &s[len];
|
|
|
|
Sputcode('<', out);
|
|
for(; s<e; s++)
|
|
{ if ( ttl_put_ucharacter(out, s[0]&0xff) < 0 )
|
|
break;
|
|
}
|
|
Sputcode('>', out);
|
|
return PL_release_stream(out);
|
|
} else if ( PL_get_wchars(Value, &len, &w, CVT_ATOM|CVT_EXCEPTION) )
|
|
{ const pl_wchar_t *e = &w[len];
|
|
|
|
Sputcode('<', out);
|
|
for(; w<e; w++)
|
|
{ if ( ttl_put_ucharacter(out, w[0]) < 0 )
|
|
break;
|
|
}
|
|
Sputcode('>', out);
|
|
return PL_release_stream(out);
|
|
} else
|
|
{ PL_release_stream(out);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*******************************
|
|
* REGISTRATION *
|
|
*******************************/
|
|
|
|
#define MKFUNCTOR(n,a) \
|
|
FUNCTOR_ ## n ## a = PL_new_functor(PL_new_atom(#n), a)
|
|
#define MKATOM(n) \
|
|
ATOM_ ## n = PL_new_atom(#n)
|
|
|
|
install_t
|
|
install_turtle()
|
|
{ MKFUNCTOR(error, 2);
|
|
MKFUNCTOR(type_error, 2);
|
|
MKFUNCTOR(syntax_error, 1);
|
|
MKFUNCTOR(representation_error, 1);
|
|
ATOM_ = PL_new_atom("");
|
|
|
|
PL_register_foreign("turtle_name_start_char",
|
|
1, turtle_name_start_char, 0);
|
|
PL_register_foreign("turtle_name", 1, turtle_name, 0);
|
|
PL_register_foreign("turtle_read_name", 4, turtle_read_name, 0);
|
|
PL_register_foreign("turtle_read_string", 4, turtle_read_string, 0);
|
|
PL_register_foreign("turtle_read_relative_uri",
|
|
4, turtle_read_relative_uri, 0);
|
|
PL_register_foreign("turtle_write_quoted_string",
|
|
2, turtle_write_quoted_string, 0);
|
|
PL_register_foreign("turtle_write_uri", 2, turtle_write_uri, 0);
|
|
}
|