• Main Page
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

ext/syck/emitter.c

Go to the documentation of this file.
00001 /*
00002  * emitter.c
00003  *
00004  * $Author: nobu $
00005  *
00006  * Copyright (C) 2003 why the lucky stiff
00007  *
00008  * All Base64 code from Ruby's pack.c.
00009  * Ruby is Copyright (C) 1993-2007 Yukihiro Matsumoto
00010  */
00011 #include "ruby/ruby.h"
00012 
00013 #include <stdio.h>
00014 #include <string.h>
00015 
00016 #include "syck.h"
00017 
00018 #define DEFAULT_ANCHOR_FORMAT "id%03d"
00019 
00020 const char hex_table[] =
00021 "0123456789ABCDEF";
00022 static char b64_table[] =
00023 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00024 
00025 /*
00026  * Built-in base64 (from Ruby's pack.c)
00027  */
00028 char *
00029 syck_base64enc( char *s, long len )
00030 {
00031     long i = 0;
00032     int padding = '=';
00033     char *buff = S_ALLOC_N(char, len * 4 / 3 + 6);
00034 
00035     while (len >= 3) {
00036         buff[i++] = b64_table[077 & (*s >> 2)];
00037         buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
00038         buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03))];
00039         buff[i++] = b64_table[077 & s[2]];
00040         s += 3;
00041         len -= 3;
00042     }
00043     if (len == 2) {
00044         buff[i++] = b64_table[077 & (*s >> 2)];
00045         buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
00046         buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
00047         buff[i++] = padding;
00048     }
00049     else if (len == 1) {
00050         buff[i++] = b64_table[077 & (*s >> 2)];
00051         buff[i++] = b64_table[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
00052         buff[i++] = padding;
00053         buff[i++] = padding;
00054     }
00055     buff[i++] = '\n';
00056     return buff;
00057 }
00058 
00059 char *
00060 syck_base64dec( char *s, long len )
00061 {
00062     int a = -1,b = -1,c = 0,d;
00063     static int first = 1;
00064     static int b64_xtable[256];
00065     char *ptr = syck_strndup( s, len );
00066     char *end = ptr;
00067     char *send = s + len;
00068 
00069     if (first) {
00070         int i;
00071         first = 0;
00072 
00073         for (i = 0; i < 256; i++) {
00074         b64_xtable[i] = -1;
00075         }
00076         for (i = 0; i < 64; i++) {
00077         b64_xtable[(int)b64_table[i]] = i;
00078         }
00079     }
00080     while (s < send) {
00081         while (s[0] == '\r' || s[0] == '\n') { s++; }
00082         if ((a = b64_xtable[(int)s[0]]) == -1) break;
00083         if ((b = b64_xtable[(int)s[1]]) == -1) break;
00084         if ((c = b64_xtable[(int)s[2]]) == -1) break;
00085         if ((d = b64_xtable[(int)s[3]]) == -1) break;
00086         *end++ = a << 2 | b >> 4;
00087         *end++ = b << 4 | c >> 2;
00088         *end++ = c << 6 | d;
00089         s += 4;
00090     }
00091     if (a != -1 && b != -1) {
00092         if (s + 2 < send && s[2] == '=')
00093         *end++ = a << 2 | b >> 4;
00094         if (c != -1 && s + 3 < send && s[3] == '=') {
00095         *end++ = a << 2 | b >> 4;
00096         *end++ = b << 4 | c >> 2;
00097         }
00098     }
00099     *end = '\0';
00100     /*RSTRING_LEN(buf) = ptr - RSTRING_PTR(buf);*/
00101     return ptr;
00102 }
00103 
00104 /*
00105  * Allocate an emitter
00106  */
00107 SyckEmitter *
00108 syck_new_emitter(void)
00109 {
00110     SyckEmitter *e;
00111     e = S_ALLOC( SyckEmitter );
00112     e->headless = 0;
00113     e->use_header = 0;
00114     e->use_version = 0;
00115     e->sort_keys = 0;
00116     e->anchor_format = NULL;
00117     e->explicit_typing = 0;
00118     e->best_width = 80;
00119     e->style = scalar_none;
00120     e->stage = doc_open;
00121     e->indent = 2;
00122     e->level = -1;
00123     e->anchors = NULL;
00124     e->markers = NULL;
00125     e->anchored = NULL;
00126     e->bufsize = SYCK_BUFFERSIZE;
00127     e->buffer = NULL;
00128     e->marker = NULL;
00129     e->bufpos = 0;
00130     e->emitter_handler = NULL;
00131     e->output_handler = NULL;
00132     e->lvl_idx = 0;
00133     e->lvl_capa = ALLOC_CT;
00134     e->levels = S_ALLOC_N( SyckLevel, e->lvl_capa );
00135     syck_emitter_reset_levels( e );
00136     e->bonus = NULL;
00137     return e;
00138 }
00139 
00140 int
00141 syck_st_free_anchors( char *key, char *name, char *arg )
00142 {
00143     S_FREE( name );
00144     return ST_CONTINUE;
00145 }
00146 
00147 void
00148 syck_emitter_st_free( SyckEmitter *e )
00149 {
00150     /*
00151      * Free the anchor tables
00152      */
00153     if ( e->anchors != NULL )
00154     {
00155         st_foreach( e->anchors, syck_st_free_anchors, 0 );
00156         st_free_table( e->anchors );
00157         e->anchors = NULL;
00158     }
00159 
00160     if ( e->anchored != NULL )
00161     {
00162         st_free_table( e->anchored );
00163         e->anchored = NULL;
00164     }
00165 
00166     /*
00167      * Free the markers tables
00168      */
00169     if ( e->markers != NULL )
00170     {
00171         st_free_table( e->markers );
00172         e->markers = NULL;
00173     }
00174 }
00175 
00176 SyckLevel *
00177 syck_emitter_current_level( SyckEmitter *e )
00178 {
00179     return &e->levels[e->lvl_idx-1];
00180 }
00181 
00182 SyckLevel *
00183 syck_emitter_parent_level( SyckEmitter *e )
00184 {
00185     return &e->levels[e->lvl_idx-2];
00186 }
00187 
00188 void
00189 syck_emitter_pop_level( SyckEmitter *e )
00190 {
00191     ASSERT( e != NULL );
00192 
00193     /* The root level should never be popped */
00194     if ( e->lvl_idx <= 1 ) return;
00195 
00196     e->lvl_idx -= 1;
00197     free( e->levels[e->lvl_idx].domain );
00198 }
00199 
00200 void
00201 syck_emitter_add_level( SyckEmitter *e, int len, enum syck_level_status status )
00202 {
00203     ASSERT( e != NULL );
00204     if ( e->lvl_idx + 1 > e->lvl_capa )
00205     {
00206         e->lvl_capa += ALLOC_CT;
00207         S_REALLOC_N( e->levels, SyckLevel, e->lvl_capa );
00208     }
00209 
00210     ASSERT( len > e->levels[e->lvl_idx-1].spaces );
00211     e->levels[e->lvl_idx].spaces = len;
00212     e->levels[e->lvl_idx].ncount = 0;
00213     e->levels[e->lvl_idx].domain = syck_strndup( e->levels[e->lvl_idx-1].domain, strlen( e->levels[e->lvl_idx-1].domain ) );
00214     e->levels[e->lvl_idx].status = status;
00215     e->levels[e->lvl_idx].anctag = 0;
00216     e->lvl_idx += 1;
00217 }
00218 
00219 void
00220 syck_emitter_reset_levels( SyckEmitter *e )
00221 {
00222     while ( e->lvl_idx > 1 )
00223     {
00224         syck_emitter_pop_level( e );
00225     }
00226 
00227     if ( e->lvl_idx < 1 )
00228     {
00229         e->lvl_idx = 1;
00230         e->levels[0].spaces = -1;
00231         e->levels[0].ncount = 0;
00232         e->levels[0].domain = syck_strndup( "", 0 );
00233         e->levels[0].anctag = 0;
00234     }
00235     e->levels[0].status = syck_lvl_header;
00236 }
00237 
00238 void
00239 syck_emitter_handler( SyckEmitter *e, SyckEmitterHandler hdlr )
00240 {
00241     e->emitter_handler = hdlr;
00242 }
00243 
00244 void
00245 syck_output_handler( SyckEmitter *e, SyckOutputHandler hdlr )
00246 {
00247     e->output_handler = hdlr;
00248 }
00249 
00250 void
00251 syck_free_emitter( SyckEmitter *e )
00252 {
00253     /*
00254      * Free tables
00255      */
00256     syck_emitter_st_free( e );
00257     syck_emitter_reset_levels( e );
00258     S_FREE( e->levels[0].domain );
00259     S_FREE( e->levels );
00260     if ( e->buffer != NULL )
00261     {
00262         S_FREE( e->buffer );
00263     }
00264     S_FREE( e );
00265 }
00266 
00267 void
00268 syck_emitter_clear( SyckEmitter *e )
00269 {
00270     if ( e->buffer == NULL )
00271     {
00272         e->buffer = S_ALLOC_N( char, e->bufsize );
00273         S_MEMZERO( e->buffer, char, e->bufsize );
00274     }
00275     e->buffer[0] = '\0';
00276     e->marker = e->buffer;
00277     e->bufpos = 0;
00278 }
00279 
00280 /*
00281  * Raw write to the emitter buffer.
00282  */
00283 void
00284 syck_emitter_write( SyckEmitter *e, const char *str, long len )
00285 {
00286     long at;
00287     ASSERT( str != NULL );
00288     if ( e->buffer == NULL )
00289     {
00290         syck_emitter_clear( e );
00291     }
00292 
00293     /*
00294      * Flush if at end of buffer
00295      */
00296     at = e->marker - e->buffer;
00297     if ( len + at >= (long)e->bufsize )
00298     {
00299         syck_emitter_flush( e, 0 );
00300         for (;;) {
00301             long rest = e->bufsize - (e->marker - e->buffer);
00302             if (len <= rest) break;
00303             S_MEMCPY( e->marker, str, char, rest );
00304             e->marker += rest;
00305             str += rest;
00306             len -= rest;
00307             syck_emitter_flush( e, 0 );
00308         }
00309     }
00310 
00311     /*
00312      * Write to buffer
00313      */
00314     S_MEMCPY( e->marker, str, char, len );
00315     e->marker += len;
00316 }
00317 
00318 /*
00319  * Write a chunk of data out.
00320  */
00321 void
00322 syck_emitter_flush( SyckEmitter *e, long check_room )
00323 {
00324     /*
00325      * Check for enough space in the buffer for check_room length.
00326      */
00327     if ( check_room > 0 )
00328     {
00329         if ( (long)e->bufsize > ( e->marker - e->buffer ) + check_room )
00330         {
00331             return;
00332         }
00333     }
00334     else
00335     {
00336         check_room = e->bufsize;
00337     }
00338 
00339     /*
00340      * Commit buffer.
00341      */
00342     if ( check_room > e->marker - e->buffer )
00343     {
00344         check_room = e->marker - e->buffer;
00345     }
00346     (e->output_handler)( e, e->buffer, check_room );
00347     e->bufpos += check_room;
00348     e->marker -= check_room;
00349 }
00350 
00351 /*
00352  * Start emitting from the given node, check for anchoring and then
00353  * issue the callback to the emitter handler.
00354  */
00355 void
00356 syck_emit( SyckEmitter *e, st_data_t n )
00357 {
00358     SYMID oid;
00359     char *anchor_name = NULL;
00360     int indent = 0;
00361     long x = 0;
00362     SyckLevel *lvl = syck_emitter_current_level( e );
00363 
00364     /*
00365      * Determine headers.
00366      */
00367     if ( e->stage == doc_open && ( e->headless == 0 || e->use_header == 1 ) )
00368     {
00369         if ( e->use_version == 1 )
00370         {
00371             char *header = S_ALLOC_N( char, 64 );
00372             S_MEMZERO( header, char, 64 );
00373             sprintf( header, "--- %%YAML:%d.%d ", SYCK_YAML_MAJOR, SYCK_YAML_MINOR );
00374             syck_emitter_write( e, header, strlen( header ) );
00375             S_FREE( header );
00376         }
00377         else
00378         {
00379             syck_emitter_write( e, "--- ", 4 );
00380         }
00381         e->stage = doc_processing;
00382     }
00383 
00384     /* Add new level */
00385     if ( lvl->spaces >= 0 ) {
00386         indent = lvl->spaces + e->indent;
00387     }
00388     syck_emitter_add_level( e, indent, syck_lvl_open );
00389     lvl = syck_emitter_current_level( e );
00390 
00391     /* Look for anchor */
00392     if ( e->anchors != NULL &&
00393         st_lookup( e->markers, n, (st_data_t *)&oid ) &&
00394         st_lookup( e->anchors, (st_data_t)oid, (void *)&anchor_name ) )
00395     {
00396         if ( e->anchored == NULL )
00397         {
00398             e->anchored = st_init_numtable();
00399         }
00400 
00401         if ( ! st_lookup( e->anchored, (st_data_t)anchor_name, (st_data_t *)&x ) )
00402         {
00403             char *an = S_ALLOC_N( char, strlen( anchor_name ) + 3 );
00404             sprintf( an, "&%s ", anchor_name );
00405             syck_emitter_write( e, an, strlen( anchor_name ) + 2 );
00406             free( an );
00407 
00408             x = 1;
00409             st_insert( e->anchored, (st_data_t)anchor_name, (st_data_t)x );
00410             lvl->anctag = 1;
00411         }
00412         else
00413         {
00414             char *an = S_ALLOC_N( char, strlen( anchor_name ) + 2 );
00415             sprintf( an, "*%s", anchor_name );
00416             syck_emitter_write( e, an, strlen( anchor_name ) + 1 );
00417             free( an );
00418 
00419             goto end_emit;
00420         }
00421     }
00422 
00423     (e->emitter_handler)( e, n );
00424 
00425     /* Pop the level */
00426 end_emit:
00427     syck_emitter_pop_level( e );
00428     if ( e->lvl_idx == 1 ) {
00429         syck_emitter_write( e, "\n", 1 );
00430         e->headless = 0;
00431         e->stage = doc_open;
00432     }
00433 }
00434 
00435 /*
00436  * Determine what tag needs to be written, based on the taguri of the node
00437  * and the implicit tag which would be assigned to this node.  If a tag is
00438  * required, write the tag.
00439  */
00440 void syck_emit_tag( SyckEmitter *e, const char *tag, const char *ignore )
00441 {
00442     SyckLevel *lvl;
00443     if ( tag == NULL ) return;
00444     if ( ignore != NULL && syck_tagcmp( tag, ignore ) == 0 && e->explicit_typing == 0 ) return;
00445     lvl = syck_emitter_current_level( e );
00446 
00447     /* implicit */
00448     if ( strlen( tag ) == 0 ) {
00449         syck_emitter_write( e, "! ", 2 );
00450 
00451     /* global types */
00452     } else if ( strncmp( tag, "tag:", 4 ) == 0 ) {
00453         int taglen = strlen( tag );
00454         syck_emitter_write( e, "!", 1 );
00455         if ( strncmp( tag + 4, YAML_DOMAIN, strlen( YAML_DOMAIN ) ) == 0 ) {
00456             int skip = 4 + strlen( YAML_DOMAIN ) + 1;
00457             syck_emitter_write( e, tag + skip, taglen - skip );
00458         } else {
00459             const char *subd = tag + 4;
00460             while ( *subd != ':' && *subd != '\0' ) subd++;
00461             if ( *subd == ':' ) {
00462                 if ( subd - tag > ( (long)( strlen( YAML_DOMAIN ) + 5 )) &&
00463                      strncmp( subd - strlen( YAML_DOMAIN ), YAML_DOMAIN, strlen( YAML_DOMAIN ) ) == 0 ) {
00464                     syck_emitter_write( e, tag + 4, subd - strlen( YAML_DOMAIN ) - ( tag + 4 ) - 1 );
00465                     syck_emitter_write( e, "/", 1 );
00466                     syck_emitter_write( e, subd + 1, ( tag + taglen ) - ( subd + 1 ) );
00467                 } else {
00468                     syck_emitter_write( e, tag + 4, subd - ( tag + 4 ) );
00469                     syck_emitter_write( e, "/", 1 );
00470                     syck_emitter_write( e, subd + 1, ( tag + taglen ) - ( subd + 1 ) );
00471                 }
00472             } else {
00473                 /* TODO: Invalid tag (no colon after domain) */
00474                 return;
00475             }
00476         }
00477         syck_emitter_write( e, " ", 1 );
00478 
00479     /* private types */
00480     } else if ( strncmp( tag, "x-private:", 10 ) == 0 ) {
00481         syck_emitter_write( e, "!!", 2 );
00482         syck_emitter_write( e, tag + 10, strlen( tag ) - 10 );
00483         syck_emitter_write( e, " ", 1 );
00484     }
00485     lvl->anctag = 1;
00486 }
00487 
00488 /*
00489  * Emit a newline and an appropriately spaced indent.
00490  */
00491 void syck_emit_indent( SyckEmitter *e )
00492 {
00493     int i;
00494     SyckLevel *lvl = syck_emitter_current_level( e );
00495     if ( e->bufpos == 0 && ( e->marker - e->buffer ) == 0 ) return;
00496     if ( lvl->spaces >= 0 ) {
00497         char *spcs = S_ALLOC_N( char, lvl->spaces + 2 );
00498 
00499         spcs[0] = '\n'; spcs[lvl->spaces + 1] = '\0';
00500         for ( i = 0; i < lvl->spaces; i++ ) spcs[i+1] = ' ';
00501         syck_emitter_write( e, spcs, lvl->spaces + 1 );
00502         free( spcs );
00503     }
00504 }
00505 
00506 /* Clear the scan */
00507 #define SCAN_NONE       0
00508 /* All printable characters? */
00509 #define SCAN_NONPRINT   1
00510 /* Any indented lines? */
00511 #define SCAN_INDENTED   2
00512 /* Larger than the requested width? */
00513 #define SCAN_WIDE       4
00514 /* Opens or closes with whitespace? */
00515 #define SCAN_WHITEEDGE  8
00516 /* Contains a newline */
00517 #define SCAN_NEWLINE    16
00518 /* Contains a single quote */
00519 #define SCAN_SINGLEQ    32
00520 /* Contains a double quote */
00521 #define SCAN_DOUBLEQ    64
00522 /* Starts with a token */
00523 #define SCAN_INDIC_S    128
00524 /* Contains a flow indicator */
00525 #define SCAN_INDIC_C    256
00526 /* Ends without newlines */
00527 #define SCAN_NONL_E     512
00528 /* Ends with many newlines */
00529 #define SCAN_MANYNL_E   1024
00530 /* Contains flow map indicators */
00531 #define SCAN_FLOWMAP    2048
00532 /* Contains flow seq indicators */
00533 #define SCAN_FLOWSEQ    4096
00534 /* Contains a valid doc separator */
00535 #define SCAN_DOCSEP     8192
00536 
00537 /*
00538  * Basic printable test for LATIN-1 characters.
00539  */
00540 int
00541 syck_scan_scalar( int req_width, const char *cursor, long len )
00542 {
00543     long i = 0, start = 0;
00544     int flags = SCAN_NONE;
00545 
00546     if ( len < 1 )  return flags;
00547 
00548     /* c-indicators from the spec */
00549     if ( cursor[0] == '[' || cursor[0] == ']' ||
00550          cursor[0] == '{' || cursor[0] == '}' ||
00551          cursor[0] == '!' || cursor[0] == '*' ||
00552          cursor[0] == '&' || cursor[0] == '|' ||
00553          cursor[0] == '>' || cursor[0] == '\'' ||
00554          cursor[0] == '"' || cursor[0] == '#' ||
00555          cursor[0] == '%' || cursor[0] == '@' ||
00556          cursor[0] == '&' ) {
00557             flags |= SCAN_INDIC_S;
00558     }
00559     if ( ( cursor[0] == '-' || cursor[0] == ':' ||
00560            cursor[0] == '?' || cursor[0] == ',' ) &&
00561            ( len == 1 || cursor[1] == ' ' || cursor[1] == '\n' ) )
00562     {
00563             flags |= SCAN_INDIC_S;
00564     }
00565 
00566     /* whitespace edges */
00567     if ( cursor[len-1] != '\n' ) {
00568         flags |= SCAN_NONL_E;
00569     } else if ( len > 1 && cursor[len-2] == '\n' ) {
00570         flags |= SCAN_MANYNL_E;
00571     }
00572     if (
00573         ( len > 0 && ( cursor[0] == ' ' || cursor[0] == '\t' || cursor[0] == '\n' || cursor[0] == '\r' ) ) ||
00574         ( len > 1 && ( cursor[len-1] == ' ' || cursor[len-1] == '\t' ) )
00575     ) {
00576         flags |= SCAN_WHITEEDGE;
00577     }
00578 
00579     /* opening doc sep */
00580     if ( len >= 3 && strncmp( cursor, "---", 3 ) == 0 )
00581         flags |= SCAN_DOCSEP;
00582 
00583     /* scan string */
00584     for ( i = 0; i < len; i++ ) {
00585 
00586         if ( ! ( cursor[i] == 0x9 ||
00587                  cursor[i] == 0xA ||
00588                  cursor[i] == 0xD ||
00589                ( cursor[i] >= 0x20 && cursor[i] <= 0x7E ) )
00590         ) {
00591             flags |= SCAN_NONPRINT;
00592         }
00593         else if ( cursor[i] == '\n' ) {
00594             flags |= SCAN_NEWLINE;
00595             if ( len - i >= 3 && strncmp( &cursor[i+1], "---", 3 ) == 0 )
00596                 flags |= SCAN_DOCSEP;
00597             if ( cursor[i+1] == ' ' || cursor[i+1] == '\t' )
00598                 flags |= SCAN_INDENTED;
00599             if ( req_width > 0 && i - start > req_width )
00600                 flags |= SCAN_WIDE;
00601             start = i;
00602         }
00603         else if ( cursor[i] == '\'' )
00604         {
00605             flags |= SCAN_SINGLEQ;
00606         }
00607         else if ( cursor[i] == '"' )
00608         {
00609             flags |= SCAN_DOUBLEQ;
00610         }
00611         else if ( cursor[i] == ']' )
00612         {
00613             flags |= SCAN_FLOWSEQ;
00614         }
00615         else if ( cursor[i] == '}' )
00616         {
00617             flags |= SCAN_FLOWMAP;
00618         }
00619         /* remember, if plain collections get implemented, to add nb-plain-flow-char */
00620         else if ( ( cursor[i] == ' ' && cursor[i+1] == '#' ) ||
00621                   ( cursor[i] == ':' &&
00622                     ( cursor[i+1] == ' ' || cursor[i+1] == '\n' || i == len - 1 ) ) )
00623         {
00624             flags |= SCAN_INDIC_C;
00625         }
00626         else if ( cursor[i] == ',' &&
00627                   ( cursor[i+1] == ' ' || cursor[i+1] == '\n' || i == len - 1 ) )
00628         {
00629             flags |= SCAN_FLOWMAP;
00630             flags |= SCAN_FLOWSEQ;
00631         }
00632     }
00633 
00634     /* printf( "---STR---\n%s\nFLAGS: %d\n", cursor, flags ); */
00635     return flags;
00636 }
00637 /*
00638  * All scalars should be emitted through this function, which determines an appropriate style,
00639  * tag and indent.
00640  */
00641 void syck_emit_scalar( SyckEmitter *e, const char *tag, enum scalar_style force_style, int force_indent, int force_width,
00642                        char keep_nl, const char *str, long len )
00643 {
00644     enum scalar_style favor_style = scalar_literal;
00645     SyckLevel *parent = syck_emitter_parent_level( e );
00646     SyckLevel *lvl = syck_emitter_current_level( e );
00647     int scan = 0;
00648     const char *match_implicit;
00649     char *implicit;
00650 
00651     if ( str == NULL ) str = "";
00652 
00653     /* No empty nulls as map keys */
00654     if ( len == 0 && ( parent->status == syck_lvl_map || parent->status == syck_lvl_imap ) &&
00655          parent->ncount % 2 == 1 && syck_tagcmp( tag, "tag:yaml.org,2002:null" ) == 0 )
00656     {
00657         str = "~";
00658         len = 1;
00659     }
00660 
00661     scan = syck_scan_scalar( force_width, str, len );
00662     match_implicit = syck_match_implicit( str, len );
00663 
00664     /* quote strings which default to implicits */
00665     implicit = syck_taguri( YAML_DOMAIN, match_implicit, strlen( match_implicit ) );
00666     if ( syck_tagcmp( tag, implicit ) != 0 && syck_tagcmp( tag, "tag:yaml.org,2002:str" ) == 0 ) {
00667         force_style = scalar_2quote;
00668     } else {
00669         /* complex key */
00670         if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 &&
00671              ( !( tag == NULL ||
00672              ( implicit != NULL && syck_tagcmp( tag, implicit ) == 0 && e->explicit_typing == 0 ) ) ) )
00673         {
00674             syck_emitter_write( e, "? ", 2 );
00675             parent->status = syck_lvl_mapx;
00676         }
00677         syck_emit_tag( e, tag, implicit );
00678     }
00679     S_FREE( implicit );
00680 
00681     /* if still arbitrary, sniff a good block style. */
00682     if ( force_style == scalar_none ) {
00683         if ( scan & SCAN_NEWLINE ) {
00684             force_style = scalar_literal;
00685         } else {
00686             force_style = scalar_plain;
00687         }
00688     }
00689 
00690     if ( e->style == scalar_fold ) {
00691         favor_style = scalar_fold;
00692     }
00693 
00694     /* Determine block style */
00695     if ( scan & SCAN_NONPRINT ) {
00696         force_style = scalar_2quote;
00697     } else if ( scan & SCAN_WHITEEDGE ) {
00698         force_style = scalar_2quote;
00699     } else if ( force_style != scalar_fold && ( scan & SCAN_INDENTED ) ) {
00700         force_style = scalar_literal;
00701     } else if ( force_style == scalar_plain && ( scan & SCAN_NEWLINE ) ) {
00702         force_style = favor_style;
00703     } else if ( force_style == scalar_plain && parent->status == syck_lvl_iseq && ( scan & SCAN_FLOWSEQ ) ) {
00704         force_style = scalar_2quote;
00705     } else if ( force_style == scalar_plain && parent->status == syck_lvl_imap && ( scan & SCAN_FLOWMAP ) ) {
00706         force_style = scalar_2quote;
00707     /* } else if ( force_style == scalar_fold && ( ! ( scan & SCAN_WIDE ) ) ) {
00708         force_style = scalar_literal; */
00709     } else if ( force_style == scalar_plain && ( scan & SCAN_INDIC_S || scan & SCAN_INDIC_C ) ) {
00710         if ( scan & SCAN_NEWLINE ) {
00711             force_style = favor_style;
00712         } else {
00713             force_style = scalar_2quote;
00714         }
00715     }
00716 
00717     if ( force_indent > 0 ) {
00718         lvl->spaces = parent->spaces + force_indent;
00719     } else if ( scan & SCAN_DOCSEP ) {
00720         lvl->spaces = parent->spaces + e->indent;
00721     }
00722 
00723     /* For now, all ambiguous keys are going to be double-quoted */
00724     if ( ( parent->status == syck_lvl_map || parent->status == syck_lvl_mapx ) && parent->ncount % 2 == 1 ) {
00725         if ( force_style != scalar_plain ) {
00726             force_style = scalar_2quote;
00727         }
00728     }
00729 
00730     /* If the parent is an inline, double quote anything complex */
00731     if ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) {
00732         if ( force_style != scalar_plain && force_style != scalar_1quote ) {
00733             force_style = scalar_2quote;
00734         }
00735     }
00736 
00737     /* Fix the ending newlines */
00738     if ( scan & SCAN_NONL_E ) {
00739         keep_nl = NL_CHOMP;
00740     } else if ( scan & SCAN_MANYNL_E ) {
00741         keep_nl = NL_KEEP;
00742     }
00743 
00744     /* Write the text node */
00745     switch ( force_style )
00746     {
00747         case scalar_1quote:
00748             syck_emit_1quoted( e, force_width, str, len );
00749         break;
00750 
00751         case scalar_none:
00752         case scalar_2quote:
00753             syck_emit_2quoted( e, force_width, str, len );
00754         break;
00755 
00756         case scalar_fold:
00757             syck_emit_folded( e, force_width, keep_nl, str, len );
00758         break;
00759 
00760         case scalar_literal:
00761             syck_emit_literal( e, keep_nl, str, len );
00762         break;
00763 
00764         case scalar_plain:
00765             syck_emitter_write( e, str, len );
00766         break;
00767     }
00768 
00769     if ( parent->status == syck_lvl_mapx )
00770     {
00771         syck_emitter_write( e, "\n", 1 );
00772     }
00773 }
00774 
00775 void
00776 syck_emitter_escape( SyckEmitter *e, const char *src, long len )
00777 {
00778     int i;
00779     for( i = 0; i < len; i++ )
00780     {
00781         if( (src[i] < 0x20) || (0x7E < src[i]) )
00782         {
00783             syck_emitter_write( e, "\\", 1 );
00784             if( '\0' == src[i] )
00785                 syck_emitter_write( e, "0", 1 );
00786             else
00787             {
00788                 syck_emitter_write( e, "x", 1 );
00789                 syck_emitter_write( e, (const char *)hex_table + ((src[i] & 0xF0) >> 4), 1 );
00790                 syck_emitter_write( e, (const char *)hex_table + (src[i] & 0x0F), 1 );
00791             }
00792         }
00793         else
00794         {
00795             syck_emitter_write( e, src + i, 1 );
00796             if( '\\' == src[i] )
00797                 syck_emitter_write( e, "\\", 1 );
00798         }
00799     }
00800 }
00801 
00802 /*
00803  * Outputs a single-quoted block.
00804  */
00805 void
00806 syck_emit_1quoted( SyckEmitter *e, int width, const char *str, long len )
00807 {
00808     char do_indent = 0;
00809     const char *mark = str;
00810     const char *start = str;
00811     const char *end = str;
00812     syck_emitter_write( e, "'", 1 );
00813     while ( mark < str + len ) {
00814         if ( do_indent ) {
00815             syck_emit_indent( e );
00816             do_indent = 0;
00817         }
00818         switch ( *mark ) {
00819             case '\'':  syck_emitter_write( e, "'", 1 ); break;
00820 
00821             case '\n':
00822                 end = mark + 1;
00823                 if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) {
00824                     syck_emitter_write( e, "\n\n", 2 );
00825                 } else {
00826                     syck_emitter_write( e, "\n", 1 );
00827                 }
00828                 do_indent = 1;
00829                 start = mark + 1;
00830             break;
00831 
00832             case ' ':
00833                 if ( width > 0 && *start != ' ' && mark - end > width ) {
00834                     do_indent = 1;
00835                     end = mark + 1;
00836                 } else {
00837                     syck_emitter_write( e, " ", 1 );
00838                 }
00839             break;
00840 
00841             default:
00842                 syck_emitter_write( e, mark, 1 );
00843             break;
00844         }
00845         mark++;
00846     }
00847     syck_emitter_write( e, "'", 1 );
00848 }
00849 
00850 /*
00851  * Outputs a double-quoted block.
00852  */
00853 void
00854 syck_emit_2quoted( SyckEmitter *e, int width, const char *str, long len )
00855 {
00856     char do_indent = 0;
00857     const char *mark = str;
00858     const char *start = str;
00859     const char *end = str;
00860     syck_emitter_write( e, "\"", 1 );
00861     while ( mark < str + len ) {
00862         if ( do_indent > 0 ) {
00863             if ( do_indent == 2 ) {
00864                 syck_emitter_write( e, "\\", 1 );
00865             }
00866             syck_emit_indent( e );
00867             do_indent = 0;
00868         }
00869         switch ( *mark ) {
00870 
00871             /* Escape sequences allowed within double quotes. */
00872             case '"':  syck_emitter_write( e, "\\\"", 2 ); break;
00873             case '\\': syck_emitter_write( e, "\\\\", 2 ); break;
00874             case '\0': syck_emitter_write( e, "\\0",  2 ); break;
00875             case '\a': syck_emitter_write( e, "\\a",  2 ); break;
00876             case '\b': syck_emitter_write( e, "\\b",  2 ); break;
00877             case '\f': syck_emitter_write( e, "\\f",  2 ); break;
00878             case '\r': syck_emitter_write( e, "\\r",  2 ); break;
00879             case '\t': syck_emitter_write( e, "\\t",  2 ); break;
00880             case '\v': syck_emitter_write( e, "\\v",  2 ); break;
00881             case 0x1b: syck_emitter_write( e, "\\e",  2 ); break;
00882 
00883             case '\n':
00884                 end = mark + 1;
00885                 syck_emitter_write( e, "\\n", 2 );
00886                 do_indent = 2;
00887                 start = mark + 1;
00888                 if ( start < str + len && ( *start == ' ' || *start == '\n' ) ) {
00889                     do_indent = 0;
00890                 }
00891             break;
00892 
00893             case ' ':
00894                 if ( width > 0 && *start != ' ' && mark - end > width ) {
00895                     do_indent = 1;
00896                     end = mark + 1;
00897                 } else {
00898                     syck_emitter_write( e, " ", 1 );
00899                 }
00900             break;
00901 
00902             default:
00903                 syck_emitter_escape( e, mark, 1 );
00904             break;
00905         }
00906         mark++;
00907     }
00908     syck_emitter_write( e, "\"", 1 );
00909 }
00910 
00911 /*
00912  * Outputs a literal block.
00913  */
00914 void
00915 syck_emit_literal( SyckEmitter *e, char keep_nl, const char *str, long len )
00916 {
00917     const char *mark = str;
00918     const char *start = str;
00919     const char *end = str;
00920     syck_emitter_write( e, "|", 1 );
00921     if ( keep_nl == NL_CHOMP ) {
00922         syck_emitter_write( e, "-", 1 );
00923     } else if ( keep_nl == NL_KEEP ) {
00924         syck_emitter_write( e, "+", 1 );
00925     }
00926     syck_emit_indent( e );
00927     while ( mark < str + len ) {
00928         if ( *mark == '\n' ) {
00929             end = mark;
00930             if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) end += 1;
00931             syck_emitter_write( e, start, end - start );
00932             if ( mark + 1 == str + len ) {
00933                 if ( keep_nl != NL_KEEP ) syck_emitter_write( e, "\n", 1 );
00934             } else {
00935                 syck_emit_indent( e );
00936             }
00937             start = mark + 1;
00938         }
00939         mark++;
00940     }
00941     end = str + len;
00942     if ( start < end ) {
00943         syck_emitter_write( e, start, end - start );
00944     }
00945 }
00946 
00947 /*
00948  * Outputs a folded block.
00949  */
00950 void
00951 syck_emit_folded( SyckEmitter *e, int width, char keep_nl, const char *str, long len )
00952 {
00953     const char *mark = str;
00954     const char *start = str;
00955     const char *end = str;
00956     syck_emitter_write( e, ">", 1 );
00957     if ( keep_nl == NL_CHOMP ) {
00958         syck_emitter_write( e, "-", 1 );
00959     } else if ( keep_nl == NL_KEEP ) {
00960         syck_emitter_write( e, "+", 1 );
00961     }
00962     syck_emit_indent( e );
00963     if ( width <= 0 ) width = e->best_width;
00964     while ( mark < str + len ) {
00965         switch ( *mark ) {
00966             case '\n':
00967                 syck_emitter_write( e, end, mark - end );
00968                 end = mark + 1;
00969                 if ( *start != ' ' && *start != '\n' && *end != '\n' && *end != ' ' ) {
00970                     syck_emitter_write( e, "\n", 1 );
00971                 }
00972                 if ( mark + 1 == str + len ) {
00973                     if ( keep_nl != NL_KEEP ) syck_emitter_write( e, "\n", 1 );
00974                 } else {
00975                     syck_emit_indent( e );
00976                 }
00977                 start = mark + 1;
00978             break;
00979 
00980             case ' ':
00981                 if ( *start != ' ' ) {
00982                     if ( mark - end > width ) {
00983                         syck_emitter_write( e, end, mark - end );
00984                         syck_emit_indent( e );
00985                         end = mark + 1;
00986                     }
00987                 }
00988             break;
00989         }
00990         mark++;
00991     }
00992     if ( end < mark ) {
00993         syck_emitter_write( e, end, mark - end );
00994     }
00995 }
00996 
00997 /*
00998  * Begins emission of a sequence.
00999  */
01000 void syck_emit_seq( SyckEmitter *e, const char *tag, enum seq_style style )
01001 {
01002     SyckLevel *parent = syck_emitter_parent_level( e );
01003     SyckLevel *lvl = syck_emitter_current_level( e );
01004     syck_emit_tag( e, tag, "tag:yaml.org,2002:seq" );
01005     if ( style == seq_inline || ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) ) {
01006         syck_emitter_write( e, "[", 1 );
01007         lvl->status = syck_lvl_iseq;
01008     } else {
01009         /* complex key */
01010         if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 ) {
01011             syck_emitter_write( e, "? ", 2 );
01012             parent->status = syck_lvl_mapx;
01013         }
01014         lvl->status = syck_lvl_seq;
01015     }
01016 }
01017 
01018 /*
01019  * Begins emission of a mapping.
01020  */
01021 void
01022 syck_emit_map( SyckEmitter *e, const char *tag, enum map_style style )
01023 {
01024     SyckLevel *parent = syck_emitter_parent_level( e );
01025     SyckLevel *lvl = syck_emitter_current_level( e );
01026     syck_emit_tag( e, tag, "tag:yaml.org,2002:map" );
01027     if ( style == map_inline || ( parent->status == syck_lvl_imap || parent->status == syck_lvl_iseq ) ) {
01028         syck_emitter_write( e, "{", 1 );
01029         lvl->status = syck_lvl_imap;
01030     } else {
01031         /* complex key */
01032         if ( parent->status == syck_lvl_map && parent->ncount % 2 == 1 ) {
01033             syck_emitter_write( e, "? ", 2 );
01034             parent->status = syck_lvl_mapx;
01035         }
01036         lvl->status = syck_lvl_map;
01037     }
01038 }
01039 
01040 /*
01041  * Handles emitting of a collection item (for both
01042  * sequences and maps)
01043  */
01044 void syck_emit_item( SyckEmitter *e, st_data_t n )
01045 {
01046     SyckLevel *lvl = syck_emitter_current_level( e );
01047     switch ( lvl->status )
01048     {
01049         case syck_lvl_seq:
01050         {
01051             SyckLevel *parent = syck_emitter_parent_level( e );
01052 
01053             /* seq-in-map shortcut -- the lvl->anctag check should be unneccesary but
01054              * there is a nasty shift/reduce in the parser on this point and
01055              * i'm not ready to tickle it. */
01056             if ( lvl->anctag == 0 && parent->status == syck_lvl_map && lvl->ncount == 0 ) {
01057                 lvl->spaces = parent->spaces;
01058             }
01059 
01060             /* seq-in-seq shortcut */
01061             else if ( lvl->anctag == 0 && parent->status == syck_lvl_seq && lvl->ncount == 0 ) {
01062                 int spcs = ( lvl->spaces - parent->spaces ) - 2;
01063                 if ( spcs >= 0 ) {
01064                     int i = 0;
01065                     for ( i = 0; i < spcs; i++ ) {
01066                         syck_emitter_write( e, " ", 1 );
01067                     }
01068                     syck_emitter_write( e, "- ", 2 );
01069                     break;
01070                 }
01071             }
01072 
01073             syck_emit_indent( e );
01074             syck_emitter_write( e, "- ", 2 );
01075         }
01076         break;
01077 
01078         case syck_lvl_iseq:
01079         {
01080             if ( lvl->ncount > 0 ) {
01081                 syck_emitter_write( e, ", ", 2 );
01082             }
01083         }
01084         break;
01085 
01086         case syck_lvl_map:
01087         {
01088             SyckLevel *parent = syck_emitter_parent_level( e );
01089 
01090             /* map-in-seq shortcut */
01091             if ( lvl->anctag == 0 && parent->status == syck_lvl_seq && lvl->ncount == 0 ) {
01092                 int spcs = ( lvl->spaces - parent->spaces ) - 2;
01093                 if ( spcs >= 0 ) {
01094                     int i = 0;
01095                     for ( i = 0; i < spcs; i++ ) {
01096                         syck_emitter_write( e, " ", 1 );
01097                     }
01098                     break;
01099                 }
01100             }
01101 
01102             if ( lvl->ncount % 2 == 0 ) {
01103                 syck_emit_indent( e );
01104             } else {
01105                 syck_emitter_write( e, ": ", 2 );
01106             }
01107         }
01108         break;
01109 
01110         case syck_lvl_mapx:
01111         {
01112             if ( lvl->ncount % 2 == 0 ) {
01113                 syck_emit_indent( e );
01114                 lvl->status = syck_lvl_map;
01115             } else {
01116                 int i;
01117                 if ( lvl->spaces > 0 ) {
01118                     char *spcs = S_ALLOC_N( char, lvl->spaces + 1 );
01119 
01120                     spcs[lvl->spaces] = '\0';
01121                     for ( i = 0; i < lvl->spaces; i++ ) spcs[i] = ' ';
01122                     syck_emitter_write( e, spcs, lvl->spaces );
01123                     S_FREE( spcs );
01124                 }
01125                 syck_emitter_write( e, ": ", 2 );
01126             }
01127         }
01128         break;
01129 
01130         case syck_lvl_imap:
01131         {
01132             if ( lvl->ncount > 0 ) {
01133                 if ( lvl->ncount % 2 == 0 ) {
01134                     syck_emitter_write( e, ", ", 2 );
01135                 } else {
01136                     syck_emitter_write( e, ": ", 2 );
01137                 }
01138             }
01139         }
01140         break;
01141 
01142         default: break;
01143     }
01144     lvl->ncount++;
01145 
01146     syck_emit( e, n );
01147 }
01148 
01149 /*
01150  * Closes emission of a collection.
01151  */
01152 void syck_emit_end( SyckEmitter *e )
01153 {
01154     SyckLevel *lvl = syck_emitter_current_level( e );
01155     SyckLevel *parent = syck_emitter_parent_level( e );
01156     switch ( lvl->status )
01157     {
01158         case syck_lvl_seq:
01159             if ( lvl->ncount == 0 ) {
01160                 syck_emitter_write( e, "[]\n", 3 );
01161             } else if ( parent->status == syck_lvl_mapx ) {
01162                 syck_emitter_write( e, "\n", 1 );
01163             }
01164         break;
01165 
01166         case syck_lvl_iseq:
01167             syck_emitter_write( e, "]\n", 1 );
01168         break;
01169 
01170         case syck_lvl_map:
01171             if ( lvl->ncount == 0 ) {
01172                 syck_emitter_write( e, "{}\n", 3 );
01173             } else if ( lvl->ncount % 2 == 1 ) {
01174                 syck_emitter_write( e, ":\n", 1 );
01175             } else if ( parent->status == syck_lvl_mapx ) {
01176                 syck_emitter_write( e, "\n", 1 );
01177             }
01178         break;
01179 
01180         case syck_lvl_imap:
01181             syck_emitter_write( e, "}\n", 1 );
01182         break;
01183 
01184         default: break;
01185     }
01186 }
01187 
01188 /*
01189  * Fill markers table with emitter nodes in the
01190  * soon-to-be-emitted tree.
01191  */
01192 SYMID
01193 syck_emitter_mark_node( SyckEmitter *e, st_data_t n )
01194 {
01195     SYMID oid = 0;
01196     char *anchor_name = NULL;
01197 
01198     /*
01199      * Ensure markers table is initialized.
01200      */
01201     if ( e->markers == NULL )
01202     {
01203         e->markers = st_init_numtable();
01204     }
01205 
01206     /*
01207      * Markers table initially marks the string position of the
01208      * object.  Doesn't yet create an anchor, simply notes the
01209      * position.
01210      */
01211     if ( ! st_lookup( e->markers, n, (st_data_t *)&oid ) )
01212     {
01213         /*
01214          * Store all markers
01215          */
01216         oid = e->markers->num_entries + 1;
01217         st_insert( e->markers, n, (st_data_t)oid );
01218     }
01219     else
01220     {
01221         if ( e->anchors == NULL )
01222         {
01223             e->anchors = st_init_numtable();
01224         }
01225 
01226         if ( ! st_lookup( e->anchors, (st_data_t)oid, (void *)&anchor_name ) )
01227         {
01228             int idx = 0;
01229             const char *anc = ( e->anchor_format == NULL ? DEFAULT_ANCHOR_FORMAT : e->anchor_format );
01230 
01231             /*
01232              * Second time hitting this object, let's give it an anchor
01233              */
01234             idx = e->anchors->num_entries + 1;
01235             anchor_name = S_ALLOC_N( char, strlen( anc ) + 10 );
01236             S_MEMZERO( anchor_name, char, strlen( anc ) + 10 );
01237             sprintf( anchor_name, anc, idx );
01238 
01239             /*
01240              * Insert into anchors table
01241              */
01242             st_insert( e->anchors, (st_data_t)oid, (st_data_t)anchor_name );
01243         }
01244     }
01245     return oid;
01246 }
01247 
01248 

Generated on Wed Sep 8 2010 09:55:25 for Ruby by  doxygen 1.7.1