diff options
Diffstat (limited to 'trunk/ext/syck/emitter.c')
-rw-r--r-- | trunk/ext/syck/emitter.c | 1247 |
1 files changed, 1247 insertions, 0 deletions
diff --git a/trunk/ext/syck/emitter.c b/trunk/ext/syck/emitter.c new file mode 100644 index 0000000000..73ff5d7a0b --- /dev/null +++ b/trunk/ext/syck/emitter.c @@ -0,0 +1,1247 @@ +/* + * emitter.c + * + * $Author$ + * + * Copyright (C) 2003 why the lucky stiff + * + * All Base64 code from Ruby's pack.c. + * Ruby is Copyright (C) 1993-2007 Yukihiro Matsumoto + */ +#include "ruby/ruby.h" + +#include <stdio.h> +#include <string.h> + +#include "syck.h" + +#define DEFAULT_ANCHOR_FORMAT "id%03d" + +const char hex_table[] = +"0123456789ABCDEF"; +static char b64_table[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* + * Built-in base64 (from Ruby's pack.c) + */ +char * +syck_base64enc( char *s, long len ) +{ + long i = 0; + int padding = '='; + char *buff = S_ALLOC_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_LEN(buf) = ptr - RSTRING_PTR(buf);*/ + return ptr; +} + +/* + * Allocate an emitter + */ +SyckEmitter * +syck_new_emitter(void) +{ + SyckEmitter *e; + e = S_ALLOC( SyckEmitter ); + e->headless = 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->style = scalar_none; + e->stage = doc_open; + e->indent = 2; + e->level = -1; + e->anchors = NULL; + e->markers = NULL; + e->anchored = NULL; + e->bufsize = SYCK_BUFFERSIZE; + e->buffer = NULL; + e->marker = NULL; + e->bufpos = 0; + e->emitter_handler = NULL; + e->output_handler = NULL; + e->lvl_idx = 0; + e->lvl_capa = ALLOC_CT; + e->levels = S_ALLOC_N( SyckLevel, e->lvl_capa ); + syck_emitter_reset_levels( e ); + e->bonus = NULL; + return e; +} + +int +syck_st_free_anchors( char *key, char *name, char *arg ) +{ + S_FREE( name ); + 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; + } + + if ( e->anchored != NULL ) + { + st_free_table( e->anchored ); + e->anchored = NULL; + } + + /* + * Free the markers tables + */ + if ( e->markers != NULL ) + { + st_free_table( e->markers ); + e->markers = NULL; + } +} + +SyckLevel * +syck_emitter_current_level( SyckEmitter *e ) +{ + return &e->levels[e->lvl_idx-1]; +} + +SyckLevel * +syck_emitter_parent_level( SyckEmitter *e ) +{ + return &e->levels[e->lvl_idx-2]; +} + +void +syck_emitter_pop_level( SyckEmitter *e ) +{ + ASSERT( e != NULL ); + + /* The root level should never be popped */ + if ( e->lvl_idx <= 1 ) return; + + e->lvl_idx -= 1; + free( e->levels[e->lvl_idx].domain ); +} + +void +syck_emitter_add_level( SyckEmitter *e, int len, enum syck_level_status status ) +{ + ASSERT( e != NULL ); + if ( e->lvl_idx + 1 > e->lvl_capa ) + { + e->lvl_capa += ALLOC_CT; + S_REALLOC_N( e->levels, SyckLevel, e->lvl_capa ); + } + + ASSERT( len > e->levels[e->lvl_idx-1].spaces ); + e->levels[e->lvl_idx].spaces = len; + e->levels[e->lvl_idx].ncount = 0; + e->levels[e->lvl_idx].domain = syck_strndup( e->levels[e->lvl_idx-1].domain, strlen( e->levels[e->lvl_idx-1].domain ) ); + e->levels[e->lvl_idx].status = status; + e->levels[e->lvl_idx].anctag = 0; + e->lvl_idx += 1; +} + +void +syck_emitter_reset_levels( SyckEmitter *e ) +{ + while ( e->lvl_idx > 1 ) + { + syck_emitter_pop_level( e ); + } + + if ( e->lvl_idx < 1 ) + { + e->lvl_idx = 1; + e->levels[0].spaces = -1; + e->levels[0].ncount = 0; + e->levels[0].domain = syck_strndup( "", 0 ); + e->levels[0].anctag = 0; + } + e->levels[0].status = syck_lvl_header; +} + +void +syck_emitter_handler( SyckEmitter *e, SyckEmitterHandler hdlr ) +{ + e->emitter_handler = hdlr; +} + +void +syck_output_handler( SyckEmitter *e, SyckOutputHandler hdlr ) +{ + e->output_handler = hdlr; +} + +void +syck_free_emitter( SyckEmitter *e ) +{ + /* + * Free tables + */ + syck_emitter_st_free( e ); + syck_emitter_reset_levels( e ); + S_FREE( e->levels[0].domain ); + S_FREE( e->levels ); + 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, const 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 ); + for (;;) { + long rest = e->bufsize - (e->marker - e->buffer); + if (len <= rest) break; + S_MEMCPY( e->marker, str, char, rest ); + e->marker += rest; + str += rest; + len -= rest; + 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; + } + + /* + * Commit buffer. + */ + if ( check_room > e->marker - e->buffer ) + { + check_room = e->marker - e->buffer; + } + (e->output_handler)( e, e->buffer, check_room ); + e->bufpos += check_room; + e->marker -= check_room; +} + +/* + * Start emitting from the given node, check for anchoring and then + * issue the callback to the emitter handler. + */ +void +syck_emit( SyckEmitter *e, st_data_t n ) +{ + SYMID oid; + char *anchor_name = NULL; + int indent = 0; + long x = 0; + SyckLevel *lvl = syck_emitter_current_level( e ); + + /* + * Determine headers. + */ + if ( e->stage == doc_open && ( e->headless == 0 || e->use_header == 1 ) ) + { + 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 ); + syck_emitter_write( e, header, strlen( header ) ); + S_FREE( header ); + } + else + { + syck_emitter_write( e, "--- ", 4 ); + } + e->stage = doc_processing; + } + + /* Add new level */ + if ( lvl->spaces >= 0 ) { + indent = lvl->spaces + e->indent; + } + syck_emitter_add_level( e, indent, syck_lvl_open ); + lvl = syck_emitter_current_level( e ); + + /* Look for anchor */ + if ( e->anchors != NULL && + st_lookup( e->markers, n, (st_data_t *)&oid ) && + st_lookup( e->anchors, (st_data_t)oid, (void *)&anchor_name ) ) + { + if ( e->anchored == NULL ) + { + e->anchored = st_init_numtable(); + } + + if ( ! st_lookup( e->anchored, (st_data_t)anchor_name, (st_data_t *)&x ) ) + { + char *an = S_ALLOC_N( char, strlen( anchor_name ) + 3 ); + sprintf( an, "&%s ", anchor_name ); + syck_emitter_write( e, an, strlen( anchor_name ) + 2 ); + free( an ); + + x = 1; + st_insert( e->anchored, (st_data_t)anchor_name, (st_data_t)x ); + lvl->anctag = 1; + } + else + { + char *an = S_ALLOC_N( char, strlen( anchor_name ) + 2 ); + sprintf( an, "*%s", anchor_name ); + syck_emitter_write( e, an, strlen( anchor_name ) + 1 ); + free( an ); + + goto end_emit; + } + } + + (e->emitter_handler)( e, n ); + + /* Pop the level */ +end_emit: + syck_emitter_pop_level( e ); + if ( e->lvl_idx == 1 ) { + syck_emitter_write( e, "\n", 1 ); + e->headless = 0; + e->stage = doc_open; + } +} + +/* + * Determine what tag needs to be written, based on the taguri of the node + * and the implicit tag which would be assigned to this node. If a tag is + * required, write the tag. + */ +void syck_emit_tag( SyckEmitter *e, const char *tag, const char *ignore ) +{ + SyckLevel *lvl; + if ( tag == NULL ) return; + if ( ignore != NULL && syck_tagcmp( tag, ignore ) == 0 && e->explicit_typing == 0 ) return; + lvl = syck_emitter_current_level( e ); + + /* implicit */ + if ( strlen( tag ) == 0 ) { + syck_emitter_write( e, "! ", 2 ); + + /* global types */ + } else if ( strncmp( tag, "tag:", 4 ) == 0 ) { + int taglen = strlen( tag ); + syck_emitter_write( e, "!", 1 ); + if ( strncmp( tag + 4, YAML_DOMAIN, strlen( YAML_DOMAIN ) ) == 0 ) { + int skip = 4 + strlen( YAML_DOMAIN ) + 1; + syck_emitter_write( e, tag + skip, taglen - skip ); + } else { + const char *subd = tag + 4; + while ( *subd != ':' && *subd != '\0' ) subd++; + if ( *subd == ':' ) { + if ( subd - tag > ( strlen( YAML_DOMAIN ) + 5 ) && + strncmp( subd - strlen( YAML_DOMAIN ), YAML_DOMAIN, strlen( YAML_DOMAIN ) ) == 0 ) { + syck_emitter_write( e, tag + 4, subd - strlen( YAML_DOMAIN ) - ( tag + 4 ) - 1 ); + syck_emitter_write( e, "/", 1 ); + syck_emitter_write( e, subd + 1, ( tag + taglen ) - ( subd + 1 ) ); + } else { + syck_emitter_write( e, tag + 4, subd - ( tag + 4 ) ); + syck_emitter_write( e, "/", 1 ); + syck_emitter_write( e, subd + 1, ( tag + taglen ) - ( subd + 1 ) ); + } + } else { + /* TODO: Invalid tag (no colon after domain) */ + return; + } + } + syck_emitter_write( e, " ", 1 ); + + /* private types */ + } else if ( strncmp( tag, "x-private:", 10 ) == 0 ) { + syck_emitter_write( e, "!!", 2 ); + syck_emitter_write( e, tag + 10, strlen( tag ) - 10 ); + syck_emitter_write( e, " ", 1 ); + } + lvl->anctag = 1; +} + +/* + * Emit a newline and an appropriately spaced indent. + */ +void syck_emit_indent( SyckEmitter *e ) +{ + int i; + SyckLevel *lvl = syck_emitter_current_level( e ); + if ( e->bufpos == 0 && ( e->marker - e->buffer ) == 0 ) return; + if ( lvl->spaces >= 0 ) { + char *spcs = S_ALLOC_N( char, lvl->spaces + 2 ); + + spcs[0] = '\n'; spcs[lvl->spaces + 1] = '\0'; + for ( i = 0; i < lvl->spaces; i++ ) spcs[i+1] = ' '; + syck_emitter_write( e, spcs, lvl->spaces + 1 ); + free( spcs ); + } +} + +/* Clear the scan */ +#define SCAN_NONE 0 +/* All printable characters? */ +#define SCAN_NONPRINT 1 +/* Any indented lines? */ +#define SCAN_INDENTED 2 +/* Larger than the requested width? */ +#define SCAN_WIDE 4 +/* Opens or closes with whitespace? */ +#define SCAN_WHITEEDGE 8 +/* Contains a newline */ +#define SCAN_NEWLINE 16 +/* Contains a single quote */ +#define SCAN_SINGLEQ 32 +/* Contains a double quote */ +#define SCAN_DOUBLEQ 64 +/* Starts with a token */ +#define SCAN_INDIC_S 128 +/* Contains a flow indicator */ +#define SCAN_INDIC_C 256 +/* Ends without newlines */ +#define SCAN_NONL_E 512 +/* Ends with many newlines */ +#define SCAN_MANYNL_E 1024 +/* Contains flow map indicators */ +#define SCAN_FLOWMAP 2048 +/* Contains flow seq indicators */ +#define SCAN_FLOWSEQ 4096 +/* Contains a valid doc separator */ +#define SCAN_DOCSEP 8192 + +/* + * Basic printable test for LATIN-1 characters. + */ +int +syck_scan_scalar( int req_width, const char *cursor, long len ) +{ + long i = 0, start = 0; + int flags = SCAN_NONE; + + if ( len < 1 ) return flags; + + /* c-indicators from the spec */ + if ( cursor[0] == '[' || cursor[0] == ']' || + cursor[0] == '{' || cursor[0] == '}' || + cursor[0] == '!' || cursor[0] == '*' || + cursor[0] == '&' || cursor[0] == '|' || + cursor[0] == '>' || cursor[0] == '\'' || + cursor[0] == '"' || cursor[0] == '#' || + cursor[0] == '%' || cursor[0] == '@' || + cursor[0] == '&' ) { + flags |= SCAN_INDIC_S; + } + if ( ( cursor[0] == '-' || cursor[0] == ':' || + cursor[0] == '?' || cursor[0] == ',' ) && + ( len == 1 || cursor[1] == ' ' || cursor[1] == '\n' ) ) + { + flags |= SCAN_INDIC_S; + } + + /* whitespace edges */ + if ( cursor[len-1] != '\n' ) { + flags |= SCAN_NONL_E; + } else if ( len > 1 && cursor[len-2] == '\n' ) { + flags |= SCAN_MANYNL_E; + } + if ( + ( len > 0 && ( cursor[0] == ' ' || cursor[0] == '\t' ) ) || + ( len > 1 && ( cursor[len-1] == ' ' || cursor[len-1] == '\t' ) ) + ) { + flags |= SCAN_WHITEEDGE; + } + + /* opening doc sep */ + if ( len >= 3 && strncmp( cursor, "---", 3 ) == 0 ) + flags |= SCAN_DOCSEP; + + /* scan string */ + for ( i = 0; i < len; i++ ) { + + if ( ! ( cursor[i] == 0x9 || + cursor[i] == 0xA || + cursor[i] == 0xD || + ( cursor[i] >= 0x20 && cursor[i] <= 0x7E ) ) + ) { + flags |= SCAN_NONPRINT; + } + else if ( cursor[i] == '\n' ) { + flags |= SCAN_NEWLINE; + if ( len - i >= 3 && strncmp( &cursor[i+1], "---", 3 ) == 0 ) + flags |= SCAN_DOCSEP; + if ( cursor[i+1] == ' ' || cursor[i+1] == '\t' ) + flags |= SCAN_INDENTED; + if ( req_width > 0 && i - start > req_width ) + flags |= SCAN_WIDE; + start = i; + } + else if ( cursor[i] == '\'' ) + { + flags |= SCAN_SINGLEQ; + } + else if ( cursor[i] == '"' ) + { + flags |= SCAN_DOUBLEQ; + } + else if ( cursor[i] == ']' ) + { + flags |= SCAN_FLOWSEQ; + } + else if ( cursor[i] == '}' ) + { + flags |= SCAN_FLOWMAP; + } + /* remember, if plain collections get implemented, to add nb-plain-flow-char */ + else if ( ( cursor[i] == ' ' && cursor[i+1] == '#' ) || + ( cursor[i] == ':' && + ( cursor[i+1] == ' ' || cursor[i+1] == '\n' || i == len - 1 ) ) ) + { + flags |= SCAN_INDIC_C; + } + else if ( cursor[i] == ',' && + ( cursor[i+1] == ' ' || cursor[i+1] == '\n' || i == len - 1 ) ) + { + flags |= SCAN_FLOWMAP; + flags |= SCAN_FLOWSEQ; + } + } + + /* printf( "---STR---\n%s\nFLAGS: %d\n", cursor, flags ); */ + return flags; +} +/* + * All scalars should be emitted through this function, which determines an appropriate style, + * tag and indent. + */ +void syck_emit_scalar( SyckEmitter *e, const char *tag, enum scalar_style force_style, int force_indent, int force_width, + char keep_nl, const char *str, long len ) +{ + enum scalar_style favor_style = scalar_literal; + SyckLevel *parent = syck_emitter_parent_level( e ); + SyckLevel *lvl = syck_emitter_current_level( e ); + int scan = 0; + const char *match_implicit; + char *implicit; + + if ( str == NULL ) str = ""; + + /* No empty nulls as map keys */ + if ( len == 0 && ( parent->status == syck_lvl_map || parent->status == syck_lvl_imap ) && + parent->ncount % 2 == 1 && syck_tagcmp( tag, "tag:yaml.org,2002:null" ) == 0 ) + { + str = "~"; + len = 1; + } + + scan = syck_scan_scalar( force_width, str, len ); + match_implicit = syck_match_implicit( str, len ); + + /* quote strings which default to implicits */ + implicit = syck_taguri( YAML_DOMAIN, match_implicit, strlen( match_implicit ) ); + if ( syck_tagcmp( tag, implicit ) != 0 && syck_tagcmp( tag, "tag:yaml.org,2002:str" ) == 0 ) { + force_style = scalar_2quote; + } else { + /* complex key */ + if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 && + ( !( tag == NULL || + ( implicit != NULL && syck_tagcmp( tag, implicit ) == 0 && e->explicit_typing == 0 ) ) ) ) + { + syck_emitter_write( e, "? ", 2 ); + parent->status = syck_lvl_mapx; + } + syck_emit_tag( e, tag, implicit ); + } + S_FREE( implicit ); + + /* if still arbitrary, sniff a good block style. */ + if ( force_style == scalar_none ) { + if ( scan & SCAN_NEWLINE ) { + force_style = scalar_literal; + } else { + force_style = scalar_plain; + } + } + + if ( e->style == scalar_fold ) { + favor_style = scalar_fold; + } + + /* Determine block style */ + if ( scan & SCAN_NONPRINT ) { + force_style = scalar_2quote; + } else if ( scan & SCAN_WHITEEDGE ) { + force_style = scalar_2quote; + } else if ( force_style != scalar_fold && ( scan & SCAN_INDENTED ) ) { + force_style = scalar_literal; + } else if ( force_style == scalar_plain && ( scan & SCAN_NEWLINE ) ) { + force_style = favor_style; + } else if ( force_style == scalar_plain && parent->status == syck_lvl_iseq && ( scan & SCAN_FLOWSEQ ) ) { + force_style = scalar_2quote; + } else if ( force_style == scalar_plain && parent->status == syck_lvl_imap && ( scan & SCAN_FLOWMAP ) ) { + force_style = scalar_2quote; + /* } else if ( force_style == scalar_fold && ( ! ( scan & SCAN_WIDE ) ) ) { + force_style = scalar_literal; */ + } else if ( force_style == scalar_plain && ( scan & SCAN_INDIC_S || scan & SCAN_INDIC_C ) ) { + if ( scan & SCAN_NEWLINE ) { + force_style = favor_style; + } else { + force_style = scalar_2quote; + } + } + + if ( force_indent > 0 ) { + lvl->spaces = parent->spaces + force_indent; + } else if ( scan & SCAN_DOCSEP ) { + lvl->spaces = parent->spaces + e->indent; + } + + /* For now, all ambiguous keys are going to be double-quoted */ + if ( ( parent->status == syck_lvl_map || parent->status == syck_lvl_mapx ) && parent->ncount % 2 == 1 ) { + if ( force_style != scalar_plain ) { + force_style = scalar_2quote; + } + } + + /* If the parent is an inline, double quote anything complex */ + if ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) { + if ( force_style != scalar_plain && force_style != scalar_1quote ) { + force_style = scalar_2quote; + } + } + + /* Fix the ending newlines */ + if ( scan & SCAN_NONL_E ) { + keep_nl = NL_CHOMP; + } else if ( scan & SCAN_MANYNL_E ) { + keep_nl = NL_KEEP; + } + + /* Write the text node */ + switch ( force_style ) + { + case scalar_1quote: + syck_emit_1quoted( e, force_width, str, len ); + break; + + case scalar_none: + case scalar_2quote: + syck_emit_2quoted( e, force_width, str, len ); + break; + + case scalar_fold: + syck_emit_folded( e, force_width, keep_nl, str, len ); + break; + + case scalar_literal: + syck_emit_literal( e, keep_nl, str, len ); + break; + + case scalar_plain: + syck_emitter_write( e, str, len ); + break; + } + + if ( parent->status == syck_lvl_mapx ) + { + syck_emitter_write( e, "\n", 1 ); + } +} + +void +syck_emitter_escape( SyckEmitter *e, const char *src, long len ) +{ + int i; + for( i = 0; i < len; i++ ) + { + if( (src[i] < 0x20) || (0x7E < src[i]) ) + { + syck_emitter_write( e, "\\", 1 ); + if( '\0' == src[i] ) + syck_emitter_write( e, "0", 1 ); + else + { + syck_emitter_write( e, "x", 1 ); + syck_emitter_write( e, (const char *)hex_table + ((src[i] & 0xF0) >> 4), 1 ); + syck_emitter_write( e, (const char *)hex_table + (src[i] & 0x0F), 1 ); + } + } + else + { + syck_emitter_write( e, src + i, 1 ); + if( '\\' == src[i] ) + syck_emitter_write( e, "\\", 1 ); + } + } +} + +/* + * Outputs a single-quoted block. + */ +void +syck_emit_1quoted( SyckEmitter *e, int width, const char *str, long len ) +{ + char do_indent = 0; + const char *mark = str; + const char *start = str; + const char *end = str; + syck_emitter_write( e, "'", 1 ); + while ( mark < str + len ) { + if ( do_indent ) { + syck_emit_indent( e ); + do_indent = 0; + } + switch ( *mark ) { + case '\'': syck_emitter_write( e, "'", 1 ); break; + + case '\n': + end = mark + 1; + if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) { + syck_emitter_write( e, "\n\n", 2 ); + } else { + syck_emitter_write( e, "\n", 1 ); + } + do_indent = 1; + start = mark + 1; + break; + + case ' ': + if ( width > 0 && *start != ' ' && mark - end > width ) { + do_indent = 1; + end = mark + 1; + } else { + syck_emitter_write( e, " ", 1 ); + } + break; + + default: + syck_emitter_write( e, mark, 1 ); + break; + } + mark++; + } + syck_emitter_write( e, "'", 1 ); +} + +/* + * Outputs a double-quoted block. + */ +void +syck_emit_2quoted( SyckEmitter *e, int width, const char *str, long len ) +{ + char do_indent = 0; + const char *mark = str; + const char *start = str; + const char *end = str; + syck_emitter_write( e, "\"", 1 ); + while ( mark < str + len ) { + if ( do_indent > 0 ) { + if ( do_indent == 2 ) { + syck_emitter_write( e, "\\", 1 ); + } + syck_emit_indent( e ); + do_indent = 0; + } + switch ( *mark ) { + + /* Escape sequences allowed within double quotes. */ + case '"': syck_emitter_write( e, "\\\"", 2 ); break; + case '\\': syck_emitter_write( e, "\\\\", 2 ); break; + case '\0': syck_emitter_write( e, "\\0", 2 ); break; + case '\a': syck_emitter_write( e, "\\a", 2 ); break; + case '\b': syck_emitter_write( e, "\\b", 2 ); break; + case '\f': syck_emitter_write( e, "\\f", 2 ); break; + case '\r': syck_emitter_write( e, "\\r", 2 ); break; + case '\t': syck_emitter_write( e, "\\t", 2 ); break; + case '\v': syck_emitter_write( e, "\\v", 2 ); break; + case 0x1b: syck_emitter_write( e, "\\e", 2 ); break; + + case '\n': + end = mark + 1; + syck_emitter_write( e, "\\n", 2 ); + do_indent = 2; + start = mark + 1; + if ( start < str + len && ( *start == ' ' || *start == '\n' ) ) { + do_indent = 0; + } + break; + + case ' ': + if ( width > 0 && *start != ' ' && mark - end > width ) { + do_indent = 1; + end = mark + 1; + } else { + syck_emitter_write( e, " ", 1 ); + } + break; + + default: + syck_emitter_escape( e, mark, 1 ); + break; + } + mark++; + } + syck_emitter_write( e, "\"", 1 ); +} + +/* + * Outputs a literal block. + */ +void +syck_emit_literal( SyckEmitter *e, char keep_nl, const char *str, long len ) +{ + const char *mark = str; + const char *start = str; + const char *end = str; + syck_emitter_write( e, "|", 1 ); + if ( keep_nl == NL_CHOMP ) { + syck_emitter_write( e, "-", 1 ); + } else if ( keep_nl == NL_KEEP ) { + syck_emitter_write( e, "+", 1 ); + } + syck_emit_indent( e ); + while ( mark < str + len ) { + if ( *mark == '\n' ) { + end = mark; + if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) end += 1; + syck_emitter_write( e, start, end - start ); + if ( mark + 1 == str + len ) { + if ( keep_nl != NL_KEEP ) syck_emitter_write( e, "\n", 1 ); + } else { + syck_emit_indent( e ); + } + start = mark + 1; + } + mark++; + } + end = str + len; + if ( start < end ) { + syck_emitter_write( e, start, end - start ); + } +} + +/* + * Outputs a folded block. + */ +void +syck_emit_folded( SyckEmitter *e, int width, char keep_nl, const char *str, long len ) +{ + const char *mark = str; + const char *start = str; + const char *end = str; + syck_emitter_write( e, ">", 1 ); + if ( keep_nl == NL_CHOMP ) { + syck_emitter_write( e, "-", 1 ); + } else if ( keep_nl == NL_KEEP ) { + syck_emitter_write( e, "+", 1 ); + } + syck_emit_indent( e ); + if ( width <= 0 ) width = e->best_width; + while ( mark < str + len ) { + switch ( *mark ) { + case '\n': + syck_emitter_write( e, end, mark - end ); + end = mark + 1; + if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) { + syck_emitter_write( e, "\n", 1 ); + } + if ( mark + 1 == str + len ) { + if ( keep_nl != NL_KEEP ) syck_emitter_write( e, "\n", 1 ); + } else { + syck_emit_indent( e ); + } + start = mark + 1; + break; + + case ' ': + if ( *start != ' ' ) { + if ( mark - end > width ) { + syck_emitter_write( e, end, mark - end ); + syck_emit_indent( e ); + end = mark + 1; + } + } + break; + } + mark++; + } + if ( end < mark ) { + syck_emitter_write( e, end, mark - end ); + } +} + +/* + * Begins emission of a sequence. + */ +void syck_emit_seq( SyckEmitter *e, const char *tag, enum seq_style style ) +{ + SyckLevel *parent = syck_emitter_parent_level( e ); + SyckLevel *lvl = syck_emitter_current_level( e ); + syck_emit_tag( e, tag, "tag:yaml.org,2002:seq" ); + if ( style == seq_inline || ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) ) { + syck_emitter_write( e, "[", 1 ); + lvl->status = syck_lvl_iseq; + } else { + /* complex key */ + if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 ) { + syck_emitter_write( e, "? ", 2 ); + parent->status = syck_lvl_mapx; + } + lvl->status = syck_lvl_seq; + } +} + +/* + * Begins emission of a mapping. + */ +void +syck_emit_map( SyckEmitter *e, const char *tag, enum map_style style ) +{ + SyckLevel *parent = syck_emitter_parent_level( e ); + SyckLevel *lvl = syck_emitter_current_level( e ); + syck_emit_tag( e, tag, "tag:yaml.org,2002:map" ); + if ( style == map_inline || ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) ) { + syck_emitter_write( e, "{", 1 ); + lvl->status = syck_lvl_imap; + } else { + /* complex key */ + if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 ) { + syck_emitter_write( e, "? ", 2 ); + parent->status = syck_lvl_mapx; + } + lvl->status = syck_lvl_map; + } +} + +/* + * Handles emitting of a collection item (for both + * sequences and maps) + */ +void syck_emit_item( SyckEmitter *e, st_data_t n ) +{ + SyckLevel *lvl = syck_emitter_current_level( e ); + switch ( lvl->status ) + { + case syck_lvl_seq: + { + SyckLevel *parent = syck_emitter_parent_level( e ); + + /* seq-in-map shortcut -- the lvl->anctag check should be unneccesary but + * there is a nasty shift/reduce in the parser on this point and + * i'm not ready to tickle it. */ + if ( lvl->anctag == 0 && parent->status == syck_lvl_map && lvl->ncount == 0 ) { + lvl->spaces = parent->spaces; + } + + /* seq-in-seq shortcut */ + else if ( lvl->anctag == 0 && parent->status == syck_lvl_seq && lvl->ncount == 0 ) { + int spcs = ( lvl->spaces - parent->spaces ) - 2; + if ( spcs >= 0 ) { + int i = 0; + for ( i = 0; i < spcs; i++ ) { + syck_emitter_write( e, " ", 1 ); + } + syck_emitter_write( e, "- ", 2 ); + break; + } + } + + syck_emit_indent( e ); + syck_emitter_write( e, "- ", 2 ); + } + break; + + case syck_lvl_iseq: + { + if ( lvl->ncount > 0 ) { + syck_emitter_write( e, ", ", 2 ); + } + } + break; + + case syck_lvl_map: + { + SyckLevel *parent = syck_emitter_parent_level( e ); + + /* map-in-seq shortcut */ + if ( lvl->anctag == 0 && parent->status == syck_lvl_seq && lvl->ncount == 0 ) { + int spcs = ( lvl->spaces - parent->spaces ) - 2; + if ( spcs >= 0 ) { + int i = 0; + for ( i = 0; i < spcs; i++ ) { + syck_emitter_write( e, " ", 1 ); + } + break; + } + } + + if ( lvl->ncount % 2 == 0 ) { + syck_emit_indent( e ); + } else { + syck_emitter_write( e, ": ", 2 ); + } + } + break; + + case syck_lvl_mapx: + { + if ( lvl->ncount % 2 == 0 ) { + syck_emit_indent( e ); + lvl->status = syck_lvl_map; + } else { + int i; + if ( lvl->spaces > 0 ) { + char *spcs = S_ALLOC_N( char, lvl->spaces + 1 ); + + spcs[lvl->spaces] = '\0'; + for ( i = 0; i < lvl->spaces; i++ ) spcs[i] = ' '; + syck_emitter_write( e, spcs, lvl->spaces ); + S_FREE( spcs ); + } + syck_emitter_write( e, ": ", 2 ); + } + } + break; + + case syck_lvl_imap: + { + if ( lvl->ncount > 0 ) { + if ( lvl->ncount % 2 == 0 ) { + syck_emitter_write( e, ", ", 2 ); + } else { + syck_emitter_write( e, ": ", 2 ); + } + } + } + break; + + default: break; + } + lvl->ncount++; + + syck_emit( e, n ); +} + +/* + * Closes emission of a collection. + */ +void syck_emit_end( SyckEmitter *e ) +{ + SyckLevel *lvl = syck_emitter_current_level( e ); + SyckLevel *parent = syck_emitter_parent_level( e ); + switch ( lvl->status ) + { + case syck_lvl_seq: + if ( lvl->ncount == 0 ) { + syck_emitter_write( e, "[]\n", 3 ); + } else if ( parent->status == syck_lvl_mapx ) { + syck_emitter_write( e, "\n", 1 ); + } + break; + + case syck_lvl_iseq: + syck_emitter_write( e, "]\n", 1 ); + break; + + case syck_lvl_map: + if ( lvl->ncount == 0 ) { + syck_emitter_write( e, "{}\n", 3 ); + } else if ( lvl->ncount % 2 == 1 ) { + syck_emitter_write( e, ":\n", 1 ); + } else if ( parent->status == syck_lvl_mapx ) { + syck_emitter_write( e, "\n", 1 ); + } + break; + + case syck_lvl_imap: + syck_emitter_write( e, "}\n", 1 ); + break; + + default: break; + } +} + +/* + * Fill markers table with emitter nodes in the + * soon-to-be-emitted tree. + */ +SYMID +syck_emitter_mark_node( SyckEmitter *e, st_data_t n ) +{ + SYMID oid = 0; + char *anchor_name = NULL; + + /* + * Ensure markers table is initialized. + */ + 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, n, (st_data_t *)&oid ) ) + { + /* + * Store all markers + */ + oid = e->markers->num_entries + 1; + st_insert( e->markers, n, (st_data_t)oid ); + } + else + { + if ( e->anchors == NULL ) + { + e->anchors = st_init_numtable(); + } + + if ( ! st_lookup( e->anchors, (st_data_t)oid, (void *)&anchor_name ) ) + { + int idx = 0; + const char *anc = ( e->anchor_format == NULL ? DEFAULT_ANCHOR_FORMAT : e->anchor_format ); + + /* + * Second time hitting this object, let's give it an anchor + */ + idx = e->anchors->num_entries + 1; + anchor_name = S_ALLOC_N( char, strlen( anc ) + 10 ); + S_MEMZERO( anchor_name, char, strlen( anc ) + 10 ); + sprintf( anchor_name, anc, idx ); + + /* + * Insert into anchors table + */ + st_insert( e->anchors, (st_data_t)oid, (st_data_t)anchor_name ); + } + } + return oid; +} + |