mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
212d962a3b
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4817 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
434 lines
10 KiB
C
434 lines
10 KiB
C
/*
|
|
* emitter.c
|
|
*
|
|
* $Author$
|
|
* $Date$
|
|
*
|
|
* Copyright (C) 2003 why the lucky stiff
|
|
*
|
|
* All Base64 code from Ruby's pack.c.
|
|
* Ruby is Copyright (C) 1993-2003 Yukihiro Matsumoto
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "syck.h"
|
|
#include "ruby.h"
|
|
|
|
#define DEFAULT_ANCHOR_FORMAT "id%03d"
|
|
|
|
static char b64_table[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
struct adjust_arg {
|
|
/* Position to start adjusting */
|
|
long startpos;
|
|
/* Adjusting by an offset */
|
|
int offset;
|
|
};
|
|
|
|
/*
|
|
* Built-in base64 (from Ruby's pack.c)
|
|
*/
|
|
char *
|
|
syck_base64enc( char *s, long len )
|
|
{
|
|
long i = 0;
|
|
int padding = '=';
|
|
char *buff = S_ALLOCA_N(char, len * 4 / 3 + 6);
|
|
|
|
while (len >= 3) {
|
|
buff[i++] = b64_table[077 & (*s >> 2)];
|
|
buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
|
|
buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03))];
|
|
buff[i++] = b64_table[077 & s[2]];
|
|
s += 3;
|
|
len -= 3;
|
|
}
|
|
if (len == 2) {
|
|
buff[i++] = b64_table[077 & (*s >> 2)];
|
|
buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
|
|
buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
|
|
buff[i++] = padding;
|
|
}
|
|
else if (len == 1) {
|
|
buff[i++] = b64_table[077 & (*s >> 2)];
|
|
buff[i++] = b64_table[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
|
|
buff[i++] = padding;
|
|
buff[i++] = padding;
|
|
}
|
|
buff[i++] = '\n';
|
|
return buff;
|
|
}
|
|
|
|
char *
|
|
syck_base64dec( char *s, long len )
|
|
{
|
|
int a = -1,b = -1,c = 0,d;
|
|
static int first = 1;
|
|
static int b64_xtable[256];
|
|
char *ptr = syck_strndup( s, len );
|
|
char *end = ptr;
|
|
char *send = s + len;
|
|
|
|
if (first) {
|
|
int i;
|
|
first = 0;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
b64_xtable[i] = -1;
|
|
}
|
|
for (i = 0; i < 64; i++) {
|
|
b64_xtable[(int)b64_table[i]] = i;
|
|
}
|
|
}
|
|
while (s < send) {
|
|
while (s[0] == '\r' || s[0] == '\n') { s++; }
|
|
if ((a = b64_xtable[(int)s[0]]) == -1) break;
|
|
if ((b = b64_xtable[(int)s[1]]) == -1) break;
|
|
if ((c = b64_xtable[(int)s[2]]) == -1) break;
|
|
if ((d = b64_xtable[(int)s[3]]) == -1) break;
|
|
*end++ = a << 2 | b >> 4;
|
|
*end++ = b << 4 | c >> 2;
|
|
*end++ = c << 6 | d;
|
|
s += 4;
|
|
}
|
|
if (a != -1 && b != -1) {
|
|
if (s + 2 < send && s[2] == '=')
|
|
*end++ = a << 2 | b >> 4;
|
|
if (c != -1 && s + 3 < send && s[3] == '=') {
|
|
*end++ = a << 2 | b >> 4;
|
|
*end++ = b << 4 | c >> 2;
|
|
}
|
|
}
|
|
*end = '\0';
|
|
/*RSTRING(buf)->len = ptr - RSTRING(buf)->ptr;*/
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
* Allocate an emitter
|
|
*/
|
|
SyckEmitter *
|
|
syck_new_emitter()
|
|
{
|
|
SyckEmitter *e;
|
|
e = S_ALLOC( SyckEmitter );
|
|
e->headless = 0;
|
|
e->seq_map = 0;
|
|
e->use_header = 0;
|
|
e->use_version = 0;
|
|
e->sort_keys = 0;
|
|
e->anchor_format = NULL;
|
|
e->explicit_typing = 0;
|
|
e->best_width = 80;
|
|
e->block_style = block_arbitrary;
|
|
e->stage = doc_open;
|
|
e->indent = 2;
|
|
e->level = -1;
|
|
e->ignore_id = 0;
|
|
e->anchors = NULL;
|
|
e->markers = NULL;
|
|
e->bufsize = SYCK_BUFFERSIZE;
|
|
e->buffer = NULL;
|
|
e->marker = NULL;
|
|
e->bufpos = 0;
|
|
e->handler = NULL;
|
|
e->bonus = NULL;
|
|
return e;
|
|
}
|
|
|
|
int
|
|
syck_st_free_anchors( char *key, char *name, char *arg )
|
|
{
|
|
S_FREE( name );
|
|
return ST_CONTINUE;
|
|
}
|
|
|
|
int
|
|
syck_st_free_markers( char *key, SyckEmitterNode *n, char *arg )
|
|
{
|
|
S_FREE( n );
|
|
return ST_CONTINUE;
|
|
}
|
|
|
|
void
|
|
syck_emitter_st_free( SyckEmitter *e )
|
|
{
|
|
/*
|
|
* Free the anchor tables
|
|
*/
|
|
if ( e->anchors != NULL )
|
|
{
|
|
st_foreach( e->anchors, syck_st_free_anchors, 0 );
|
|
st_free_table( e->anchors );
|
|
e->anchors = NULL;
|
|
}
|
|
|
|
/*
|
|
* Free the markers tables
|
|
*/
|
|
if ( e->markers != NULL )
|
|
{
|
|
st_foreach( e->markers, syck_st_free_markers, 0 );
|
|
st_free_table( e->markers );
|
|
e->markers = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
syck_emitter_ignore_id( SyckEmitter *e, SYMID id )
|
|
{
|
|
e->ignore_id = id;
|
|
}
|
|
|
|
void
|
|
syck_emitter_handler( SyckEmitter *e, SyckOutputHandler hdlr )
|
|
{
|
|
e->handler = hdlr;
|
|
}
|
|
|
|
void
|
|
syck_free_emitter( SyckEmitter *e )
|
|
{
|
|
/*
|
|
* Free tables
|
|
*/
|
|
syck_emitter_st_free( e );
|
|
if ( e->buffer != NULL )
|
|
{
|
|
S_FREE( e->buffer );
|
|
}
|
|
S_FREE( e );
|
|
}
|
|
|
|
void
|
|
syck_emitter_clear( SyckEmitter *e )
|
|
{
|
|
if ( e->buffer == NULL )
|
|
{
|
|
e->buffer = S_ALLOC_N( char, e->bufsize );
|
|
S_MEMZERO( e->buffer, char, e->bufsize );
|
|
}
|
|
e->buffer[0] = '\0';
|
|
e->marker = e->buffer;
|
|
e->bufpos = 0;
|
|
}
|
|
|
|
/*
|
|
* Raw write to the emitter buffer.
|
|
*/
|
|
void
|
|
syck_emitter_write( SyckEmitter *e, char *str, long len )
|
|
{
|
|
long at;
|
|
ASSERT( str != NULL )
|
|
if ( e->buffer == NULL )
|
|
{
|
|
syck_emitter_clear( e );
|
|
}
|
|
|
|
/*
|
|
* Flush if at end of buffer
|
|
*/
|
|
at = e->marker - e->buffer;
|
|
if ( len + at > e->bufsize )
|
|
{
|
|
syck_emitter_flush( e, 0 );
|
|
}
|
|
|
|
/*
|
|
* Write to buffer
|
|
*/
|
|
S_MEMCPY( e->marker, str, char, len );
|
|
e->marker += len;
|
|
}
|
|
|
|
/*
|
|
* Write a chunk of data out.
|
|
*/
|
|
void
|
|
syck_emitter_flush( SyckEmitter *e, long check_room )
|
|
{
|
|
/*
|
|
* Check for enough space in the buffer for check_room length.
|
|
*/
|
|
if ( check_room > 0 )
|
|
{
|
|
if ( e->bufsize > ( e->marker - e->buffer ) + check_room )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check_room = e->bufsize;
|
|
}
|
|
|
|
/*
|
|
* Determine headers.
|
|
*/
|
|
if ( ( e->stage == doc_open && ( e->headless == 0 || e->use_header == 1 ) ) ||
|
|
e->stage == doc_need_header )
|
|
{
|
|
if ( e->use_version == 1 )
|
|
{
|
|
char *header = S_ALLOC_N( char, 64 );
|
|
S_MEMZERO( header, char, 64 );
|
|
sprintf( header, "--- %%YAML:%d.%d ", SYCK_YAML_MAJOR, SYCK_YAML_MINOR );
|
|
(e->handler)( e, header, strlen( header ) );
|
|
S_FREE( header );
|
|
}
|
|
else
|
|
{
|
|
(e->handler)( e, "--- ", 4 );
|
|
}
|
|
e->stage = doc_processing;
|
|
}
|
|
|
|
/*
|
|
* Commit buffer.
|
|
*/
|
|
if ( check_room > e->marker - e->buffer )
|
|
{
|
|
check_room = e->marker - e->buffer;
|
|
}
|
|
(e->handler)( e, e->buffer, check_room );
|
|
e->bufpos += check_room;
|
|
e->marker -= check_room;
|
|
}
|
|
|
|
/*
|
|
* Emit a simple, unquoted string.
|
|
*/
|
|
void
|
|
syck_emitter_simple( SyckEmitter *e, char *str, long len )
|
|
{
|
|
e->seq_map = 0;
|
|
syck_emitter_write( e, str, len );
|
|
}
|
|
|
|
/*
|
|
* Shift the offsets of all applicable anchors
|
|
*/
|
|
int
|
|
syck_adjust_anchors( char *key, SyckEmitterNode *n, struct adjust_arg *arg )
|
|
{
|
|
if ( arg->startpos < n->pos )
|
|
{
|
|
n->pos += arg->offset;
|
|
}
|
|
return ST_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* call on start of an object's marshalling
|
|
* (handles anchors, returns an alias)
|
|
*/
|
|
char *
|
|
syck_emitter_start_obj( SyckEmitter *e, SYMID oid )
|
|
{
|
|
SyckEmitterNode *n = NULL;
|
|
char *anchor_name = NULL;
|
|
|
|
e->level++;
|
|
if ( oid != e->ignore_id )
|
|
{
|
|
/*
|
|
* Look for anchors
|
|
*/
|
|
if ( e->markers == NULL )
|
|
{
|
|
e->markers = st_init_numtable();
|
|
}
|
|
|
|
/*
|
|
* Markers table initially marks the string position of the
|
|
* object. Doesn't yet create an anchor, simply notes the
|
|
* position.
|
|
*/
|
|
if ( ! st_lookup( e->markers, (st_data_t)oid, (st_data_t *)&n ) )
|
|
{
|
|
/*
|
|
* Store all markers
|
|
*/
|
|
n = S_ALLOC( SyckEmitterNode );
|
|
n->is_shortcut = 0;
|
|
n->indent = e->level * e->indent;
|
|
n->pos = e->bufpos + ( e->marker - e->buffer );
|
|
st_insert( e->markers, (st_data_t)oid, (st_data_t)n );
|
|
}
|
|
else
|
|
{
|
|
if ( e->anchors == NULL )
|
|
{
|
|
e->anchors = st_init_numtable();
|
|
}
|
|
|
|
if ( ! st_lookup( e->anchors, (st_data_t)oid, (st_data_t *)&anchor_name ) )
|
|
{
|
|
int idx = 0;
|
|
/*
|
|
* Second time hitting this object, let's give it an anchor
|
|
*/
|
|
idx = e->anchors->num_entries + 1;
|
|
|
|
/*
|
|
* Create the anchor tag
|
|
*/
|
|
if ( n->pos >= e->bufpos )
|
|
{
|
|
int alen;
|
|
struct adjust_arg *args = S_ALLOC( struct adjust_arg );
|
|
char *start = e->buffer + ( n->pos - e->bufpos );
|
|
|
|
char *anc = ( e->anchor_format == NULL ? DEFAULT_ANCHOR_FORMAT : e->anchor_format );
|
|
anchor_name = S_ALLOC_N( char, strlen( anc ) + 10 );
|
|
S_MEMZERO( anchor_name, char, strlen( anc ) + 10 );
|
|
sprintf( anchor_name, anc, idx );
|
|
|
|
/*
|
|
* Need to flush the buffer some, if there is not room for the anchor.
|
|
*/
|
|
alen = strlen( anchor_name ) + 2;
|
|
syck_emitter_flush( e, alen );
|
|
|
|
/*
|
|
* Write the anchor into the buffer
|
|
*/
|
|
S_MEMMOVE( start + alen, start, char, e->marker - start );
|
|
S_MEMCPY( start + 1, anchor_name, char, strlen( anchor_name ) );
|
|
start[0] = '&';
|
|
start[alen - 1] = ' ';
|
|
e->marker += alen;
|
|
|
|
/*
|
|
* Cycle through anchors, modify for the size of the anchor.
|
|
*/
|
|
args->startpos = n->pos;
|
|
args->offset = alen;
|
|
st_foreach( e->markers, syck_adjust_anchors, (st_data_t)args );
|
|
S_FREE( args );
|
|
|
|
/*
|
|
* Insert into anchors table
|
|
*/
|
|
st_insert( e->anchors, (st_data_t)oid, (st_data_t)anchor_name );
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return anchor_name;
|
|
}
|
|
|
|
/*
|
|
* call on completion of an object's marshalling
|
|
*/
|
|
void
|
|
syck_emitter_end_obj( SyckEmitter *e )
|
|
{
|
|
e->level--;
|
|
}
|
|
|