dict generates HTML anchor elements linking identifiers with their definitions.

dict.h

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 );

dict.c

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

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;
}

dictEntry

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

dictHashTable is a hash table of dictEntry lists, implementing the anchor name dictionary.

dictEntry *dictHashTable[PRIME];

dictEntryNew

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

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

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

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

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

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

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

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;
}

dictFilter

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;
}