dict generates HTML anchor elements linking identifiers with their definitions.
Copyright © 2003, 2004 Dave Bayer. Subject to the terms and conditions of the MIT License.
Function prototypes:
void dictInit( void ); void dictFree( void ); void dictFilename( char *name ); BOOL dictDefine( line *l, char *buf ); unsigned dictFilter( char **ps, char **pt, unsigned state );
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 "merge.h" #include "html.h" #include "dict.h"
dictFilename sets the current HTML file for which dictDefine registers anchor names. name needs to be the unique, permanently allocated string naming this file; file names will be tested for equality by comparing addresses.
static char *filename;
void dictFilename( char *name )
{
filename = name;
}
A dictEntry binds a C identifier string name to an HTML file name file. name points to its own copy of the identifier string; file points to the unique, permanently allocated string naming the HTML file.
typedef struct dictEntry
{
char *name, *file;
BOOL local;
struct dictEntry *next;
} dictEntry;
dictHashTable is a hash table of dictEntry lists, implementing the anchor name dictionary.
dictEntry *dictHashTable[PRIME];
dictEntryNew allocates and initializes a dictEntry.
static dictEntry *dictEntryNew( char *name, BOOL local )
{
dictEntry *entry;
unsigned len;
entry = malloc( sizeof( *entry ));
len = strlen( name ) + 1;
entry->name = malloc( len * sizeof( char ));
strncpy( entry->name, name, len );
entry->file = filename;
entry->local = local;
entry->next = 0;
return entry;
}
dictFree frees the memory allocated by dict.
void dictFree( void )
{
int i;
dictEntry **pp, *p, *q;
for ( i=0, pp=dictHashTable; i<PRIME; ++i, ++pp)
for ( p=*pp; p!=0; )
{
q = p->next;
free( p->name );
free( p );
p = q;
}
}
dictEntryLocate is the common code for insertion and lookup into the hash table dictHashTable. It returns a handle to the dictEntry for name if found. Otherwise it returns a handle pointing to the end of the linked list where a new entry should be inserted.
static dictEntry **dictEntryLocate( char *name )
{
char *s;
unsigned temp, hash;
dictEntry *entry, **pentry;
hash = 0;
for ( s=name; *s!=0; ++s ) HASH( temp, hash, *s );
hash %= PRIME;
pentry = dictHashTable + hash;
while ( ( entry = *pentry ) != 0 )
{
if ( strcmp( entry->name, name ) == 0 &&
( !entry->local || strcmp( entry->file, filename ) == 0 ))
break;
pentry = &entry->next;
}
return pentry;
}
dictEntryInsert makes a new entry for name in the hash table dictHashTable, returning YES on success and NO if the name is already taken.
static BOOL dictEntryInsert( char *name, BOOL local )
{
dictEntry **pentry;
pentry = dictEntryLocate( name );
if ( *pentry != 0 ) return NO;
*pentry = dictEntryNew( name, local );
return YES;
}
dictEntryLookup looks up the C identifier name in the hash table dictHashTable, returning a dictEntry if name is found and 0 if name is not found.
static dictEntry *dictEntryLookup( char *name )
{
return *dictEntryLocate( name );
}
iscsym and iscsymf are traditional C facilities that are not found in ISO C. iscsym tests whether c is a character that may appear in a C identifier.
static BOOL iscsym (char c)
{
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || (c == '_') || ((c >= '0') && (c <= '9')) ? YES : NO;
}
iscsymf tests whether c is a character that may begin a C identifier.
static BOOL iscsymf (char c)
{
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || (c == '_') ? YES : NO;
}
dictDefine writes a string to buf defining an anchor name, and enters the name in an internal dictionary so that links can be generated. l is a line defining an anchor name. dictDefine returns YES on success, and NO if the name is malformed or already taken.
BOOL dictDefine( line *l, char *buf )
{
filterMark *m;
char c, *s, *t;
BOOL local, ok;
m = l->data;
assert( m != 0 && ( m->mark == mergeGlobal || m->mark == mergeLocal ));
local = m->mark == mergeLocal ? YES : NO;
Set s to the start of the first C identifier that follows the comment string, temporarily terminating this identifier with '\0'. A set of rules giving stateFilter this task would be verbose and less efficient.
s = l->s + m->index;
while ( !iscsymf( *s ))
if ( *s++ == '\0' ) return NO;
t = s;
while ( iscsym( *++t )) {}
c = *t;
*t = '\0';
sprintf( buf, "<h4>\n<a name=%s>%s</a>\n</h4>\n\n", s, s );
ok = dictEntryInsert( s, local );
*t = c;
return ok;
}
Note that if BUFLEN is set too small, dictFilter will write beyond allocated memory.
unsigned dictFilter( char **ps, char **pt, unsigned state )
{
char c, *s, buf[LEN];
dictEntry *entry;
if (( state == dictCode || state == dictSamp || state == dictPre )
&& iscsym( **ps ))
{
s = *ps;
while ( iscsym( *++s )) {}
c = *s;
*s = '\0';
entry = dictEntryLookup( *ps );
if ( entry != 0 )
{
if ( entry->file == filename )
sprintf( *pt, "<a href=\"#%s\">%s</a>", entry->name, entry->name );
else
{
makeRelativeName( filename, entry->file, buf, LEN );
sprintf( *pt, "<a href=\"%s#%s\">%s</a>", buf, entry->name, entry->name );
}
}
else
strcpy( *pt, *ps );
*ps = s;
*s = c;
*pt += strlen( *pt );
}
else
*(*pt)++ = *(*ps)++;
return state;
}