/* Copyright (C) 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License. */ #include "root.h" #include "file.h" #include "filter.h" #include "rules.h" #include "dict.h" #include "annote.h" #include "merge.h" /* delimiters for preformatted code */ static const char codeBegin[] = "
\n" ; static const char codeEnd[] = "\n\n"; /*. docText */ typedef struct docText { char *s; line *tag, *doc, **pdoc; BOOL used; unsigned mark; struct docText *next; } docText; /*. docTextAlloc */ static docText *docTextAlloc( void ) { docText *d; d = malloc( sizeof( *d )); d->next = 0; d->s = 0; d->tag = d->doc = 0; d->pdoc = 0; d->used = NO; d->mark = 0; return d; } /*. docTextFree */ static void docTextFree( docText *d ) { docText *p; while ( d != 0 ) { p = d->next; lineListFree( d->tag, markFree ); lineListFree( d->doc, markFree ); free( d ); d = p; } } /*. docTextAppend */ static docText *docTextAppend( docText ***ppt ) { docText *t; **ppt = t = docTextAlloc(); *ppt = &t->next; return t; } /*. textAdvance */ static void textAdvance( line ***ppl, BOOL par ) { filterMark *m; BOOL inPar, wasPar; inPar = wasPar = NO; while ( **ppl != 0 ) { m = (**ppl)->data; if ( m != 0 ) { if ( inPar ) { inPar = NO; } if ( m->mark == mergeBlank ) lineDelete( *ppl, markFree ); else break; } else { if ( !inPar ) { if ( wasPar ) lineInsert( lineFromStr( "\n" ), ppl ); wasPar = inPar = YES; } lineAdvance( ppl ); } } if ( par && wasPar ) lineInsert( lineFromStr( "\n" ), ppl ); } /*. docTextTransfer */ static void docTextTransfer( line ***ppdoc, docText ***pptext ) { line **pstart; docText *t; filterMark *m; pstart = *ppdoc; t = docTextAppend( pptext ); t->tag = **ppdoc; m = t->tag->data; assert( m != 0 && ( m->mark == mergeTag || m->mark == mergeGlobal || m->mark == mergeLocal )); t->mark = m->mark; t->s = t->tag->s + m->index; lineRemove( *ppdoc ); textAdvance( ppdoc, YES ); if ( *pstart != **ppdoc ) { t->doc = *pstart; t->pdoc = *ppdoc; lineListRemove( pstart, ppdoc ); } else t->doc = 0; } /*. substituteDoc */ static void substituteDoc( line ***ppsource, docText *text ) { filterMark *m; char *s, buf[LEN]; line *source; unsigned mark; BOOL anchor, ok; source = **ppsource; m = source->data; assert( m != 0 ); mark = m->mark; assert( m != 0 && ( m->mark == mergeTag || m->mark == mergeGlobal || m->mark == mergeLocal )); if ( mark == mergeGlobal || mark == mergeLocal ) { anchor = YES; ok = dictDefine( source, buf ); } else { anchor = NO; ok = YES; } s = source->s + m->index; if ( ok ) for ( ; text != 0; text = text->next ) if ( text->used == NO && text->mark == mark && strcmp( s, text->s ) == 0 ) { text->used = YES; lineDelete( *ppsource, markFree ); lineInsert( lineFromStr( codeEnd ), ppsource ); if ( anchor ) lineInsert( lineFromStr( buf ), ppsource ); if ( text->doc != 0 ) { lineListInsert( text->doc, text->pdoc, ppsource ); text->doc = 0; } lineInsert( lineFromStr( codeBegin ), ppsource ); return; } source->s[--source->len] = '\0'; lineInsert( lineFromStr( "\n" ), ppsource ); lineAdvance( ppsource ); lineInsert( lineFromStr( "\n\n" ), ppsource ); } /*. mergeSource */ static void mergeSource( line ***ppdoc, line *source ) { line *doc, **psource; docText *text, **ptext, *t; filterMark *m; /* save documentation in docText records */ textAdvance( ppdoc, YES ); text = 0; ptext = &text; while ( (doc = **ppdoc) != 0 ) { m = doc->data; assert( m != 0 ); if ( m->mark != mergeTag && m->mark != mergeGlobal && m->mark != mergeLocal ) break; docTextTransfer( ppdoc, &ptext ); } /* merge documentation into source lines */ psource = &source; lineInsert( lineFromStr( codeBegin ), &psource ); for ( textAdvance( &psource, NO ); *psource != 0; textAdvance( &psource, NO )) substituteDoc( &psource, text ); lineInsert( lineFromStr( codeEnd ), &psource ); /* remove empty pre.code elements */ for ( psource = &source; *psource!=0; lineAdvance( &psource )) while ( *psource !=0 && (*psource)->next != 0 && strcmp( (*psource)->s, codeBegin ) == 0 && strcmp( (*psource)->next->s, codeEnd ) == 0 ) { lineDelete( psource, 0 ); lineDelete( psource, 0 ); } /* splice source into doc */ lineListInsert( source, psource, ppdoc ); /* output unused documentation */ for ( t=text; t!=0; t=t->next ) { if ( t->used ) continue; lineInsert( lineFromStr( "
\n" ), ppdoc ); lineInsert( t->tag, ppdoc ); t->tag = 0; lineInsert( lineFromStr( "
\n\n" ), ppdoc ); if ( t->doc != 0 ) { lineListInsert( t->doc, t->pdoc, ppdoc ); t->doc = 0; } } docTextFree( text ); } /*. readSource */ static BOOL readSource( line ***ppdoc, line **psource, lineFilter *pfilter, char *path ) { line *doc; FILE *fin; filterMark *m; char c, *s, *t; char buf[BUFLEN]; *psource = 0; doc = **ppdoc; m = doc->data; assert( m != 0 && m->mark == mergeCmd ); s = doc->s + m->index; if ( *s == '\n' ) { lineDelete( *ppdoc, markFree ); return YES; } m = m->next; if ( m != 0 ) { assert( m->mark == mergeHtml ); doc->s[m->index] = '\0'; t = strrchr( s, '/' ); t != 0 ? ++t : ( t = s ); sprintf( buf, "\n" ), ppl ); lineAdvance( ppl ); lineInsert( lineFromStr( "
\n\n" ), ppl ); } /*. mergeDoc */ void mergeDoc( line **pdoc, filterTable *table, unsigned start, char *path ) { filterMark *m; line *source; lineFilter filter; filterState state; filter.filt = stateFilter; filter.next = 0; filter.data = &state; state.table = table; state.filt = 0; for ( textAdvance( &pdoc, YES ); *pdoc != 0; textAdvance( &pdoc, YES )) { m = (*pdoc)->data; assert( m->mark != 0 ); if ( m->mark == mergeCmd ) { state.state = start; if ( !readSource( &pdoc, &source, &filter, path )) mergeError( &pdoc ); else if ( source != 0 ) mergeSource( &pdoc, source ); } else mergeError( &pdoc ); } }