html handles file input and output, and calls merge to combine documentation .txt files with source files to form the .html documentation.
Copyright © 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License.
Function prototypes:
void makeRelativeName( const char *fromName, const char *toName, char *buf, int len ); void htmlFromDoc( int argc, const char *argv[] );
Copyright © 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 "merge.h" #include "html.h" #include "annote.h"
preamble is a format string for the text to be written at the beginning of each output .html file. It takes two string arguments: a title, and a path to the style file.
static const char preamble[] =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n"
"\t\"http://www.w3.org/TR/html4/strict.dtd\">\n"
"<html>\n"
"<head>\n"
"\t<title>%s</title>\n"
"\t<LINK REL=StyleSheet HREF=\"%s\" TYPE=\"text/css\">\n"
"</head>\n"
"<body>\n\n"
;
postamble is a format string for the text to be written at the end of each output .html file. It takes one string argument: the modification date.
static const char postamble[] =
"<hr>\n"
"<p class=footer>\n"
"Last modified %s.\n"
"</p>\n"
"</body>\n"
"</html>\n"
;
stylefile is a string representation of the style file for each output .html file. We make an array of strings here, so gcc -pedantic doesn't complain about the string length. stylefile can be updated using the command line tool cstring, part of this project.
static const char *stylefile[] =
{
"body",
"{",
"\tbackground: white;",
"\tcolor: black;",
"}",
"",
"body, table, strong, code, samp, pre",
"{",
"\tfont-family: lucida grande, geneva, helvetica, arial, sans-serif;",
"\tfont-size: 10.5pt;",
"\tfont-style: normal;",
"\tfont-weight: normal;",
"}",
"",
"pre",
"{",
"\tpadding-top: 0.6em;",
"\tpadding-left: 1.2em;",
"\tpadding-bottom: 0.8em;",
"}",
"",
"p.footer",
"{",
"\tfont-size: small;",
"\tfont-style: italic;",
"}",
"",
"pre.code",
"{",
"\tbackground: #dff;",
"\tborder: 1px solid gray;",
"}",
"",
"samp, pre.samp",
"{",
"\tcolor: #03f;",
"}",
"",
"strong, strong ul",
"{",
"\tbackground: #fcc;",
"}",
"",
"code a, samp a, pre a",
"{",
"\tcolor: #909;",
"\ttext-decoration: none;",
"}",
0
};
makeName allocates and returns a string containing the path prefix, which may be empty, of fileName.
static char *makePath( const char *fileName )
{
char *s, *name;
int offset;
s = strrchr( fileName, '/' );
offset = s == 0 ? 0 : s - fileName + 1;
name = malloc( ( offset + 1 ) * sizeof( char ));
strncpy( name, fileName, offset );
name[offset] = '\0';
return name;
}
makeName allocates and returns a string formed by removing the path if trunc is YES, and replacing the extension of fileName with the string ext, which may be empty.
static char *makeName( const char *fileName, char *ext, BOOL trunc )
{
char *s, *name;
int offset;
if ( trunc )
{
s = strrchr( fileName, '/' );
if ( s != 0 ) fileName = s + 1;
}
s = strrchr( fileName, '.' );
offset = s == 0 ? strlen( fileName ) : s - fileName;
name = malloc( ( offset + strlen( ext ) + 1 ) * sizeof( char ));
The next line originally called strcpy, which wrote beyond allocated memory when name is shorter than fileName. This bug was caught by defining MEMORYDEBUG.
strncpy( name, fileName, offset );
strcpy( name + offset, ext );
return name;
}
makeRelativeName writes a relative path that reaches toName from fromName, to buf of length len. It will not overwrite buf, and always terminates buf.
void makeRelativeName( const char *fromName, const char *toName, char *buf, int len )
{
int offset, up = 0;
BOOL agree = YES;
char *s;
while (( s = strchr( fromName, '/' )) != 0 )
{
offset = s - fromName + 1;
if ( agree && strncmp( fromName, toName, len ) == 0 )
toName += offset;
else
++up;
fromName += offset;
}
s = buf + len - 1;
while ( up-- > 0 && len >= 3 )
{
strcpy( buf, "../" );
buf += 3;
len -= 3;
}
strncpy( buf, toName, len );
*s = '\0';
}
makeDate writes the current date, to buf of length len. It will not overwrite buf, and always terminates buf.
static void makeDate( char *buf, int len )
{
time_t now;
now = time( 0 );
strftime( buf, len, "%B %e, %Y", localtime( &now ));
buf[ len-1 ] = '\0';
}
void htmlFromDoc( int nFiles, const char *fileName[] )
{
int i;
FILE *fin, *fout;
line **pDoc;
char **path, **truncName, **htmlName;
const char **ps;
lineFilter filter, filter2, filter3;
filterState state, state2, state3;
char style[LEN], date[LEN];
Initialize the four finite state machines used by annote.
sourceTable is used to read source files. docTable is used to read documentation .txt files, and reuses docRules as part of its definition. htmlTable is used to write HTML files, to escape <, >, and & where needed. dictTable is applied after htmlTable, to link C identifiers with their definitions.
initFilterTable( sourceTable, sourceRules, docNStates, YES );
initFilterTable( docTable, sourceRules, docNStates, YES );
initFilterTable( docTable, docRules, docNStates, NO );
initFilterTable( htmlTable, htmlRules, htmlNStates, YES );
initFilterTable( parTable, parRules, parNStates, YES );
initFilterTable( dictTable, dictRules, dictNStates, YES );
Set up filtering for reading documentation .txt files, create file name strings, and read each file.
filter.filt = stateFilter;
filter.next = 0;
filter.data = &state;
state.table = docTable;
state.filt = 0;
pDoc = malloc( nFiles * sizeof( *pDoc ));
path = malloc( nFiles * sizeof( *path ));
truncName = malloc( nFiles * sizeof( *truncName ));
htmlName = malloc( nFiles * sizeof( *htmlName ));
for ( i=0; i<nFiles; ++i )
{
path[i] = makePath( fileName[i] );
truncName[i] = makeName( fileName[i], "", YES );
htmlName[i] = makeName( fileName[i], ".html", NO );
dictFilename( htmlName[i] );
fin = fileOpen( fileName[i], "r", YES );
state.state = docStart;
pDoc[i] = fileRead( fin, &filter, 0 );
fileClose( fin, YES );
mergeDoc uses sourceTable with initial state docStart to read each source file requested by pDoc + i, and weaves in their contents to form the annotated source listing.
mergeDoc( pDoc + i, sourceTable, docStart, path[i] );
}
Set up the sequence of filters for writing .html output files, and write each file.
state.table = htmlTable;
state.filt = 0;
filter.next = &filter2;
filter2.filt = stateFilter;
filter2.next = &filter3;
filter2.data = &state2;
state2.table = parTable;
state2.filt = 0;
filter3.filt = stateFilter;
filter3.next = 0;
filter3.data = &state3;
state3.table = dictTable;
state3.filt = dictFilter;
makeDate( date, LEN );
for ( i=0; i<nFiles; ++i )
{
dictFilename( htmlName[i] );
fout = fileOpen( htmlName[i], "w", YES );
makeRelativeName( htmlName[i], "style.css", style, LEN );
fprintf( fout, preamble, truncName[i], style );
state.state = htmlDocStart;
state2.state = parStart;
state3.state = dictStart;
fileWrite( fout, pDoc[i], &filter );
fprintf( fout, postamble, date );
fileClose( fout, YES );
}
Unless the -nocss command line option is present, write the style file style.css.
if ( nocss == NO )
{
fout = fileOpen( "style.css", "w", YES );
for ( ps = stylefile; *ps != 0; ++ps )
fprintf( fout, "%s\n", *ps );
fileClose( fout, YES );
}
Free allocated memory. This is not necessary, because annote is about to terminate, but finding memory leaks is one way to find bugs.
dictFree();
for ( i=0; i<nFiles; ++i )
{
lineListFree( pDoc[i], markFree );
free( path[i] );
free( truncName[i] );
free( htmlName[i] );
}
free( pDoc );
free( path );
free( truncName );
free( htmlName );
Now check that all allocated memory has been freed.
mallocCount();
}