* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / ext / syck / emitter.c
blob73ff5d7a0b4d416fafb7ffdd640ce3fb8af5bba1
1 /*
2 * emitter.c
4 * $Author$
6 * Copyright (C) 2003 why the lucky stiff
7 *
8 * All Base64 code from Ruby's pack.c.
9 * Ruby is Copyright (C) 1993-2007 Yukihiro Matsumoto
11 #include "ruby/ruby.h"
13 #include <stdio.h>
14 #include <string.h>
16 #include "syck.h"
18 #define DEFAULT_ANCHOR_FORMAT "id%03d"
20 const char hex_table[] =
21 "0123456789ABCDEF";
22 static char b64_table[] =
23 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
26 * Built-in base64 (from Ruby's pack.c)
28 char *
29 syck_base64enc( char *s, long len )
31 long i = 0;
32 int padding = '=';
33 char *buff = S_ALLOC_N(char, len * 4 / 3 + 6);
35 while (len >= 3) {
36 buff[i++] = b64_table[077 & (*s >> 2)];
37 buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
38 buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03))];
39 buff[i++] = b64_table[077 & s[2]];
40 s += 3;
41 len -= 3;
43 if (len == 2) {
44 buff[i++] = b64_table[077 & (*s >> 2)];
45 buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
46 buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
47 buff[i++] = padding;
49 else if (len == 1) {
50 buff[i++] = b64_table[077 & (*s >> 2)];
51 buff[i++] = b64_table[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
52 buff[i++] = padding;
53 buff[i++] = padding;
55 buff[i++] = '\n';
56 return buff;
59 char *
60 syck_base64dec( char *s, long len )
62 int a = -1,b = -1,c = 0,d;
63 static int first = 1;
64 static int b64_xtable[256];
65 char *ptr = syck_strndup( s, len );
66 char *end = ptr;
67 char *send = s + len;
69 if (first) {
70 int i;
71 first = 0;
73 for (i = 0; i < 256; i++) {
74 b64_xtable[i] = -1;
76 for (i = 0; i < 64; i++) {
77 b64_xtable[(int)b64_table[i]] = i;
80 while (s < send) {
81 while (s[0] == '\r' || s[0] == '\n') { s++; }
82 if ((a = b64_xtable[(int)s[0]]) == -1) break;
83 if ((b = b64_xtable[(int)s[1]]) == -1) break;
84 if ((c = b64_xtable[(int)s[2]]) == -1) break;
85 if ((d = b64_xtable[(int)s[3]]) == -1) break;
86 *end++ = a << 2 | b >> 4;
87 *end++ = b << 4 | c >> 2;
88 *end++ = c << 6 | d;
89 s += 4;
91 if (a != -1 && b != -1) {
92 if (s + 2 < send && s[2] == '=')
93 *end++ = a << 2 | b >> 4;
94 if (c != -1 && s + 3 < send && s[3] == '=') {
95 *end++ = a << 2 | b >> 4;
96 *end++ = b << 4 | c >> 2;
99 *end = '\0';
100 /*RSTRING_LEN(buf) = ptr - RSTRING_PTR(buf);*/
101 return ptr;
105 * Allocate an emitter
107 SyckEmitter *
108 syck_new_emitter(void)
110 SyckEmitter *e;
111 e = S_ALLOC( SyckEmitter );
112 e->headless = 0;
113 e->use_header = 0;
114 e->use_version = 0;
115 e->sort_keys = 0;
116 e->anchor_format = NULL;
117 e->explicit_typing = 0;
118 e->best_width = 80;
119 e->style = scalar_none;
120 e->stage = doc_open;
121 e->indent = 2;
122 e->level = -1;
123 e->anchors = NULL;
124 e->markers = NULL;
125 e->anchored = NULL;
126 e->bufsize = SYCK_BUFFERSIZE;
127 e->buffer = NULL;
128 e->marker = NULL;
129 e->bufpos = 0;
130 e->emitter_handler = NULL;
131 e->output_handler = NULL;
132 e->lvl_idx = 0;
133 e->lvl_capa = ALLOC_CT;
134 e->levels = S_ALLOC_N( SyckLevel, e->lvl_capa );
135 syck_emitter_reset_levels( e );
136 e->bonus = NULL;
137 return e;
141 syck_st_free_anchors( char *key, char *name, char *arg )
143 S_FREE( name );
144 return ST_CONTINUE;
147 void
148 syck_emitter_st_free( SyckEmitter *e )
151 * Free the anchor tables
153 if ( e->anchors != NULL )
155 st_foreach( e->anchors, syck_st_free_anchors, 0 );
156 st_free_table( e->anchors );
157 e->anchors = NULL;
160 if ( e->anchored != NULL )
162 st_free_table( e->anchored );
163 e->anchored = NULL;
167 * Free the markers tables
169 if ( e->markers != NULL )
171 st_free_table( e->markers );
172 e->markers = NULL;
176 SyckLevel *
177 syck_emitter_current_level( SyckEmitter *e )
179 return &e->levels[e->lvl_idx-1];
182 SyckLevel *
183 syck_emitter_parent_level( SyckEmitter *e )
185 return &e->levels[e->lvl_idx-2];
188 void
189 syck_emitter_pop_level( SyckEmitter *e )
191 ASSERT( e != NULL );
193 /* The root level should never be popped */
194 if ( e->lvl_idx <= 1 ) return;
196 e->lvl_idx -= 1;
197 free( e->levels[e->lvl_idx].domain );
200 void
201 syck_emitter_add_level( SyckEmitter *e, int len, enum syck_level_status status )
203 ASSERT( e != NULL );
204 if ( e->lvl_idx + 1 > e->lvl_capa )
206 e->lvl_capa += ALLOC_CT;
207 S_REALLOC_N( e->levels, SyckLevel, e->lvl_capa );
210 ASSERT( len > e->levels[e->lvl_idx-1].spaces );
211 e->levels[e->lvl_idx].spaces = len;
212 e->levels[e->lvl_idx].ncount = 0;
213 e->levels[e->lvl_idx].domain = syck_strndup( e->levels[e->lvl_idx-1].domain, strlen( e->levels[e->lvl_idx-1].domain ) );
214 e->levels[e->lvl_idx].status = status;
215 e->levels[e->lvl_idx].anctag = 0;
216 e->lvl_idx += 1;
219 void
220 syck_emitter_reset_levels( SyckEmitter *e )
222 while ( e->lvl_idx > 1 )
224 syck_emitter_pop_level( e );
227 if ( e->lvl_idx < 1 )
229 e->lvl_idx = 1;
230 e->levels[0].spaces = -1;
231 e->levels[0].ncount = 0;
232 e->levels[0].domain = syck_strndup( "", 0 );
233 e->levels[0].anctag = 0;
235 e->levels[0].status = syck_lvl_header;
238 void
239 syck_emitter_handler( SyckEmitter *e, SyckEmitterHandler hdlr )
241 e->emitter_handler = hdlr;
244 void
245 syck_output_handler( SyckEmitter *e, SyckOutputHandler hdlr )
247 e->output_handler = hdlr;
250 void
251 syck_free_emitter( SyckEmitter *e )
254 * Free tables
256 syck_emitter_st_free( e );
257 syck_emitter_reset_levels( e );
258 S_FREE( e->levels[0].domain );
259 S_FREE( e->levels );
260 if ( e->buffer != NULL )
262 S_FREE( e->buffer );
264 S_FREE( e );
267 void
268 syck_emitter_clear( SyckEmitter *e )
270 if ( e->buffer == NULL )
272 e->buffer = S_ALLOC_N( char, e->bufsize );
273 S_MEMZERO( e->buffer, char, e->bufsize );
275 e->buffer[0] = '\0';
276 e->marker = e->buffer;
277 e->bufpos = 0;
281 * Raw write to the emitter buffer.
283 void
284 syck_emitter_write( SyckEmitter *e, const char *str, long len )
286 long at;
287 ASSERT( str != NULL );
288 if ( e->buffer == NULL )
290 syck_emitter_clear( e );
294 * Flush if at end of buffer
296 at = e->marker - e->buffer;
297 if ( len + at >= e->bufsize )
299 syck_emitter_flush( e, 0 );
300 for (;;) {
301 long rest = e->bufsize - (e->marker - e->buffer);
302 if (len <= rest) break;
303 S_MEMCPY( e->marker, str, char, rest );
304 e->marker += rest;
305 str += rest;
306 len -= rest;
307 syck_emitter_flush( e, 0 );
312 * Write to buffer
314 S_MEMCPY( e->marker, str, char, len );
315 e->marker += len;
319 * Write a chunk of data out.
321 void
322 syck_emitter_flush( SyckEmitter *e, long check_room )
325 * Check for enough space in the buffer for check_room length.
327 if ( check_room > 0 )
329 if ( e->bufsize > ( e->marker - e->buffer ) + check_room )
331 return;
334 else
336 check_room = e->bufsize;
340 * Commit buffer.
342 if ( check_room > e->marker - e->buffer )
344 check_room = e->marker - e->buffer;
346 (e->output_handler)( e, e->buffer, check_room );
347 e->bufpos += check_room;
348 e->marker -= check_room;
352 * Start emitting from the given node, check for anchoring and then
353 * issue the callback to the emitter handler.
355 void
356 syck_emit( SyckEmitter *e, st_data_t n )
358 SYMID oid;
359 char *anchor_name = NULL;
360 int indent = 0;
361 long x = 0;
362 SyckLevel *lvl = syck_emitter_current_level( e );
365 * Determine headers.
367 if ( e->stage == doc_open && ( e->headless == 0 || e->use_header == 1 ) )
369 if ( e->use_version == 1 )
371 char *header = S_ALLOC_N( char, 64 );
372 S_MEMZERO( header, char, 64 );
373 sprintf( header, "--- %%YAML:%d.%d ", SYCK_YAML_MAJOR, SYCK_YAML_MINOR );
374 syck_emitter_write( e, header, strlen( header ) );
375 S_FREE( header );
377 else
379 syck_emitter_write( e, "--- ", 4 );
381 e->stage = doc_processing;
384 /* Add new level */
385 if ( lvl->spaces >= 0 ) {
386 indent = lvl->spaces + e->indent;
388 syck_emitter_add_level( e, indent, syck_lvl_open );
389 lvl = syck_emitter_current_level( e );
391 /* Look for anchor */
392 if ( e->anchors != NULL &&
393 st_lookup( e->markers, n, (st_data_t *)&oid ) &&
394 st_lookup( e->anchors, (st_data_t)oid, (void *)&anchor_name ) )
396 if ( e->anchored == NULL )
398 e->anchored = st_init_numtable();
401 if ( ! st_lookup( e->anchored, (st_data_t)anchor_name, (st_data_t *)&x ) )
403 char *an = S_ALLOC_N( char, strlen( anchor_name ) + 3 );
404 sprintf( an, "&%s ", anchor_name );
405 syck_emitter_write( e, an, strlen( anchor_name ) + 2 );
406 free( an );
408 x = 1;
409 st_insert( e->anchored, (st_data_t)anchor_name, (st_data_t)x );
410 lvl->anctag = 1;
412 else
414 char *an = S_ALLOC_N( char, strlen( anchor_name ) + 2 );
415 sprintf( an, "*%s", anchor_name );
416 syck_emitter_write( e, an, strlen( anchor_name ) + 1 );
417 free( an );
419 goto end_emit;
423 (e->emitter_handler)( e, n );
425 /* Pop the level */
426 end_emit:
427 syck_emitter_pop_level( e );
428 if ( e->lvl_idx == 1 ) {
429 syck_emitter_write( e, "\n", 1 );
430 e->headless = 0;
431 e->stage = doc_open;
436 * Determine what tag needs to be written, based on the taguri of the node
437 * and the implicit tag which would be assigned to this node. If a tag is
438 * required, write the tag.
440 void syck_emit_tag( SyckEmitter *e, const char *tag, const char *ignore )
442 SyckLevel *lvl;
443 if ( tag == NULL ) return;
444 if ( ignore != NULL && syck_tagcmp( tag, ignore ) == 0 && e->explicit_typing == 0 ) return;
445 lvl = syck_emitter_current_level( e );
447 /* implicit */
448 if ( strlen( tag ) == 0 ) {
449 syck_emitter_write( e, "! ", 2 );
451 /* global types */
452 } else if ( strncmp( tag, "tag:", 4 ) == 0 ) {
453 int taglen = strlen( tag );
454 syck_emitter_write( e, "!", 1 );
455 if ( strncmp( tag + 4, YAML_DOMAIN, strlen( YAML_DOMAIN ) ) == 0 ) {
456 int skip = 4 + strlen( YAML_DOMAIN ) + 1;
457 syck_emitter_write( e, tag + skip, taglen - skip );
458 } else {
459 const char *subd = tag + 4;
460 while ( *subd != ':' && *subd != '\0' ) subd++;
461 if ( *subd == ':' ) {
462 if ( subd - tag > ( strlen( YAML_DOMAIN ) + 5 ) &&
463 strncmp( subd - strlen( YAML_DOMAIN ), YAML_DOMAIN, strlen( YAML_DOMAIN ) ) == 0 ) {
464 syck_emitter_write( e, tag + 4, subd - strlen( YAML_DOMAIN ) - ( tag + 4 ) - 1 );
465 syck_emitter_write( e, "/", 1 );
466 syck_emitter_write( e, subd + 1, ( tag + taglen ) - ( subd + 1 ) );
467 } else {
468 syck_emitter_write( e, tag + 4, subd - ( tag + 4 ) );
469 syck_emitter_write( e, "/", 1 );
470 syck_emitter_write( e, subd + 1, ( tag + taglen ) - ( subd + 1 ) );
472 } else {
473 /* TODO: Invalid tag (no colon after domain) */
474 return;
477 syck_emitter_write( e, " ", 1 );
479 /* private types */
480 } else if ( strncmp( tag, "x-private:", 10 ) == 0 ) {
481 syck_emitter_write( e, "!!", 2 );
482 syck_emitter_write( e, tag + 10, strlen( tag ) - 10 );
483 syck_emitter_write( e, " ", 1 );
485 lvl->anctag = 1;
489 * Emit a newline and an appropriately spaced indent.
491 void syck_emit_indent( SyckEmitter *e )
493 int i;
494 SyckLevel *lvl = syck_emitter_current_level( e );
495 if ( e->bufpos == 0 && ( e->marker - e->buffer ) == 0 ) return;
496 if ( lvl->spaces >= 0 ) {
497 char *spcs = S_ALLOC_N( char, lvl->spaces + 2 );
499 spcs[0] = '\n'; spcs[lvl->spaces + 1] = '\0';
500 for ( i = 0; i < lvl->spaces; i++ ) spcs[i+1] = ' ';
501 syck_emitter_write( e, spcs, lvl->spaces + 1 );
502 free( spcs );
506 /* Clear the scan */
507 #define SCAN_NONE 0
508 /* All printable characters? */
509 #define SCAN_NONPRINT 1
510 /* Any indented lines? */
511 #define SCAN_INDENTED 2
512 /* Larger than the requested width? */
513 #define SCAN_WIDE 4
514 /* Opens or closes with whitespace? */
515 #define SCAN_WHITEEDGE 8
516 /* Contains a newline */
517 #define SCAN_NEWLINE 16
518 /* Contains a single quote */
519 #define SCAN_SINGLEQ 32
520 /* Contains a double quote */
521 #define SCAN_DOUBLEQ 64
522 /* Starts with a token */
523 #define SCAN_INDIC_S 128
524 /* Contains a flow indicator */
525 #define SCAN_INDIC_C 256
526 /* Ends without newlines */
527 #define SCAN_NONL_E 512
528 /* Ends with many newlines */
529 #define SCAN_MANYNL_E 1024
530 /* Contains flow map indicators */
531 #define SCAN_FLOWMAP 2048
532 /* Contains flow seq indicators */
533 #define SCAN_FLOWSEQ 4096
534 /* Contains a valid doc separator */
535 #define SCAN_DOCSEP 8192
538 * Basic printable test for LATIN-1 characters.
541 syck_scan_scalar( int req_width, const char *cursor, long len )
543 long i = 0, start = 0;
544 int flags = SCAN_NONE;
546 if ( len < 1 ) return flags;
548 /* c-indicators from the spec */
549 if ( cursor[0] == '[' || cursor[0] == ']' ||
550 cursor[0] == '{' || cursor[0] == '}' ||
551 cursor[0] == '!' || cursor[0] == '*' ||
552 cursor[0] == '&' || cursor[0] == '|' ||
553 cursor[0] == '>' || cursor[0] == '\'' ||
554 cursor[0] == '"' || cursor[0] == '#' ||
555 cursor[0] == '%' || cursor[0] == '@' ||
556 cursor[0] == '&' ) {
557 flags |= SCAN_INDIC_S;
559 if ( ( cursor[0] == '-' || cursor[0] == ':' ||
560 cursor[0] == '?' || cursor[0] == ',' ) &&
561 ( len == 1 || cursor[1] == ' ' || cursor[1] == '\n' ) )
563 flags |= SCAN_INDIC_S;
566 /* whitespace edges */
567 if ( cursor[len-1] != '\n' ) {
568 flags |= SCAN_NONL_E;
569 } else if ( len > 1 && cursor[len-2] == '\n' ) {
570 flags |= SCAN_MANYNL_E;
572 if (
573 ( len > 0 && ( cursor[0] == ' ' || cursor[0] == '\t' ) ) ||
574 ( len > 1 && ( cursor[len-1] == ' ' || cursor[len-1] == '\t' ) )
576 flags |= SCAN_WHITEEDGE;
579 /* opening doc sep */
580 if ( len >= 3 && strncmp( cursor, "---", 3 ) == 0 )
581 flags |= SCAN_DOCSEP;
583 /* scan string */
584 for ( i = 0; i < len; i++ ) {
586 if ( ! ( cursor[i] == 0x9 ||
587 cursor[i] == 0xA ||
588 cursor[i] == 0xD ||
589 ( cursor[i] >= 0x20 && cursor[i] <= 0x7E ) )
591 flags |= SCAN_NONPRINT;
593 else if ( cursor[i] == '\n' ) {
594 flags |= SCAN_NEWLINE;
595 if ( len - i >= 3 && strncmp( &cursor[i+1], "---", 3 ) == 0 )
596 flags |= SCAN_DOCSEP;
597 if ( cursor[i+1] == ' ' || cursor[i+1] == '\t' )
598 flags |= SCAN_INDENTED;
599 if ( req_width > 0 && i - start > req_width )
600 flags |= SCAN_WIDE;
601 start = i;
603 else if ( cursor[i] == '\'' )
605 flags |= SCAN_SINGLEQ;
607 else if ( cursor[i] == '"' )
609 flags |= SCAN_DOUBLEQ;
611 else if ( cursor[i] == ']' )
613 flags |= SCAN_FLOWSEQ;
615 else if ( cursor[i] == '}' )
617 flags |= SCAN_FLOWMAP;
619 /* remember, if plain collections get implemented, to add nb-plain-flow-char */
620 else if ( ( cursor[i] == ' ' && cursor[i+1] == '#' ) ||
621 ( cursor[i] == ':' &&
622 ( cursor[i+1] == ' ' || cursor[i+1] == '\n' || i == len - 1 ) ) )
624 flags |= SCAN_INDIC_C;
626 else if ( cursor[i] == ',' &&
627 ( cursor[i+1] == ' ' || cursor[i+1] == '\n' || i == len - 1 ) )
629 flags |= SCAN_FLOWMAP;
630 flags |= SCAN_FLOWSEQ;
634 /* printf( "---STR---\n%s\nFLAGS: %d\n", cursor, flags ); */
635 return flags;
638 * All scalars should be emitted through this function, which determines an appropriate style,
639 * tag and indent.
641 void syck_emit_scalar( SyckEmitter *e, const char *tag, enum scalar_style force_style, int force_indent, int force_width,
642 char keep_nl, const char *str, long len )
644 enum scalar_style favor_style = scalar_literal;
645 SyckLevel *parent = syck_emitter_parent_level( e );
646 SyckLevel *lvl = syck_emitter_current_level( e );
647 int scan = 0;
648 const char *match_implicit;
649 char *implicit;
651 if ( str == NULL ) str = "";
653 /* No empty nulls as map keys */
654 if ( len == 0 && ( parent->status == syck_lvl_map || parent->status == syck_lvl_imap ) &&
655 parent->ncount % 2 == 1 && syck_tagcmp( tag, "tag:yaml.org,2002:null" ) == 0 )
657 str = "~";
658 len = 1;
661 scan = syck_scan_scalar( force_width, str, len );
662 match_implicit = syck_match_implicit( str, len );
664 /* quote strings which default to implicits */
665 implicit = syck_taguri( YAML_DOMAIN, match_implicit, strlen( match_implicit ) );
666 if ( syck_tagcmp( tag, implicit ) != 0 && syck_tagcmp( tag, "tag:yaml.org,2002:str" ) == 0 ) {
667 force_style = scalar_2quote;
668 } else {
669 /* complex key */
670 if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 &&
671 ( !( tag == NULL ||
672 ( implicit != NULL && syck_tagcmp( tag, implicit ) == 0 && e->explicit_typing == 0 ) ) ) )
674 syck_emitter_write( e, "? ", 2 );
675 parent->status = syck_lvl_mapx;
677 syck_emit_tag( e, tag, implicit );
679 S_FREE( implicit );
681 /* if still arbitrary, sniff a good block style. */
682 if ( force_style == scalar_none ) {
683 if ( scan & SCAN_NEWLINE ) {
684 force_style = scalar_literal;
685 } else {
686 force_style = scalar_plain;
690 if ( e->style == scalar_fold ) {
691 favor_style = scalar_fold;
694 /* Determine block style */
695 if ( scan & SCAN_NONPRINT ) {
696 force_style = scalar_2quote;
697 } else if ( scan & SCAN_WHITEEDGE ) {
698 force_style = scalar_2quote;
699 } else if ( force_style != scalar_fold && ( scan & SCAN_INDENTED ) ) {
700 force_style = scalar_literal;
701 } else if ( force_style == scalar_plain && ( scan & SCAN_NEWLINE ) ) {
702 force_style = favor_style;
703 } else if ( force_style == scalar_plain && parent->status == syck_lvl_iseq && ( scan & SCAN_FLOWSEQ ) ) {
704 force_style = scalar_2quote;
705 } else if ( force_style == scalar_plain && parent->status == syck_lvl_imap && ( scan & SCAN_FLOWMAP ) ) {
706 force_style = scalar_2quote;
707 /* } else if ( force_style == scalar_fold && ( ! ( scan & SCAN_WIDE ) ) ) {
708 force_style = scalar_literal; */
709 } else if ( force_style == scalar_plain && ( scan & SCAN_INDIC_S || scan & SCAN_INDIC_C ) ) {
710 if ( scan & SCAN_NEWLINE ) {
711 force_style = favor_style;
712 } else {
713 force_style = scalar_2quote;
717 if ( force_indent > 0 ) {
718 lvl->spaces = parent->spaces + force_indent;
719 } else if ( scan & SCAN_DOCSEP ) {
720 lvl->spaces = parent->spaces + e->indent;
723 /* For now, all ambiguous keys are going to be double-quoted */
724 if ( ( parent->status == syck_lvl_map || parent->status == syck_lvl_mapx ) && parent->ncount % 2 == 1 ) {
725 if ( force_style != scalar_plain ) {
726 force_style = scalar_2quote;
730 /* If the parent is an inline, double quote anything complex */
731 if ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) {
732 if ( force_style != scalar_plain && force_style != scalar_1quote ) {
733 force_style = scalar_2quote;
737 /* Fix the ending newlines */
738 if ( scan & SCAN_NONL_E ) {
739 keep_nl = NL_CHOMP;
740 } else if ( scan & SCAN_MANYNL_E ) {
741 keep_nl = NL_KEEP;
744 /* Write the text node */
745 switch ( force_style )
747 case scalar_1quote:
748 syck_emit_1quoted( e, force_width, str, len );
749 break;
751 case scalar_none:
752 case scalar_2quote:
753 syck_emit_2quoted( e, force_width, str, len );
754 break;
756 case scalar_fold:
757 syck_emit_folded( e, force_width, keep_nl, str, len );
758 break;
760 case scalar_literal:
761 syck_emit_literal( e, keep_nl, str, len );
762 break;
764 case scalar_plain:
765 syck_emitter_write( e, str, len );
766 break;
769 if ( parent->status == syck_lvl_mapx )
771 syck_emitter_write( e, "\n", 1 );
775 void
776 syck_emitter_escape( SyckEmitter *e, const char *src, long len )
778 int i;
779 for( i = 0; i < len; i++ )
781 if( (src[i] < 0x20) || (0x7E < src[i]) )
783 syck_emitter_write( e, "\\", 1 );
784 if( '\0' == src[i] )
785 syck_emitter_write( e, "0", 1 );
786 else
788 syck_emitter_write( e, "x", 1 );
789 syck_emitter_write( e, (const char *)hex_table + ((src[i] & 0xF0) >> 4), 1 );
790 syck_emitter_write( e, (const char *)hex_table + (src[i] & 0x0F), 1 );
793 else
795 syck_emitter_write( e, src + i, 1 );
796 if( '\\' == src[i] )
797 syck_emitter_write( e, "\\", 1 );
803 * Outputs a single-quoted block.
805 void
806 syck_emit_1quoted( SyckEmitter *e, int width, const char *str, long len )
808 char do_indent = 0;
809 const char *mark = str;
810 const char *start = str;
811 const char *end = str;
812 syck_emitter_write( e, "'", 1 );
813 while ( mark < str + len ) {
814 if ( do_indent ) {
815 syck_emit_indent( e );
816 do_indent = 0;
818 switch ( *mark ) {
819 case '\'': syck_emitter_write( e, "'", 1 ); break;
821 case '\n':
822 end = mark + 1;
823 if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) {
824 syck_emitter_write( e, "\n\n", 2 );
825 } else {
826 syck_emitter_write( e, "\n", 1 );
828 do_indent = 1;
829 start = mark + 1;
830 break;
832 case ' ':
833 if ( width > 0 && *start != ' ' && mark - end > width ) {
834 do_indent = 1;
835 end = mark + 1;
836 } else {
837 syck_emitter_write( e, " ", 1 );
839 break;
841 default:
842 syck_emitter_write( e, mark, 1 );
843 break;
845 mark++;
847 syck_emitter_write( e, "'", 1 );
851 * Outputs a double-quoted block.
853 void
854 syck_emit_2quoted( SyckEmitter *e, int width, const char *str, long len )
856 char do_indent = 0;
857 const char *mark = str;
858 const char *start = str;
859 const char *end = str;
860 syck_emitter_write( e, "\"", 1 );
861 while ( mark < str + len ) {
862 if ( do_indent > 0 ) {
863 if ( do_indent == 2 ) {
864 syck_emitter_write( e, "\\", 1 );
866 syck_emit_indent( e );
867 do_indent = 0;
869 switch ( *mark ) {
871 /* Escape sequences allowed within double quotes. */
872 case '"': syck_emitter_write( e, "\\\"", 2 ); break;
873 case '\\': syck_emitter_write( e, "\\\\", 2 ); break;
874 case '\0': syck_emitter_write( e, "\\0", 2 ); break;
875 case '\a': syck_emitter_write( e, "\\a", 2 ); break;
876 case '\b': syck_emitter_write( e, "\\b", 2 ); break;
877 case '\f': syck_emitter_write( e, "\\f", 2 ); break;
878 case '\r': syck_emitter_write( e, "\\r", 2 ); break;
879 case '\t': syck_emitter_write( e, "\\t", 2 ); break;
880 case '\v': syck_emitter_write( e, "\\v", 2 ); break;
881 case 0x1b: syck_emitter_write( e, "\\e", 2 ); break;
883 case '\n':
884 end = mark + 1;
885 syck_emitter_write( e, "\\n", 2 );
886 do_indent = 2;
887 start = mark + 1;
888 if ( start < str + len && ( *start == ' ' || *start == '\n' ) ) {
889 do_indent = 0;
891 break;
893 case ' ':
894 if ( width > 0 && *start != ' ' && mark - end > width ) {
895 do_indent = 1;
896 end = mark + 1;
897 } else {
898 syck_emitter_write( e, " ", 1 );
900 break;
902 default:
903 syck_emitter_escape( e, mark, 1 );
904 break;
906 mark++;
908 syck_emitter_write( e, "\"", 1 );
912 * Outputs a literal block.
914 void
915 syck_emit_literal( SyckEmitter *e, char keep_nl, const char *str, long len )
917 const char *mark = str;
918 const char *start = str;
919 const char *end = str;
920 syck_emitter_write( e, "|", 1 );
921 if ( keep_nl == NL_CHOMP ) {
922 syck_emitter_write( e, "-", 1 );
923 } else if ( keep_nl == NL_KEEP ) {
924 syck_emitter_write( e, "+", 1 );
926 syck_emit_indent( e );
927 while ( mark < str + len ) {
928 if ( *mark == '\n' ) {
929 end = mark;
930 if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) end += 1;
931 syck_emitter_write( e, start, end - start );
932 if ( mark + 1 == str + len ) {
933 if ( keep_nl != NL_KEEP ) syck_emitter_write( e, "\n", 1 );
934 } else {
935 syck_emit_indent( e );
937 start = mark + 1;
939 mark++;
941 end = str + len;
942 if ( start < end ) {
943 syck_emitter_write( e, start, end - start );
948 * Outputs a folded block.
950 void
951 syck_emit_folded( SyckEmitter *e, int width, char keep_nl, const char *str, long len )
953 const char *mark = str;
954 const char *start = str;
955 const char *end = str;
956 syck_emitter_write( e, ">", 1 );
957 if ( keep_nl == NL_CHOMP ) {
958 syck_emitter_write( e, "-", 1 );
959 } else if ( keep_nl == NL_KEEP ) {
960 syck_emitter_write( e, "+", 1 );
962 syck_emit_indent( e );
963 if ( width <= 0 ) width = e->best_width;
964 while ( mark < str + len ) {
965 switch ( *mark ) {
966 case '\n':
967 syck_emitter_write( e, end, mark - end );
968 end = mark + 1;
969 if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) {
970 syck_emitter_write( e, "\n", 1 );
972 if ( mark + 1 == str + len ) {
973 if ( keep_nl != NL_KEEP ) syck_emitter_write( e, "\n", 1 );
974 } else {
975 syck_emit_indent( e );
977 start = mark + 1;
978 break;
980 case ' ':
981 if ( *start != ' ' ) {
982 if ( mark - end > width ) {
983 syck_emitter_write( e, end, mark - end );
984 syck_emit_indent( e );
985 end = mark + 1;
988 break;
990 mark++;
992 if ( end < mark ) {
993 syck_emitter_write( e, end, mark - end );
998 * Begins emission of a sequence.
1000 void syck_emit_seq( SyckEmitter *e, const char *tag, enum seq_style style )
1002 SyckLevel *parent = syck_emitter_parent_level( e );
1003 SyckLevel *lvl = syck_emitter_current_level( e );
1004 syck_emit_tag( e, tag, "tag:yaml.org,2002:seq" );
1005 if ( style == seq_inline || ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) ) {
1006 syck_emitter_write( e, "[", 1 );
1007 lvl->status = syck_lvl_iseq;
1008 } else {
1009 /* complex key */
1010 if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 ) {
1011 syck_emitter_write( e, "? ", 2 );
1012 parent->status = syck_lvl_mapx;
1014 lvl->status = syck_lvl_seq;
1019 * Begins emission of a mapping.
1021 void
1022 syck_emit_map( SyckEmitter *e, const char *tag, enum map_style style )
1024 SyckLevel *parent = syck_emitter_parent_level( e );
1025 SyckLevel *lvl = syck_emitter_current_level( e );
1026 syck_emit_tag( e, tag, "tag:yaml.org,2002:map" );
1027 if ( style == map_inline || ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) ) {
1028 syck_emitter_write( e, "{", 1 );
1029 lvl->status = syck_lvl_imap;
1030 } else {
1031 /* complex key */
1032 if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 ) {
1033 syck_emitter_write( e, "? ", 2 );
1034 parent->status = syck_lvl_mapx;
1036 lvl->status = syck_lvl_map;
1041 * Handles emitting of a collection item (for both
1042 * sequences and maps)
1044 void syck_emit_item( SyckEmitter *e, st_data_t n )
1046 SyckLevel *lvl = syck_emitter_current_level( e );
1047 switch ( lvl->status )
1049 case syck_lvl_seq:
1051 SyckLevel *parent = syck_emitter_parent_level( e );
1053 /* seq-in-map shortcut -- the lvl->anctag check should be unneccesary but
1054 * there is a nasty shift/reduce in the parser on this point and
1055 * i'm not ready to tickle it. */
1056 if ( lvl->anctag == 0 && parent->status == syck_lvl_map && lvl->ncount == 0 ) {
1057 lvl->spaces = parent->spaces;
1060 /* seq-in-seq shortcut */
1061 else if ( lvl->anctag == 0 && parent->status == syck_lvl_seq && lvl->ncount == 0 ) {
1062 int spcs = ( lvl->spaces - parent->spaces ) - 2;
1063 if ( spcs >= 0 ) {
1064 int i = 0;
1065 for ( i = 0; i < spcs; i++ ) {
1066 syck_emitter_write( e, " ", 1 );
1068 syck_emitter_write( e, "- ", 2 );
1069 break;
1073 syck_emit_indent( e );
1074 syck_emitter_write( e, "- ", 2 );
1076 break;
1078 case syck_lvl_iseq:
1080 if ( lvl->ncount > 0 ) {
1081 syck_emitter_write( e, ", ", 2 );
1084 break;
1086 case syck_lvl_map:
1088 SyckLevel *parent = syck_emitter_parent_level( e );
1090 /* map-in-seq shortcut */
1091 if ( lvl->anctag == 0 && parent->status == syck_lvl_seq && lvl->ncount == 0 ) {
1092 int spcs = ( lvl->spaces - parent->spaces ) - 2;
1093 if ( spcs >= 0 ) {
1094 int i = 0;
1095 for ( i = 0; i < spcs; i++ ) {
1096 syck_emitter_write( e, " ", 1 );
1098 break;
1102 if ( lvl->ncount % 2 == 0 ) {
1103 syck_emit_indent( e );
1104 } else {
1105 syck_emitter_write( e, ": ", 2 );
1108 break;
1110 case syck_lvl_mapx:
1112 if ( lvl->ncount % 2 == 0 ) {
1113 syck_emit_indent( e );
1114 lvl->status = syck_lvl_map;
1115 } else {
1116 int i;
1117 if ( lvl->spaces > 0 ) {
1118 char *spcs = S_ALLOC_N( char, lvl->spaces + 1 );
1120 spcs[lvl->spaces] = '\0';
1121 for ( i = 0; i < lvl->spaces; i++ ) spcs[i] = ' ';
1122 syck_emitter_write( e, spcs, lvl->spaces );
1123 S_FREE( spcs );
1125 syck_emitter_write( e, ": ", 2 );
1128 break;
1130 case syck_lvl_imap:
1132 if ( lvl->ncount > 0 ) {
1133 if ( lvl->ncount % 2 == 0 ) {
1134 syck_emitter_write( e, ", ", 2 );
1135 } else {
1136 syck_emitter_write( e, ": ", 2 );
1140 break;
1142 default: break;
1144 lvl->ncount++;
1146 syck_emit( e, n );
1150 * Closes emission of a collection.
1152 void syck_emit_end( SyckEmitter *e )
1154 SyckLevel *lvl = syck_emitter_current_level( e );
1155 SyckLevel *parent = syck_emitter_parent_level( e );
1156 switch ( lvl->status )
1158 case syck_lvl_seq:
1159 if ( lvl->ncount == 0 ) {
1160 syck_emitter_write( e, "[]\n", 3 );
1161 } else if ( parent->status == syck_lvl_mapx ) {
1162 syck_emitter_write( e, "\n", 1 );
1164 break;
1166 case syck_lvl_iseq:
1167 syck_emitter_write( e, "]\n", 1 );
1168 break;
1170 case syck_lvl_map:
1171 if ( lvl->ncount == 0 ) {
1172 syck_emitter_write( e, "{}\n", 3 );
1173 } else if ( lvl->ncount % 2 == 1 ) {
1174 syck_emitter_write( e, ":\n", 1 );
1175 } else if ( parent->status == syck_lvl_mapx ) {
1176 syck_emitter_write( e, "\n", 1 );
1178 break;
1180 case syck_lvl_imap:
1181 syck_emitter_write( e, "}\n", 1 );
1182 break;
1184 default: break;
1189 * Fill markers table with emitter nodes in the
1190 * soon-to-be-emitted tree.
1192 SYMID
1193 syck_emitter_mark_node( SyckEmitter *e, st_data_t n )
1195 SYMID oid = 0;
1196 char *anchor_name = NULL;
1199 * Ensure markers table is initialized.
1201 if ( e->markers == NULL )
1203 e->markers = st_init_numtable();
1207 * Markers table initially marks the string position of the
1208 * object. Doesn't yet create an anchor, simply notes the
1209 * position.
1211 if ( ! st_lookup( e->markers, n, (st_data_t *)&oid ) )
1214 * Store all markers
1216 oid = e->markers->num_entries + 1;
1217 st_insert( e->markers, n, (st_data_t)oid );
1219 else
1221 if ( e->anchors == NULL )
1223 e->anchors = st_init_numtable();
1226 if ( ! st_lookup( e->anchors, (st_data_t)oid, (void *)&anchor_name ) )
1228 int idx = 0;
1229 const char *anc = ( e->anchor_format == NULL ? DEFAULT_ANCHOR_FORMAT : e->anchor_format );
1232 * Second time hitting this object, let's give it an anchor
1234 idx = e->anchors->num_entries + 1;
1235 anchor_name = S_ALLOC_N( char, strlen( anc ) + 10 );
1236 S_MEMZERO( anchor_name, char, strlen( anc ) + 10 );
1237 sprintf( anchor_name, anc, idx );
1240 * Insert into anchors table
1242 st_insert( e->anchors, (st_data_t)oid, (st_data_t)anchor_name );
1245 return oid;