Hacked up halibut source code

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@1534 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
kichik 2002-11-01 18:22:40 +00:00
parent 8204a7298a
commit b23696e64c
24 changed files with 12849 additions and 0 deletions

View file

@ -0,0 +1,21 @@
Halibut is copyright (c) 1999-2001 Simon Tatham and James Aylett.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,127 @@
/*
* biblio.c: process the bibliography
*/
#include <assert.h>
#include "halibut.h"
static wchar_t *
gentext (int num)
{
wchar_t text[22];
wchar_t *p = text + sizeof (text);
*--p = L'\0';
*--p = L']';
while (num != 0)
{
assert (p > text);
*--p = L"0123456789"[num % 10];
num /= 10;
}
assert (p > text);
*--p = L'[';
return ustrdup (p);
}
static void
cite_biblio (keywordlist * kl, wchar_t * key, filepos fpos)
{
keyword *kw = kw_lookup (kl, key);
if (!kw)
error (err_nosuchkw, &fpos, key);
else
{
/*
* We've found a \k reference. If it's a
* bibliography entry ...
*/
if (kw->para->type == para_Biblio)
{
/*
* ... then mark the paragraph as cited.
*/
kw->para->type = para_BiblioCited;
}
}
}
/*
* Make a pass through the source form, generating citation formats
* for bibliography entries and also marking which bibliography
* entries are actually cited (or \nocite-ed).
*/
void
gen_citations (paragraph * source, keywordlist * kl)
{
paragraph *para;
int bibnum = 0;
for (para = source; para; para = para->next)
{
word *ptr;
/*
* \BR and \nocite paragraphs get special processing here.
*/
if (para->type == para_BR)
{
keyword *kw = kw_lookup (kl, para->keyword);
if (!kw)
{
error (err_nosuchkw, &para->fpos, para->keyword);
}
else if (kw->text)
{
error (err_multiBR, &para->fpos, para->keyword);
}
else
{
kw->text = dup_word_list (para->words);
}
}
else if (para->type == para_NoCite)
{
wchar_t *wp = para->keyword;
while (*wp)
{
cite_biblio (kl, wp, para->fpos);
wp = uadv (wp);
}
}
/*
* Scan for keyword references.
*/
for (ptr = para->words; ptr; ptr = ptr->next)
{
if (ptr->type == word_UpperXref || ptr->type == word_LowerXref)
cite_biblio (kl, ptr->text, ptr->fpos);
}
}
/*
* We're now almost done; all that remains is to scan through
* the cited bibliography entries and invent default citation
* texts for the ones that don't already have explicitly
* provided \BR text.
*/
for (para = source; para; para = para->next)
{
if (para->type == para_BiblioCited)
{
keyword *kw = kw_lookup (kl, para->keyword);
assert (kw != NULL);
if (!kw->text)
{
word *wd = smalloc (sizeof (word));
wd->text = gentext (++bibnum);
wd->type = word_Normal;
wd->alt = NULL;
wd->next = NULL;
kw->text = wd;
}
para->kwtext = kw->text;
}
}
}

View file

@ -0,0 +1,751 @@
/*
* text backend for Halibut
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "halibut.h"
typedef enum
{ LEFT, LEFTPLUS, CENTRE }
alignment;
typedef struct
{
alignment align;
int just_numbers;
wchar_t underline;
wchar_t *number_suffix;
}
alignstruct;
typedef struct
{
int indent, indent_code;
int listindentbefore, listindentafter;
int width;
alignstruct atitle, achapter, *asect;
int nasect;
int include_version_id;
int indent_preambles;
word bullet;
}
textconfig;
static int text_convert (wchar_t *, char **);
static void text_heading (FILE *, word *, word *, word *, alignstruct, int,
int);
static void text_rule (FILE *, int, int);
static void text_para (FILE *, word *, char *, word *, int, int, int);
static void text_codepara (FILE *, word *, int, int);
static void text_versionid (FILE *, word *);
static alignment
utoalign (wchar_t * p)
{
if (!ustricmp (p, L"centre") || !ustricmp (p, L"center"))
return CENTRE;
if (!ustricmp (p, L"leftplus"))
return LEFTPLUS;
return LEFT;
}
static textconfig
text_configure (paragraph * source)
{
textconfig ret;
/*
* Non-negotiables.
*/
ret.bullet.next = NULL;
ret.bullet.alt = NULL;
ret.bullet.type = word_Normal;
ret.atitle.just_numbers = FALSE; /* ignored */
/*
* Defaults.
*/
ret.indent = 7;
ret.indent_code = 2;
ret.listindentbefore = 1;
ret.listindentafter = 3;
ret.width = 68;
ret.atitle.align = CENTRE;
ret.atitle.underline = L'=';
ret.achapter.align = LEFT;
ret.achapter.just_numbers = FALSE;
ret.achapter.number_suffix = ustrdup (L": ");
ret.achapter.underline = L'-';
ret.nasect = 1;
ret.asect = mknewa (alignstruct, ret.nasect);
ret.asect[0].align = LEFTPLUS;
ret.asect[0].just_numbers = TRUE;
ret.asect[0].number_suffix = ustrdup (L" ");
ret.asect[0].underline = L'\0';
ret.include_version_id = TRUE;
ret.indent_preambles = FALSE;
ret.bullet.text = ustrdup (L"-");
for (; source; source = source->next)
{
if (source->type == para_Config)
{
if (!ustricmp (source->keyword, L"text-indent"))
{
ret.indent = utoi (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-indent-code"))
{
ret.indent_code = utoi (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-width"))
{
ret.width = utoi (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-list-indent"))
{
ret.listindentbefore = utoi (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-listitem-indent"))
{
ret.listindentafter = utoi (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-chapter-align"))
{
ret.achapter.align = utoalign (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-chapter-underline"))
{
ret.achapter.underline = *uadv (source->keyword);
}
else if (!ustricmp (source->keyword, L"text-chapter-numeric"))
{
ret.achapter.just_numbers = utob (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-chapter-suffix"))
{
ret.achapter.number_suffix = ustrdup (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-section-align"))
{
wchar_t *p = uadv (source->keyword);
int n = 0;
if (uisdigit (*p))
{
n = utoi (p);
p = uadv (p);
}
if (n >= ret.nasect)
{
int i;
ret.asect = resize (ret.asect, n + 1);
for (i = ret.nasect; i <= n; i++)
ret.asect[i] = ret.asect[ret.nasect - 1];
ret.nasect = n + 1;
}
ret.asect[n].align = utoalign (p);
}
else if (!ustricmp (source->keyword, L"text-section-underline"))
{
wchar_t *p = uadv (source->keyword);
int n = 0;
if (uisdigit (*p))
{
n = utoi (p);
p = uadv (p);
}
if (n >= ret.nasect)
{
int i;
ret.asect = resize (ret.asect, n + 1);
for (i = ret.nasect; i <= n; i++)
ret.asect[i] = ret.asect[ret.nasect - 1];
ret.nasect = n + 1;
}
ret.asect[n].underline = *p;
}
else if (!ustricmp (source->keyword, L"text-section-numeric"))
{
wchar_t *p = uadv (source->keyword);
int n = 0;
if (uisdigit (*p))
{
n = utoi (p);
p = uadv (p);
}
if (n >= ret.nasect)
{
int i;
ret.asect = resize (ret.asect, n + 1);
for (i = ret.nasect; i <= n; i++)
ret.asect[i] = ret.asect[ret.nasect - 1];
ret.nasect = n + 1;
}
ret.asect[n].just_numbers = utob (p);
}
else if (!ustricmp (source->keyword, L"text-section-suffix"))
{
wchar_t *p = uadv (source->keyword);
int n = 0;
if (uisdigit (*p))
{
n = utoi (p);
p = uadv (p);
}
if (n >= ret.nasect)
{
int i;
ret.asect = resize (ret.asect, n + 1);
for (i = ret.nasect; i <= n; i++)
ret.asect[i] = ret.asect[ret.nasect - 1];
ret.nasect = n + 1;
}
ret.asect[n].number_suffix = ustrdup (p);
}
else if (!ustricmp (source->keyword, L"text-title-align"))
{
ret.atitle.align = utoalign (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-title-underline"))
{
ret.atitle.underline = *uadv (source->keyword);
}
else if (!ustricmp (source->keyword, L"text-versionid"))
{
ret.include_version_id = utob (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-indent-preamble"))
{
ret.indent_preambles = utob (uadv (source->keyword));
}
else if (!ustricmp (source->keyword, L"text-bullet"))
{
ret.bullet.text = uadv (source->keyword);
}
}
}
return ret;
}
void
text_backend (paragraph * sourceform, keywordlist * keywords, indexdata * idx)
{
paragraph *p;
textconfig conf;
word *prefix, *body, *wp;
word spaceword;
FILE *fp;
char *prefixextra;
int indentb, indenta;
IGNORE (keywords); /* we don't happen to need this */
IGNORE (idx); /* or this */
conf = text_configure (sourceform);
/*
* Determine the output file name, and open the output file
*
* FIXME: want configurable output file names here. For the
* moment, we'll just call it `output.txt'.
*/
fp = fopen ("output.txt", "w");
if (!fp)
{
error (err_cantopenw, "output.txt");
return;
}
/* Do the title */
for (p = sourceform; p; p = p->next)
if (p->type == para_Title)
text_heading (fp, NULL, NULL, p->words,
conf.atitle, conf.indent, conf.width);
/* Do the preamble and copyright */
for (p = sourceform; p; p = p->next)
if (p->type == para_Preamble)
text_para (fp, NULL, NULL, p->words,
conf.indent_preambles ? conf.indent : 0, 0,
conf.width + (conf.indent_preambles ? 0 : conf.indent));
for (p = sourceform; p; p = p->next)
if (p->type == para_Copyright)
text_para (fp, NULL, NULL, p->words,
conf.indent_preambles ? conf.indent : 0, 0,
conf.width + (conf.indent_preambles ? 0 : conf.indent));
/* Do the main document */
for (p = sourceform; p; p = p->next)
switch (p->type)
{
/*
* Things we ignore because we've already processed them or
* aren't going to touch them in this pass.
*/
case para_IM:
case para_BR:
case para_Biblio: /* only touch BiblioCited */
case para_VersionID:
case para_Copyright:
case para_Preamble:
case para_NoCite:
case para_Title:
break;
/*
* Chapter titles.
*/
case para_Chapter:
case para_Appendix:
case para_UnnumberedChapter:
text_heading (fp, p->kwtext, p->kwtext2, p->words,
conf.achapter, conf.indent, conf.width);
break;
case para_Heading:
case para_Subsect:
text_heading (fp, p->kwtext, p->kwtext2, p->words,
conf.asect[p->aux >=
conf.nasect ? conf.nasect - 1 : p->aux],
conf.indent, conf.width);
break;
case para_Rule:
text_rule (fp, conf.indent, conf.width);
break;
case para_Normal:
case para_BiblioCited:
case para_Bullet:
case para_NumberedList:
if (p->type == para_Bullet)
{
prefix = &conf.bullet;
prefixextra = NULL;
indentb = conf.listindentbefore;
indenta = conf.listindentafter;
}
else if (p->type == para_NumberedList)
{
prefix = p->kwtext;
prefixextra = "."; /* FIXME: configurability */
indentb = conf.listindentbefore;
indenta = conf.listindentafter;
}
else
{
prefix = NULL;
prefixextra = NULL;
indentb = indenta = 0;
}
if (p->type == para_BiblioCited)
{
body = dup_word_list (p->kwtext);
for (wp = body; wp->next; wp = wp->next);
wp->next = &spaceword;
spaceword.next = p->words;
spaceword.alt = NULL;
spaceword.type = word_WhiteSpace;
spaceword.text = NULL;
}
else
{
wp = NULL;
body = p->words;
}
text_para (fp, prefix, prefixextra, body,
conf.indent + indentb, indenta,
conf.width - indentb - indenta);
if (wp)
{
wp->next = NULL;
free_word_list (body);
}
break;
case para_Code:
text_codepara (fp, p->words, conf.indent + conf.indent_code,
conf.width - 2 * conf.indent_code);
break;
}
/* Do the version ID */
if (conf.include_version_id)
{
for (p = sourceform; p; p = p->next)
if (p->type == para_VersionID)
text_versionid (fp, p->words);
}
/*
* Tidy up
*/
fclose (fp);
{
int i;
sfree (conf.achapter.number_suffix);
for (i = 0; i < conf.nasect; i++)
sfree (conf.asect[i].number_suffix);
sfree (conf.asect);
sfree (conf.bullet.text);
}
}
/*
* Convert a wide string into a string of chars. If `result' is
* non-NULL, mallocs the resulting string and stores a pointer to
* it in `*result'. If `result' is NULL, merely checks whether all
* characters in the string are feasible for the output character
* set.
*
* Return is nonzero if all characters are OK. If not all
* characters are OK but `result' is non-NULL, a result _will_
* still be generated!
*/
static int
text_convert (wchar_t * s, char **result)
{
/*
* FIXME. Currently this is ISO8859-1 only.
*/
int doing = (result != 0);
int ok = TRUE;
char *p = NULL;
int plen = 0, psize = 0;
for (; *s; s++)
{
wchar_t c = *s;
char outc;
if ((c >= 32 && c <= 126) || (c >= 160 && c <= 255))
{
/* Char is OK. */
outc = (char) c;
}
else
{
/* Char is not OK. */
ok = FALSE;
outc = 0xBF; /* approximate the good old DEC `uh?' */
}
if (doing)
{
if (plen >= psize)
{
psize = plen + 256;
p = resize (p, psize);
}
p[plen++] = outc;
}
}
if (doing)
{
p = resize (p, plen + 1);
p[plen] = '\0';
*result = p;
}
return ok;
}
static void
text_rdaddwc (rdstringc * rs, word * text, word * end)
{
char *c;
for (; text && text != end; text = text->next)
switch (text->type)
{
case word_HyperLink:
case word_HyperEnd:
case word_UpperXref:
case word_LowerXref:
case word_XrefEnd:
case word_IndexRef:
break;
case word_Normal:
case word_Emph:
case word_Code:
case word_WeakCode:
case word_WhiteSpace:
case word_EmphSpace:
case word_CodeSpace:
case word_WkCodeSpace:
case word_Quote:
case word_EmphQuote:
case word_CodeQuote:
case word_WkCodeQuote:
assert (text->type != word_CodeQuote &&
text->type != word_WkCodeQuote);
if (towordstyle (text->type) == word_Emph &&
(attraux (text->aux) == attr_First ||
attraux (text->aux) == attr_Only))
rdaddc (rs, '_'); /* FIXME: configurability */
else if (towordstyle (text->type) == word_Code &&
(attraux (text->aux) == attr_First ||
attraux (text->aux) == attr_Only))
rdaddc (rs, '`'); /* FIXME: configurability */
if (removeattr (text->type) == word_Normal)
{
if (text_convert (text->text, &c))
rdaddsc (rs, c);
else
text_rdaddwc (rs, text->alt, NULL);
sfree (c);
}
else if (removeattr (text->type) == word_WhiteSpace)
{
rdaddc (rs, ' ');
}
else if (removeattr (text->type) == word_Quote)
{
rdaddc (rs, quoteaux (text->aux) == quote_Open ? '`' : '\'');
/* FIXME: configurability */
}
if (towordstyle (text->type) == word_Emph &&
(attraux (text->aux) == attr_Last ||
attraux (text->aux) == attr_Only))
rdaddc (rs, '_'); /* FIXME: configurability */
else if (towordstyle (text->type) == word_Code &&
(attraux (text->aux) == attr_Last ||
attraux (text->aux) == attr_Only))
rdaddc (rs, '\''); /* FIXME: configurability */
break;
}
}
static int text_width (word *);
static int
text_width_list (word * text)
{
int w = 0;
while (text)
{
w += text_width (text);
text = text->next;
}
return w;
}
static int
text_width (word * text)
{
switch (text->type)
{
case word_HyperLink:
case word_HyperEnd:
case word_UpperXref:
case word_LowerXref:
case word_XrefEnd:
case word_IndexRef:
return 0;
case word_Normal:
case word_Emph:
case word_Code:
case word_WeakCode:
return (((text->type == word_Emph ||
text->type == word_Code)
? (attraux (text->aux) == attr_Only ? 2 :
attraux (text->aux) == attr_Always ? 0 : 1)
: 0) +
(text_convert (text->text, NULL) ?
ustrlen (text->text) : text_width_list (text->alt)));
case word_WhiteSpace:
case word_EmphSpace:
case word_CodeSpace:
case word_WkCodeSpace:
case word_Quote:
case word_EmphQuote:
case word_CodeQuote:
case word_WkCodeQuote:
assert (text->type != word_CodeQuote && text->type != word_WkCodeQuote);
return (((towordstyle (text->type) == word_Emph ||
towordstyle (text->type) == word_Code)
? (attraux (text->aux) == attr_Only ? 2 :
attraux (text->aux) == attr_Always ? 0 : 1) : 0) + 1);
}
return 0; /* should never happen */
}
static void
text_heading (FILE * fp, word * tprefix, word * nprefix, word * text,
alignstruct align, int indent, int width)
{
rdstringc t = { 0, 0, NULL };
int margin, length;
int firstlinewidth, wrapwidth;
wrappedline *wrapping, *p;
if (align.just_numbers && nprefix)
{
char *c;
text_rdaddwc (&t, nprefix, NULL);
if (text_convert (align.number_suffix, &c))
{
rdaddsc (&t, c);
sfree (c);
}
}
else if (!align.just_numbers && tprefix)
{
char *c;
text_rdaddwc (&t, tprefix, NULL);
if (text_convert (align.number_suffix, &c))
{
rdaddsc (&t, c);
sfree (c);
}
}
margin = length = (t.text ? strlen (t.text) : 0);
if (align.align == LEFTPLUS)
{
margin = indent - margin;
if (margin < 0)
margin = 0;
firstlinewidth = indent + width - margin - length;
wrapwidth = width;
}
else if (align.align == LEFT || align.align == CENTRE)
{
margin = 0;
firstlinewidth = indent + width - length;
wrapwidth = indent + width;
}
wrapping = wrap_para (text, firstlinewidth, wrapwidth, text_width);
for (p = wrapping; p; p = p->next)
{
text_rdaddwc (&t, p->begin, p->end);
length = (t.text ? strlen (t.text) : 0);
if (align.align == CENTRE)
{
margin = (indent + width - length) / 2;
if (margin < 0)
margin = 0;
}
fprintf (fp, "%*s%s\n", margin, "", t.text);
if (align.underline != L'\0')
{
char *u, uc;
wchar_t uw[2];
uw[0] = align.underline;
uw[1] = L'\0';
text_convert (uw, &u);
uc = u[0];
sfree (u);
fprintf (fp, "%*s", margin, "");
while (length--)
putc (uc, fp);
putc ('\n', fp);
}
if (align.align == LEFTPLUS)
margin = indent;
else
margin = 0;
sfree (t.text);
t = empty_rdstringc;
}
wrap_free (wrapping);
putc ('\n', fp);
sfree (t.text);
}
static void
text_rule (FILE * fp, int indent, int width)
{
while (indent--)
putc (' ', fp);
while (width--)
putc ('-', fp); /* FIXME: configurability! */
putc ('\n', fp);
putc ('\n', fp);
}
static void
text_para (FILE * fp, word * prefix, char *prefixextra, word * text,
int indent, int extraindent, int width)
{
wrappedline *wrapping, *p;
rdstringc pfx = { 0, 0, NULL };
int e;
int firstlinewidth = width;
if (prefix)
{
text_rdaddwc (&pfx, prefix, NULL);
if (prefixextra)
rdaddsc (&pfx, prefixextra);
fprintf (fp, "%*s%s", indent, "", pfx.text);
/* If the prefix is too long, shorten the first line to fit. */
e = extraindent - strlen (pfx.text);
if (e < 0)
{
firstlinewidth += e; /* this decreases it, since e < 0 */
if (firstlinewidth < 0)
{
e = indent + extraindent;
firstlinewidth = width;
fprintf (fp, "\n");
}
else
e = 0;
}
sfree (pfx.text);
}
else
e = indent + extraindent;
wrapping = wrap_para (text, firstlinewidth, width, text_width);
for (p = wrapping; p; p = p->next)
{
rdstringc t = { 0, 0, NULL };
text_rdaddwc (&t, p->begin, p->end);
fprintf (fp, "%*s%s\n", e, "", t.text);
e = indent + extraindent;
sfree (t.text);
}
wrap_free (wrapping);
putc ('\n', fp);
}
static void
text_codepara (FILE * fp, word * text, int indent, int width)
{
for (; text; text = text->next)
if (text->type == word_WeakCode)
{
char *c;
text_convert (text->text, &c);
if (strlen (c) > (size_t) width)
{
/* FIXME: warn */
}
fprintf (fp, "%*s%s\n", indent, "", c);
sfree (c);
}
putc ('\n', fp);
}
static void
text_versionid (FILE * fp, word * text)
{
rdstringc t = { 0, 0, NULL };
rdaddc (&t, '['); /* FIXME: configurability */
text_rdaddwc (&t, text, NULL);
rdaddc (&t, ']'); /* FIXME: configurability */
fprintf (fp, "%s\n", t.text);
sfree (t.text);
}

View file

@ -0,0 +1,711 @@
/*
* Windows Help backend for Halibut
*
* TODO:
* - allow user to specify section contexts.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "halibut.h"
#include "winhelp.h"
struct bk_whlp_state
{
WHLP h;
indexdata *idx;
keywordlist *keywords;
WHLP_TOPIC curr_topic;
FILE *cntfp;
int cnt_last_level, cnt_workaround;
};
/*
* Indexes of fonts in our standard font descriptor set.
*/
enum
{
FONT_NORMAL,
FONT_EMPH,
FONT_CODE,
FONT_TITLE,
FONT_TITLE_EMPH,
FONT_TITLE_CODE,
FONT_RULE
};
static void whlp_rdaddwc (rdstringc * rs, word * text);
static int whlp_convert (wchar_t * s, char **result, int hard_spaces);
static void whlp_mkparagraph (struct bk_whlp_state *state,
int font, word * text, int subsidiary);
static void whlp_navmenu (struct bk_whlp_state *state, paragraph * p);
static void whlp_contents_write (struct bk_whlp_state *state,
int level, char *text, WHLP_TOPIC topic);
void
whlp_backend (paragraph * sourceform, keywordlist * keywords, indexdata * idx)
{
WHLP h;
char *filename, *cntname;
paragraph *p, *lastsect;
struct bk_whlp_state state;
WHLP_TOPIC contents_topic;
int i;
indexentry *ie;
filename = "output.hlp"; /* FIXME: configurability */
cntname = "output.cnt"; /* corresponding contents file */
state.cntfp = fopen (cntname, "wb");
state.cnt_last_level = -1;
state.cnt_workaround = 0;
h = state.h = whlp_new ();
state.keywords = keywords;
state.idx = idx;
whlp_start_macro (h, "CB(\"btn_about\",\"&About\",\"About()\")");
whlp_start_macro (h, "CB(\"btn_up\",\"&Up\",\"Contents()\")");
whlp_start_macro (h, "BrowseButtons()");
whlp_create_font (h, "Times New Roman", WHLP_FONTFAM_SERIF, 24, 0, 0, 0, 0);
whlp_create_font (h, "Times New Roman", WHLP_FONTFAM_SERIF, 24,
WHLP_FONT_ITALIC, 0, 0, 0);
whlp_create_font (h, "Courier New", WHLP_FONTFAM_FIXED, 24, 0, 0, 0, 0);
whlp_create_font (h, "Arial", WHLP_FONTFAM_SERIF, 30,
WHLP_FONT_BOLD, 0, 0, 0);
whlp_create_font (h, "Arial", WHLP_FONTFAM_SERIF, 30,
WHLP_FONT_BOLD | WHLP_FONT_ITALIC, 0, 0, 0);
whlp_create_font (h, "Courier New", WHLP_FONTFAM_FIXED, 30,
WHLP_FONT_BOLD, 0, 0, 0);
whlp_create_font (h, "Courier New", WHLP_FONTFAM_SANS, 18,
WHLP_FONT_STRIKEOUT, 0, 0, 0);
/*
* Loop over the source form finding out whether the user has
* specified particular help topic names for anything.
*/
for (p = sourceform; p; p = p->next)
{
p->private_data = NULL;
if (p->type == para_Config && p->parent)
{
if (!ustricmp (p->keyword, L"winhelp-topic"))
{
char *topicname;
whlp_convert (uadv (p->keyword), &topicname, 0);
/* Store the topic name in the private_data field of the
* containing section. */
p->parent->private_data = topicname;
}
}
}
/*
* Loop over the source form registering WHLP_TOPICs for
* everything.
*/
contents_topic = whlp_register_topic (h, "Top", NULL);
whlp_primary_topic (h, contents_topic);
for (p = sourceform; p; p = p->next)
{
if (p->type == para_Chapter ||
p->type == para_Appendix ||
p->type == para_UnnumberedChapter ||
p->type == para_Heading || p->type == para_Subsect)
{
char *topicid = p->private_data;
char *errstr;
p->private_data = whlp_register_topic (h, topicid, &errstr);
if (!p->private_data)
{
p->private_data = whlp_register_topic (h, NULL, NULL);
error (err_winhelp_ctxclash, &p->fpos, topicid, errstr);
}
sfree (topicid);
}
}
/*
* Loop over the index entries, preparing final text forms for
* each one.
*/
for (i = 0; (ie = index234 (idx->entries, i)) != NULL; i++)
{
rdstringc rs = { 0, 0, NULL };
whlp_rdaddwc (&rs, ie->text);
ie->backend_data = rs.text;
}
whlp_prepare (h);
/* ------------------------------------------------------------------
* Do the contents page, containing title, preamble and
* copyright.
*/
whlp_begin_topic (h, contents_topic, "Contents", "DB(\"btn_up\")", NULL);
/*
* The manual title goes in the non-scroll region, and also
* goes into the system title slot.
*/
{
rdstringc rs = { 0, 0, NULL };
for (p = sourceform; p; p = p->next)
{
if (p->type == para_Title)
{
whlp_begin_para (h, WHLP_PARA_NONSCROLL);
whlp_mkparagraph (&state, FONT_TITLE, p->words, FALSE);
whlp_rdaddwc (&rs, p->words);
whlp_end_para (h);
}
}
if (rs.text)
{
whlp_title (h, rs.text);
fprintf (state.cntfp, ":Title %s\r\n", rs.text);
sfree (rs.text);
}
whlp_contents_write (&state, 1, "Title page", contents_topic);
/* FIXME: configurability in that string */
}
/*
* Next comes the preamble, which just goes into the ordinary
* scrolling region.
*/
for (p = sourceform; p; p = p->next)
{
if (p->type == para_Preamble)
{
whlp_para_attr (h, WHLP_PARA_SPACEBELOW, 12);
whlp_begin_para (h, WHLP_PARA_SCROLL);
whlp_mkparagraph (&state, FONT_NORMAL, p->words, FALSE);
whlp_end_para (h);
}
}
/*
* The copyright goes to two places, again: into the contents
* page and also into the system section.
*/
{
rdstringc rs = { 0, 0, NULL };
for (p = sourceform; p; p = p->next)
{
if (p->type == para_Copyright)
{
whlp_para_attr (h, WHLP_PARA_SPACEBELOW, 12);
whlp_begin_para (h, WHLP_PARA_SCROLL);
whlp_mkparagraph (&state, FONT_NORMAL, p->words, FALSE);
whlp_end_para (h);
whlp_rdaddwc (&rs, p->words);
}
}
if (rs.text)
{
whlp_copyright (h, rs.text);
sfree (rs.text);
}
}
/*
* Now do the primary navigation menu.
*/
for (p = sourceform; p; p = p->next)
{
if (p->type == para_Chapter ||
p->type == para_Appendix || p->type == para_UnnumberedChapter)
whlp_navmenu (&state, p);
}
state.curr_topic = contents_topic;
lastsect = NULL;
/* ------------------------------------------------------------------
* Now we've done the contents page, we're ready to go through
* and do the main manual text. Ooh.
*/
for (p = sourceform; p; p = p->next)
switch (p->type)
{
/*
* Things we ignore because we've already processed them or
* aren't going to touch them in this pass.
*/
case para_IM:
case para_BR:
case para_Biblio: /* only touch BiblioCited */
case para_VersionID:
case para_Copyright:
case para_Preamble:
case para_NoCite:
case para_Title:
break;
/*
* Chapter and section titles: start a new Help topic.
*/
case para_Chapter:
case para_Appendix:
case para_UnnumberedChapter:
case para_Heading:
case para_Subsect:
if (lastsect && lastsect->child)
{
paragraph *q;
/*
* Do a navigation menu for the previous section we
* were in.
*/
for (q = lastsect->child; q; q = q->sibling)
whlp_navmenu (&state, q);
}
{
rdstringc rs = { 0, 0, NULL };
WHLP_TOPIC new_topic, parent_topic;
char *macro, *topicid;
new_topic = p->private_data;
whlp_browse_link (h, state.curr_topic, new_topic);
state.curr_topic = new_topic;
if (p->kwtext)
{
whlp_rdaddwc (&rs, p->kwtext);
rdaddsc (&rs, ": "); /* FIXME: configurability */
}
whlp_rdaddwc (&rs, p->words);
if (p->parent == NULL)
parent_topic = contents_topic;
else
parent_topic = (WHLP_TOPIC) p->parent->private_data;
topicid = whlp_topic_id (parent_topic);
macro = smalloc (100 + strlen (topicid));
sprintf (macro,
"CBB(\"btn_up\",\"JI(`',`%s')\");EB(\"btn_up\")", topicid);
whlp_begin_topic (h, new_topic,
rs.text ? rs.text : "", macro, NULL);
sfree (macro);
{
/*
* Output the .cnt entry.
*
* WinHelp has a bug involving having an internal
* node followed by a leaf at the same level: the
* leaf is output at the wrong level. We can mostly
* work around this by modifying the leaf level
* itself (see whlp_contents_write), but this
* doesn't work for top-level sections since we
* can't turn a level-1 leaf into a level-0 one. So
* for top-level leaf sections (Bibliography
* springs to mind), we output an internal node
* containing only the leaf for that section.
*/
int i;
paragraph *q;
/* Count up the level. */
i = 1;
for (q = p; q->parent; q = q->parent)
i++;
if (p->child || !p->parent)
{
/*
* If p has children then it needs to be a
* folder; if it has no parent then it needs to
* be a folder to work around the bug.
*/
whlp_contents_write (&state, i, rs.text, NULL);
i++;
}
whlp_contents_write (&state, i, rs.text, new_topic);
}
sfree (rs.text);
whlp_begin_para (h, WHLP_PARA_NONSCROLL);
if (p->kwtext)
{
whlp_mkparagraph (&state, FONT_TITLE, p->kwtext, FALSE);
whlp_set_font (h, FONT_TITLE);
whlp_text (h, ": "); /* FIXME: configurability */
}
whlp_mkparagraph (&state, FONT_TITLE, p->words, FALSE);
whlp_end_para (h);
lastsect = p;
}
break;
case para_Rule:
whlp_para_attr (h, WHLP_PARA_SPACEBELOW, 12);
whlp_para_attr (h, WHLP_PARA_ALIGNMENT, WHLP_ALIGN_CENTRE);
whlp_begin_para (h, WHLP_PARA_SCROLL);
whlp_set_font (h, FONT_RULE);
#define TEN "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"
#define TWENTY TEN TEN
#define FORTY TWENTY TWENTY
#define EIGHTY FORTY FORTY
whlp_text (h, EIGHTY);
#undef TEN
#undef TWENTY
#undef FORTY
#undef EIGHTY
whlp_end_para (h);
break;
case para_Normal:
case para_BiblioCited:
case para_Bullet:
case para_NumberedList:
whlp_para_attr (h, WHLP_PARA_SPACEBELOW, 12);
if (p->type == para_Bullet || p->type == para_NumberedList)
{
whlp_para_attr (h, WHLP_PARA_LEFTINDENT, 72);
whlp_para_attr (h, WHLP_PARA_FIRSTLINEINDENT, -36);
whlp_set_tabstop (h, 72, WHLP_ALIGN_LEFT);
whlp_begin_para (h, WHLP_PARA_SCROLL);
whlp_set_font (h, FONT_NORMAL);
if (p->type == para_Bullet)
{
whlp_text (h, "\x95");
}
else
{
whlp_mkparagraph (&state, FONT_NORMAL, p->kwtext, FALSE);
whlp_text (h, ".");
}
whlp_tab (h);
}
else
{
whlp_begin_para (h, WHLP_PARA_SCROLL);
}
if (p->type == para_BiblioCited)
{
whlp_mkparagraph (&state, FONT_NORMAL, p->kwtext, FALSE);
whlp_text (h, " ");
}
whlp_mkparagraph (&state, FONT_NORMAL, p->words, FALSE);
whlp_end_para (h);
break;
case para_Code:
/*
* In a code paragraph, each individual word is a line. For
* Help files, we will have to output this as a set of
* paragraphs, all but the last of which don't set
* SPACEBELOW.
*/
{
word *w;
char *c;
for (w = p->words; w; w = w->next)
{
if (!w->next)
whlp_para_attr (h, WHLP_PARA_SPACEBELOW, 12);
whlp_begin_para (h, WHLP_PARA_SCROLL);
whlp_set_font (h, FONT_CODE);
whlp_convert (w->text, &c, FALSE);
whlp_text (h, c);
sfree (c);
whlp_end_para (h);
}
}
break;
}
fclose (state.cntfp);
whlp_close (h, filename);
/*
* Loop over the index entries, cleaning up our final text
* forms.
*/
for (i = 0; (ie = index234 (idx->entries, i)) != NULL; i++)
{
sfree (ie->backend_data);
}
}
static void
whlp_contents_write (struct bk_whlp_state *state,
int level, char *text, WHLP_TOPIC topic)
{
/*
* Horrifying bug in WinHelp. When dropping a section level or
* more without using a folder-type entry, WinHelp accidentally
* adds one to the section level. So we correct for that here.
*/
if (state->cnt_last_level > level && topic)
state->cnt_workaround = -1;
else if (!topic)
state->cnt_workaround = 0;
state->cnt_last_level = level;
fprintf (state->cntfp, "%d ", level + state->cnt_workaround);
while (*text)
{
if (*text == '=')
fputc ('\\', state->cntfp);
fputc (*text, state->cntfp);
text++;
}
if (topic)
fprintf (state->cntfp, "=%s", whlp_topic_id (topic));
fputc ('\n', state->cntfp);
}
static void
whlp_navmenu (struct bk_whlp_state *state, paragraph * p)
{
whlp_begin_para (state->h, WHLP_PARA_NONSCROLL);
whlp_start_hyperlink (state->h, (WHLP_TOPIC) p->private_data);
if (p->kwtext)
{
whlp_mkparagraph (state, FONT_NORMAL, p->kwtext, TRUE);
whlp_set_font (state->h, FONT_NORMAL);
whlp_text (state->h, ": "); /* FIXME: configurability */
}
whlp_mkparagraph (state, FONT_NORMAL, p->words, TRUE);
whlp_end_hyperlink (state->h);
whlp_end_para (state->h);
}
static void
whlp_mkparagraph (struct bk_whlp_state *state,
int font, word * text, int subsidiary)
{
keyword *kwl;
int deffont = font;
int currfont = -1;
int newfont;
char *c;
paragraph *xref_target = NULL;
for (; text; text = text->next)
switch (text->type)
{
case word_HyperLink:
case word_HyperEnd:
break;
case word_IndexRef:
if (subsidiary)
break; /* disabled in subsidiary bits */
{
indextag *tag = index_findtag (state->idx, text->text);
int i;
if (!tag)
break;
for (i = 0; i < tag->nrefs; i++)
whlp_index_term (state->h, tag->refs[i]->backend_data,
state->curr_topic);
}
break;
case word_UpperXref:
case word_LowerXref:
if (subsidiary)
break; /* disabled in subsidiary bits */
kwl = kw_lookup (state->keywords, text->text);
assert (xref_target == NULL);
if (kwl->para->type == para_NumberedList)
{
break; /* don't xref to numbered list items */
}
else if (kwl->para->type == para_BiblioCited)
{
/*
* An xref to a bibliography item jumps to the section
* containing it.
*/
if (kwl->para->parent)
xref_target = kwl->para->parent;
else
break;
}
else
{
xref_target = kwl->para;
}
whlp_start_hyperlink (state->h,
(WHLP_TOPIC) xref_target->private_data);
break;
case word_XrefEnd:
if (subsidiary)
break; /* disabled in subsidiary bits */
if (xref_target)
whlp_end_hyperlink (state->h);
xref_target = NULL;
break;
case word_Normal:
case word_Emph:
case word_Code:
case word_WeakCode:
case word_WhiteSpace:
case word_EmphSpace:
case word_CodeSpace:
case word_WkCodeSpace:
case word_Quote:
case word_EmphQuote:
case word_CodeQuote:
case word_WkCodeQuote:
if (towordstyle (text->type) == word_Emph)
newfont = deffont + FONT_EMPH;
else if (towordstyle (text->type) == word_Code ||
towordstyle (text->type) == word_WeakCode)
newfont = deffont + FONT_CODE;
else
newfont = deffont;
if (newfont != currfont)
{
currfont = newfont;
whlp_set_font (state->h, newfont);
}
if (removeattr (text->type) == word_Normal)
{
if (whlp_convert (text->text, &c, TRUE))
whlp_text (state->h, c);
else
whlp_mkparagraph (state, deffont, text->alt, FALSE);
sfree (c);
}
else if (removeattr (text->type) == word_WhiteSpace)
{
whlp_text (state->h, " ");
}
else if (removeattr (text->type) == word_Quote)
{
whlp_text (state->h,
quoteaux (text->aux) == quote_Open ? "\x91" : "\x92");
/* FIXME: configurability */
}
break;
}
}
static void
whlp_rdaddwc (rdstringc * rs, word * text)
{
char *c;
for (; text; text = text->next)
switch (text->type)
{
case word_HyperLink:
case word_HyperEnd:
case word_UpperXref:
case word_LowerXref:
case word_XrefEnd:
case word_IndexRef:
break;
case word_Normal:
case word_Emph:
case word_Code:
case word_WeakCode:
case word_WhiteSpace:
case word_EmphSpace:
case word_CodeSpace:
case word_WkCodeSpace:
case word_Quote:
case word_EmphQuote:
case word_CodeQuote:
case word_WkCodeQuote:
assert (text->type != word_CodeQuote &&
text->type != word_WkCodeQuote);
if (removeattr (text->type) == word_Normal)
{
if (whlp_convert (text->text, &c, FALSE))
rdaddsc (rs, c);
else
whlp_rdaddwc (rs, text->alt);
sfree (c);
}
else if (removeattr (text->type) == word_WhiteSpace)
{
rdaddc (rs, ' ');
}
else if (removeattr (text->type) == word_Quote)
{
rdaddc (rs, quoteaux (text->aux) == quote_Open ? '\x91' : '\x92');
/* FIXME: configurability */
}
break;
}
}
/*
* Convert a wide string into a string of chars. If `result' is
* non-NULL, mallocs the resulting string and stores a pointer to
* it in `*result'. If `result' is NULL, merely checks whether all
* characters in the string are feasible for the output character
* set.
*
* Return is nonzero if all characters are OK. If not all
* characters are OK but `result' is non-NULL, a result _will_
* still be generated!
*/
static int
whlp_convert (wchar_t * s, char **result, int hard_spaces)
{
/*
* FIXME. Currently this is ISO8859-1 only.
*/
int doing = (result != 0);
int ok = TRUE;
char *p = NULL;
int plen = 0, psize = 0;
for (; *s; s++)
{
wchar_t c = *s;
char outc;
if ((c >= 32 && c <= 126) || (c >= 160 && c <= 255))
{
/* Char is OK. */
if (c == 32 && hard_spaces)
outc = '\240';
else
outc = (char) c;
}
else
{
/* Char is not OK. */
ok = FALSE;
outc = 0xBF; /* approximate the good old DEC `uh?' */
}
if (doing)
{
if (plen >= psize)
{
psize = plen + 256;
p = resize (p, psize);
}
p[plen++] = outc;
}
}
if (doing)
{
p = resize (p, plen + 1);
p[plen] = '\0';
*result = p;
}
return ok;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,258 @@
/*
* contents.c: build a table of contents
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include "halibut.h"
struct numberstate_Tag
{
int chapternum;
int appendixnum;
int ischapter;
int *sectionlevels;
paragraph **currentsects;
paragraph *lastsect;
int oklevel;
int maxsectlevel;
int listitem;
wchar_t *chaptertext; /* the word for a chapter */
wchar_t *sectiontext; /* the word for a section */
wchar_t *apptext; /* the word for an appendix */
};
numberstate *
number_init (void)
{
numberstate *ret = mknew (numberstate);
ret->chapternum = 0;
ret->appendixnum = -1;
ret->ischapter = 1;
ret->oklevel = -1; /* not even in a chapter yet */
ret->maxsectlevel = 32;
ret->sectionlevels = mknewa (int, ret->maxsectlevel);
ret->currentsects = mknewa (paragraph *, ret->maxsectlevel + 1);
memset (ret->currentsects, 0,
(ret->maxsectlevel + 1) * sizeof (paragraph *));
ret->lastsect = NULL;
ret->listitem = -1;
return ret;
}
void
number_free (numberstate * state)
{
sfree (state->sectionlevels);
sfree (state->currentsects);
sfree (state);
}
static void
dotext (word *** wret, wchar_t * text)
{
word *mnewword = mknew (word);
mnewword->text = ustrdup (text);
mnewword->type = word_Normal;
mnewword->alt = NULL;
mnewword->next = NULL;
**wret = mnewword;
*wret = &mnewword->next;
}
static void
dospace (word *** wret)
{
word *mnewword = mknew (word);
mnewword->text = NULL;
mnewword->type = word_WhiteSpace;
mnewword->alt = NULL;
mnewword->next = NULL;
**wret = mnewword;
*wret = &mnewword->next;
}
static void
donumber (word *** wret, int num)
{
wchar_t text[20];
wchar_t *p = text + sizeof (text);
*--p = L'\0';
while (num != 0)
{
assert (p > text);
*--p = L"0123456789"[num % 10];
num /= 10;
}
dotext (wret, p);
}
static void
doanumber (word *** wret, int num)
{
wchar_t text[20];
wchar_t *p;
int nletters, aton;
nletters = 1;
aton = 25;
while (num > aton)
{
nletters++;
num -= aton + 1;
if (aton < INT_MAX / 26)
aton = (aton + 1) * 26 - 1;
else
aton = INT_MAX;
}
p = text + sizeof (text);
*--p = L'\0';
while (nletters--)
{
assert (p > text);
*--p = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num % 26];
num /= 26;
}
dotext (wret, p);
}
void
number_cfg (numberstate * state, paragraph * source)
{
/*
* Defaults
*/
state->chaptertext = L"Chapter";
state->sectiontext = L"Section";
state->apptext = L"Appendix";
for (; source; source = source->next)
{
if (source->type == para_Config)
{
if (!ustricmp (source->keyword, L"chapter"))
{
state->chaptertext = uadv (source->keyword);
}
else if (!ustricmp (source->keyword, L"section"))
{
state->sectiontext = uadv (source->keyword);
}
else if (!ustricmp (source->keyword, L"appendix"))
{
state->apptext = uadv (source->keyword);
}
}
}
}
word *
number_mktext (numberstate * state, paragraph * p, wchar_t * category,
int prev, int *errflag)
{
word *ret = NULL;
word **ret2 = &ret;
word **pret = &ret;
int i, level;
level = -2; /* default for non-section-heading */
switch (p->type)
{
case para_Chapter:
state->chapternum++;
for (i = 0; i < state->maxsectlevel; i++)
state->sectionlevels[i] = 0;
dotext (&pret, category ? category : state->chaptertext);
dospace (&pret);
ret2 = pret;
donumber (&pret, state->chapternum);
state->ischapter = 1;
state->oklevel = 0;
level = -1;
break;
case para_Heading:
case para_Subsect:
level = (p->type == para_Heading ? 0 : p->aux);
if (level > state->oklevel)
{
error (err_sectjump, &p->fpos);
*errflag = TRUE;
ret = NULL;
break;
}
state->oklevel = level + 1;
if (state->maxsectlevel <= level)
{
state->maxsectlevel = level + 32;
state->sectionlevels = resize (state->sectionlevels,
state->maxsectlevel);
}
state->sectionlevels[level]++;
for (i = level + 1; i < state->maxsectlevel; i++)
state->sectionlevels[i] = 0;
dotext (&pret, category ? category : state->sectiontext);
dospace (&pret);
ret2 = pret;
if (state->ischapter)
donumber (&pret, state->chapternum);
else
doanumber (&pret, state->appendixnum);
for (i = 0; i <= level; i++)
{
dotext (&pret, L".");
if (state->sectionlevels[i] == 0)
state->sectionlevels[i] = 1;
donumber (&pret, state->sectionlevels[i]);
}
break;
case para_Appendix:
state->appendixnum++;
for (i = 0; i < state->maxsectlevel; i++)
state->sectionlevels[i] = 0;
dotext (&pret, category ? category : state->apptext);
dospace (&pret);
ret2 = pret;
doanumber (&pret, state->appendixnum);
state->ischapter = 0;
state->oklevel = 0;
level = -1;
break;
case para_UnnumberedChapter:
level = -1;
break;
case para_NumberedList:
ret2 = pret;
if (prev != para_NumberedList)
state->listitem = 0;
state->listitem++;
donumber (&pret, state->listitem);
break;
}
/*
* Now set up parent, child and sibling links.
*/
p->parent = p->child = p->sibling = NULL;
if (level != -2)
{
if (state->currentsects[level + 1])
state->currentsects[level + 1]->sibling = p;
if (level >= 0 && state->currentsects[level])
{
p->parent = state->currentsects[level];
if (!state->currentsects[level]->child)
state->currentsects[level]->child = p;
}
state->currentsects[level + 1] = state->lastsect = p;
for (i = level + 2; i < state->maxsectlevel + 1; i++)
state->currentsects[i] = NULL;
}
else
{
p->parent = state->lastsect;
}
p->kwtext2 = *ret2;
return ret;
}

View file

@ -0,0 +1,227 @@
/*
* error.c: Halibut error handling
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "halibut.h"
/*
* Error flags
*/
#define PREFIX 0x0001 /* give `halibut:' prefix */
#define FILEPOS 0x0002 /* give file position prefix */
static void
do_error (int code, va_list ap)
{
char error[1024];
char auxbuf[256];
char *sp, *sp2;
wchar_t *wsp;
filepos fpos, fpos2;
int flags;
switch (code)
{
case err_nomemory: /* no arguments */
sprintf (error, "out of memory");
flags = PREFIX;
break;
case err_optnoarg:
sp = va_arg (ap, char *);
sprintf (error, "option `-%.200s' requires an argument", sp);
flags = PREFIX;
break;
case err_nosuchopt:
sp = va_arg (ap, char *);
sprintf (error, "unrecognised option `-%.200s'", sp);
flags = PREFIX;
break;
case err_noinput: /* no arguments */
sprintf (error, "no input files");
flags = PREFIX;
break;
case err_cantopen:
sp = va_arg (ap, char *);
sprintf (error, "unable to open input file `%.200s'", sp);
flags = PREFIX;
break;
case err_nodata: /* no arguments */
sprintf (error, "no data in input files");
flags = PREFIX;
break;
case err_brokencodepara:
fpos = *va_arg (ap, filepos *);
sprintf (error, "every line of a code paragraph should begin `\\c'");
flags = FILEPOS;
break;
case err_kwunclosed:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected `}' after paragraph keyword");
flags = FILEPOS;
break;
case err_kwexpected:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected a paragraph keyword");
flags = FILEPOS;
break;
case err_kwillegal:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected no paragraph keyword");
flags = FILEPOS;
break;
case err_kwtoomany:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected only one paragraph keyword");
flags = FILEPOS;
break;
case err_bodyillegal:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected no text after paragraph keyword");
flags = FILEPOS;
break;
case err_badparatype:
wsp = va_arg (ap, wchar_t *);
sp = ustrtoa (wsp, auxbuf, sizeof (auxbuf));
fpos = *va_arg (ap, filepos *);
sprintf (error, "command `%.200s' unrecognised at start of"
" paragraph", sp);
flags = FILEPOS;
break;
case err_badmidcmd:
wsp = va_arg (ap, wchar_t *);
sp = ustrtoa (wsp, auxbuf, sizeof (auxbuf));
fpos = *va_arg (ap, filepos *);
sprintf (error, "command `%.200s' unexpected in mid-paragraph", sp);
flags = FILEPOS;
break;
case err_unexbrace:
fpos = *va_arg (ap, filepos *);
sprintf (error, "brace character unexpected in mid-paragraph");
flags = FILEPOS;
break;
case err_explbr:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected `{' after command");
flags = FILEPOS;
break;
case err_commenteof:
fpos = *va_arg (ap, filepos *);
sprintf (error, "end of file unexpected inside `\\#{...}' comment");
flags = FILEPOS;
break;
case err_kwexprbr:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected `}' after cross-reference");
flags = FILEPOS;
break;
case err_missingrbrace:
fpos = *va_arg (ap, filepos *);
sprintf (error, "unclosed braces at end of paragraph");
flags = FILEPOS;
break;
case err_nestedstyles:
fpos = *va_arg (ap, filepos *);
sprintf (error, "unable to nest text styles");
flags = FILEPOS;
break;
case err_nestedindex:
fpos = *va_arg (ap, filepos *);
sprintf (error, "unable to nest index markings");
flags = FILEPOS;
break;
case err_nosuchkw:
fpos = *va_arg (ap, filepos *);
wsp = va_arg (ap, wchar_t *);
sp = ustrtoa (wsp, auxbuf, sizeof (auxbuf));
sprintf (error, "unable to resolve cross-reference to `%.200s'", sp);
flags = FILEPOS;
break;
case err_multiBR:
fpos = *va_arg (ap, filepos *);
wsp = va_arg (ap, wchar_t *);
sp = ustrtoa (wsp, auxbuf, sizeof (auxbuf));
sprintf (error, "multiple `\\BR' entries given for `%.200s'", sp);
flags = FILEPOS;
break;
case err_nosuchidxtag:
wsp = va_arg (ap, wchar_t *);
sp = ustrtoa (wsp, auxbuf, sizeof (auxbuf));
sprintf (error, "`\\IM' on unknown index tag `%.200s'", sp);
flags = 0;
/* FIXME: need to get a filepos to here somehow */
break;
case err_cantopenw:
sp = va_arg (ap, char *);
sprintf (error, "unable to open output file `%.200s'", sp);
flags = PREFIX;
break;
case err_macroexists:
fpos = *va_arg (ap, filepos *);
wsp = va_arg (ap, wchar_t *);
sp = ustrtoa (wsp, auxbuf, sizeof (auxbuf));
sprintf (error, "macro `%.200s' already defined", sp);
flags = FILEPOS;
break;
case err_sectjump:
fpos = *va_arg (ap, filepos *);
sprintf (error, "expected higher heading levels before this one");
flags = FILEPOS;
break;
case err_winhelp_ctxclash:
fpos = *va_arg (ap, filepos *);
sp = va_arg (ap, char *);
sp2 = va_arg (ap, char *);
sprintf (error, "Windows Help context id `%.200s' clashes with "
"previously defined `%.200s'", sp, sp2);
flags = FILEPOS;
break;
case err_multikw:
fpos = *va_arg (ap, filepos *);
fpos2 = *va_arg (ap, filepos *);
wsp = va_arg (ap, wchar_t *);
sp = ustrtoa (wsp, auxbuf, sizeof (auxbuf));
sprintf (error, "paragraph keyword `%.200s' already defined at ", sp);
sprintf (error + strlen (error), "%s:%d", fpos2.filename, fpos2.line);
flags = FILEPOS;
break;
case err_whatever:
sp = va_arg (ap, char *);
vsprintf (error, sp, ap);
flags = PREFIX;
break;
}
if (flags & PREFIX)
fputs ("halibut: ", stderr);
if (flags & FILEPOS)
{
fprintf (stderr, "%s:%d:", fpos.filename, fpos.line);
if (fpos.col > 0)
fprintf (stderr, "%d:", fpos.col);
fputc (' ', stderr);
}
fputs (error, stderr);
fputc ('\n', stderr);
}
void
fatal (int code, ...)
{
va_list ap;
va_start (ap, code);
do_error (code, ap);
va_end (ap);
exit (EXIT_FAILURE);
}
void
error (int code, ...)
{
va_list ap;
va_start (ap, code);
do_error (code, ap);
va_end (ap);
}

View file

@ -0,0 +1,438 @@
#ifndef HALIBUT_HALIBUT_H
#define HALIBUT_HALIBUT_H
#include <stdio.h>
#include <wchar.h>
#include <time.h>
#ifdef __GNUC__
#define NORETURN __attribute__((__noreturn__))
#else
#define NORETURN /* nothing */
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* For suppressing unused-parameter warnings */
#define IGNORE(x) ( (x) = (x) )
#include "tree234.h"
/*
* Structure tags
*/
typedef struct input_Tag input;
typedef struct filepos_Tag filepos;
typedef struct paragraph_Tag paragraph;
typedef struct word_Tag word;
typedef struct keywordlist_Tag keywordlist;
typedef struct keyword_Tag keyword;
typedef struct userstyle_Tag userstyle;
typedef struct numberstate_Tag numberstate;
typedef struct indexdata_Tag indexdata;
typedef struct indextag_Tag indextag;
typedef struct indexentry_Tag indexentry;
typedef struct macrostack_Tag macrostack;
/*
* Data structure to hold a file name and index, a line and a
* column number, for reporting errors
*/
struct filepos_Tag
{
char *filename;
int line, col;
};
/*
* Data structure to hold all the file names etc for input
*/
typedef struct pushback_Tag
{
int chr;
filepos pos;
}
pushback;
struct input_Tag
{
char **filenames; /* complete list of input files */
int nfiles; /* how many in the list */
FILE *currfp; /* the currently open one */
int currindex; /* which one is that in the list */
pushback *pushback; /* pushed-back input characters */
int npushback, pushbacksize;
filepos pos;
int reportcols; /* report column numbers in errors */
macrostack *stack; /* macro expansions in force */
};
/*
* Data structure to hold the input form of the source, ie a linked
* list of paragraphs
*/
struct paragraph_Tag
{
paragraph *next;
int type;
wchar_t *keyword; /* for most special paragraphs */
word *words; /* list of words in paragraph */
int aux; /* number, in a numbered paragraph
* or subsection level
*/
word *kwtext; /* chapter/section indication */
word *kwtext2; /* numeric-only form of kwtext */
filepos fpos;
paragraph *parent, *child, *sibling; /* for hierarchy navigation */
void *private_data; /* for temp use in backends */
};
enum
{
para_IM, /* index merge */
para_BR, /* bibliography rewrite */
para_Rule, /* random horizontal rule */
para_Chapter,
para_Appendix,
para_UnnumberedChapter,
para_Heading,
para_Subsect,
para_Normal,
para_Biblio, /* causes no output unless turned ... */
para_BiblioCited, /* ... into this paragraph type */
para_Bullet,
para_NumberedList,
para_Code,
para_Copyright,
para_Preamble,
para_NoCite,
para_Title,
para_VersionID,
para_Config, /* configuration directive */
para_NotParaType /* placeholder value */
};
/*
* Data structure to hold an individual word
*/
struct word_Tag
{
word *next, *alt;
int type;
int aux;
int breaks; /* can a line break after it? */
wchar_t *text;
filepos fpos;
};
enum
{
/* ORDERING CONSTRAINT: these normal-word types ... */
word_Normal,
word_Emph,
word_Code, /* monospaced; `quoted' in text */
word_WeakCode, /* monospaced, normal in text */
/* ... must be in the same order as these space types ... */
word_WhiteSpace, /* text is NULL or ignorable */
word_EmphSpace, /* WhiteSpace when emphasised */
word_CodeSpace, /* WhiteSpace when code */
word_WkCodeSpace, /* WhiteSpace when weak code */
/* ... and must be in the same order as these quote types ... */
word_Quote, /* text is NULL or ignorable */
word_EmphQuote, /* Quote when emphasised */
word_CodeQuote, /* (can't happen) */
word_WkCodeQuote, /* (can't happen) */
/* END ORDERING CONSTRAINT */
word_internal_endattrs,
word_UpperXref, /* \K */
word_LowerXref, /* \k */
word_XrefEnd, /* (invisible; no text) */
word_IndexRef, /* (always an invisible one) */
word_HyperLink, /* (invisible) */
word_HyperEnd /* (also invisible; no text) */
};
/* aux values for attributed words */
enum
{
attr_Only = 0x0000, /* a lone word with the attribute */
attr_First = 0x0001, /* the first of a series */
attr_Last = 0x0002, /* the last of a series */
attr_Always = 0x0003, /* any other part of a series */
attr_mask = 0x0003,
};
/* aux values for quote-type words */
enum
{
quote_Open = 0x0010,
quote_Close = 0x0020,
quote_mask = 0x0030,
};
#define isattr(x) ( ( (x) > word_Normal && (x) < word_WhiteSpace ) || \
( (x) > word_WhiteSpace && (x) < word_internal_endattrs ) )
#define sameattr(x,y) ( (((x)-(y)) & 3) == 0 )
#define towordstyle(x) ( word_Normal + ((x) & 3) )
#define tospacestyle(x) ( word_WhiteSpace + ((x) & 3) )
#define toquotestyle(x) ( word_Quote + ((x) & 3) )
#define removeattr(x) ( word_Normal + ((x) &~ 3) )
#define attraux(x) ( (x) & attr_mask )
#define quoteaux(x) ( (x) & quote_mask )
/*
* error.c
*/
void
fatal (int code, ...)
NORETURN;
void error (int code, ...);
enum
{
err_nomemory, /* out of memory */
err_optnoarg, /* option `-%s' requires an argument */
err_nosuchopt, /* unrecognised option `-%s' */
err_noinput, /* no input files */
err_cantopen, /* unable to open input file `%s' */
err_nodata, /* no data in input files */
err_brokencodepara, /* line in codepara didn't begin `\c' */
err_kwunclosed, /* expected `}' after keyword */
err_kwillegal, /* paragraph type expects no keyword */
err_kwexpected, /* paragraph type expects a keyword */
err_kwtoomany, /* paragraph type expects only 1 */
err_bodyillegal, /* paragraph type expects only kws! */
err_badparatype, /* invalid command at start of para */
err_badmidcmd, /* invalid command in mid-para */
err_unexbrace, /* unexpected brace */
err_explbr, /* expected `{' after command */
err_commenteof, /* EOF inside braced comment */
err_kwexprbr, /* expected `}' after cross-ref */
err_missingrbrace, /* unclosed braces at end of para */
err_nestedstyles, /* unable to nest text styles */
err_nestedindex, /* unable to nest `\i' thingys */
err_nosuchkw, /* unresolved cross-reference */
err_multiBR, /* multiple \BRs on same keyword */
err_nosuchidxtag, /* \IM on unknown index tag (warning) */
err_cantopenw, /* can't open output file for write */
err_macroexists, /* this macro already exists */
err_sectjump, /* jump a heading level, eg \C -> \S */
err_winhelp_ctxclash, /* WinHelp context ID hash clash */
err_multikw, /* keyword clash in sections */
err_whatever /* random error of another type */
};
/*
* malloc.c
*/
#ifdef LOGALLOC
void *smalloc (char *file, int line, int size);
void *srealloc (char *file, int line, void *p, int size);
void sfree (char *file, int line, void *p);
#define smalloc(x) smalloc(__FILE__, __LINE__, x)
#define srealloc(x, y) srealloc(__FILE__, __LINE__, x, y)
#define sfree(x) sfree(__FILE__, __LINE__, x)
#else
void *smalloc (int size);
void *srealloc (void *p, int size);
void sfree (void *p);
#endif
void free_word_list (word * w);
void free_para_list (paragraph * p);
word *dup_word_list (word * w);
char *dupstr (char *s);
#define mknew(type) ( (type *) smalloc (sizeof (type)) )
#define mknewa(type, number) ( (type *) smalloc ((number) * sizeof (type)) )
#define resize(array, len) ( srealloc ((array), (len) * sizeof (*(array))) )
#define lenof(array) ( sizeof(array) / sizeof(*(array)) )
/*
* ustring.c
*/
wchar_t *ustrdup (wchar_t * s);
char *ustrtoa (wchar_t * s, char *outbuf, int size);
int ustrlen (wchar_t * s);
wchar_t *uadv (wchar_t * s);
wchar_t *ustrcpy (wchar_t * dest, wchar_t * source);
wchar_t utolower (wchar_t);
int ustrcmp (wchar_t * lhs, wchar_t * rhs);
int ustricmp (wchar_t * lhs, wchar_t * rhs);
int utoi (wchar_t *);
int utob (wchar_t *);
int uisdigit (wchar_t);
wchar_t *ustrlow (wchar_t * s);
wchar_t *ustrftime (wchar_t * fmt, struct tm *timespec);
/*
* help.c
*/
void help (void);
void usage (void);
void showversion (void);
/*
* licence.c
*/
void licence (void);
/*
* version.c
*/
const char *const version;
/*
* misc.c
*/
typedef struct stackTag *stack;
stack stk_new (void);
void stk_free (stack);
void stk_push (stack, void *);
void *stk_pop (stack);
typedef struct tagRdstring rdstring;
struct tagRdstring
{
int pos, size;
wchar_t *text;
};
typedef struct tagRdstringc rdstringc;
struct tagRdstringc
{
int pos, size;
char *text;
};
extern const rdstring empty_rdstring;
extern const rdstringc empty_rdstringc;
void rdadd (rdstring * rs, wchar_t c);
void rdadds (rdstring * rs, wchar_t * p);
wchar_t *rdtrim (rdstring * rs);
void rdaddc (rdstringc * rs, char c);
void rdaddsc (rdstringc * rs, char *p);
char *rdtrimc (rdstringc * rs);
int compare_wordlists (word * a, word * b);
void mark_attr_ends (paragraph * sourceform);
typedef struct tagWrappedLine wrappedline;
struct tagWrappedLine
{
wrappedline *next;
word *begin, *end; /* first & last words of line */
int nspaces; /* number of whitespaces in line */
int shortfall; /* how much shorter than max width */
};
wrappedline *wrap_para (word *, int, int, int (*)(word *));
void wrap_free (wrappedline *);
/*
* input.c
*/
paragraph *read_input (input * in, indexdata * idx);
/*
* keywords.c
*/
struct keywordlist_Tag
{
int nkeywords;
int size;
tree234 *keys; /* sorted by `key' field */
word **looseends; /* non-keyword list element numbers */
int nlooseends;
int looseendssize;
};
struct keyword_Tag
{
wchar_t *key; /* the keyword itself */
word *text; /* "Chapter 2", "Appendix Q"... */
/* (NB: filepos are not set) */
paragraph *para; /* the paragraph referenced */
};
keyword *kw_lookup (keywordlist *, wchar_t *);
keywordlist *get_keywords (paragraph *);
void free_keywords (keywordlist *);
void subst_keywords (paragraph *, keywordlist *);
/*
* index.c
*/
/*
* Data structure to hold both sides of the index.
*/
struct indexdata_Tag
{
tree234 *tags; /* holds type `indextag' */
tree234 *entries; /* holds type `indexentry' */
};
/*
* Data structure to hold an index tag (LHS of index).
*/
struct indextag_Tag
{
wchar_t *name;
word *implicit_text;
word **explicit_texts;
int nexplicit, explicit_size;
int nrefs;
indexentry **refs; /* array of entries referenced by tag */
};
/*
* Data structure to hold an index entry (RHS of index).
*/
struct indexentry_Tag
{
word *text;
void *backend_data; /* private to back end */
};
indexdata *make_index (void);
void cleanup_index (indexdata *);
/* index_merge takes responsibility for freeing arg 3 iff implicit; never
* takes responsibility for arg 2 */
void index_merge (indexdata *, int is_explicit, wchar_t *, word *);
void build_index (indexdata *);
void index_debug (indexdata *);
indextag *index_findtag (indexdata * idx, wchar_t * name);
/*
* contents.c
*/
numberstate *number_init (void);
void number_cfg (numberstate *, paragraph *);
word *number_mktext (numberstate *, paragraph *, wchar_t *, int, int *);
void number_free (numberstate *);
/*
* biblio.c
*/
void gen_citations (paragraph *, keywordlist *);
/*
* style.c
*/
struct userstyle_Tag
{
};
/*
* bk_text.c
*/
void text_backend (paragraph *, keywordlist *, indexdata *);
/*
* bk_xhtml.c
*/
void xhtml_backend (paragraph *, keywordlist *, indexdata *);
/*
* bk_whlp.c
*/
void whlp_backend (paragraph *, keywordlist *, indexdata *);
#endif

View file

@ -0,0 +1,38 @@
/*
* help.c: usage instructions
*/
#include <stdio.h>
#include "halibut.h"
static char *helptext[] = {
"FIXME: help text goes here",
NULL
};
static char *usagetext[] = {
"FIXME: usage text goes here",
NULL
};
void
help (void)
{
char **p;
for (p = helptext; *p; p++)
puts (*p);
}
void
usage (void)
{
char **p;
for (p = usagetext; *p; p++)
puts (*p);
}
void
showversion (void)
{
printf ("Halibut, %s\n", version);
}

View file

@ -0,0 +1,278 @@
/*
* index.c: create and collate index data structures
*/
#include <stdio.h>
#include <stdlib.h>
#include "halibut.h"
static int compare_tags (void *av, void *bv);
static int compare_entries (void *av, void *bv);
indexdata *
make_index (void)
{
indexdata *ret = mknew (indexdata);
ret->tags = newtree234 (compare_tags);
ret->entries = newtree234 (compare_entries);
return ret;
}
static indextag *
make_indextag (void)
{
indextag *ret = mknew (indextag);
ret->name = NULL;
ret->implicit_text = NULL;
ret->explicit_texts = NULL;
ret->nexplicit = ret->explicit_size = ret->nrefs = 0;
ret->refs = NULL;
return ret;
}
static int
compare_tags (void *av, void *bv)
{
indextag *a = (indextag *) av, *b = (indextag *) bv;
return ustricmp (a->name, b->name);
}
static int
compare_to_find_tag (void *av, void *bv)
{
wchar_t *a = (wchar_t *) av;
indextag *b = (indextag *) bv;
return ustricmp (a, b->name);
}
static int
compare_entries (void *av, void *bv)
{
indexentry *a = (indexentry *) av, *b = (indexentry *) bv;
return compare_wordlists (a->text, b->text);
}
/*
* Back-end utility: find the indextag with a given name.
*/
indextag *
index_findtag (indexdata * idx, wchar_t * name)
{
return find234 (idx->tags, name, compare_to_find_tag);
}
/*
* Add a \IM. `tags' points to a zero-terminated chain of
* zero-terminated strings ("first\0second\0thirdandlast\0\0").
* `text' points to a word list.
*
* Guarantee on calling sequence: all implicit merges are given
* before the explicit ones.
*/
void
index_merge (indexdata * idx, int is_explicit, wchar_t * tags, word * text)
{
indextag *t, *existing;
/*
* FIXME: want to warn on overlapping source sets.
*/
for (; *tags; tags = uadv (tags))
{
t = make_indextag ();
t->name = tags;
existing = add234 (idx->tags, t);
if (existing == t)
{
/*
* Duplicate this so we can free it independently.
*/
t->name = ustrdup (tags);
/*
* Every tag has an implicit \IM. So if this tag
* doesn't exist and we're explicit, then we should
* warn (and drop it, since it won't be referenced).
*/
if (is_explicit)
{
error (err_nosuchidxtag, tags);
continue;
}
/*
* Otherwise, this is a new tag with an implicit \IM.
*/
t->implicit_text = text;
}
else
{
sfree (t);
t = existing;
if (!is_explicit)
{
/*
* An implicit \IM for a tag that's had an implicit
* \IM before. FIXME: we should check the text
* against the existing text and warn on
* differences. And check the tag for case match
* against the existing tag, likewise.
*/
}
else
{
/*
* An explicit \IM added to a valid tag. In
* particular, this removes the implicit \IM if
* present.
*/
if (t->implicit_text)
{
free_word_list (t->implicit_text);
t->implicit_text = NULL;
}
if (t->nexplicit >= t->explicit_size)
{
t->explicit_size = t->nexplicit + 8;
t->explicit_texts = resize (t->explicit_texts,
t->explicit_size);
}
t->explicit_texts[t->nexplicit++] = text;
}
}
}
}
/*
* Build the final-form index. We now have every tag, with every
* \IM, set up in a 2-3 tree indexed by tag. We now want to collate
* the RHSes of the \IMs, and sort by final form, and decorate the
* entries in the original 2-3 tree with pointers to the RHS
* entries.
*/
void
build_index (indexdata * i)
{
indextag *t;
word **ta;
int ti;
int j;
for (ti = 0; (t = (indextag *) index234 (i->tags, ti)) != NULL; ti++)
{
if (t->implicit_text)
{
t->nrefs = 1;
ta = &t->implicit_text;
}
else
{
t->nrefs = t->nexplicit;
ta = t->explicit_texts;
}
if (t->nrefs)
{
t->refs = mknewa (indexentry *, t->nrefs);
for (j = 0; j < t->nrefs; j++)
{
indexentry *ent = mknew (indexentry);
ent->text = *ta++;
t->refs[j] = add234 (i->entries, ent);
if (t->refs[j] != ent) /* duplicate */
sfree (ent);
}
}
}
}
void
cleanup_index (indexdata * i)
{
indextag *t;
indexentry *ent;
int ti;
for (ti = 0; (t = (indextag *) index234 (i->tags, ti)) != NULL; ti++)
{
sfree (t->name);
free_word_list (t->implicit_text);
sfree (t->explicit_texts);
sfree (t->refs);
sfree (t);
}
freetree234 (i->tags);
for (ti = 0; (ent = (indexentry *) index234 (i->entries, ti)) != NULL; ti++)
{
sfree (ent);
}
freetree234 (i->entries);
sfree (i);
}
static void dbg_prtwordlist (int level, word * w);
static void dbg_prtmerge (int is_explicit, wchar_t * tag, word * text);
void
index_debug (indexdata * i)
{
indextag *t;
indexentry *y;
int ti;
int j;
printf ("\nINDEX TAGS\n==========\n\n");
for (ti = 0; (t = (indextag *) index234 (i->tags, ti)) != NULL; ti++)
{
printf ("\n");
if (t->implicit_text)
dbg_prtmerge (0, t->name, t->implicit_text);
for (j = 0; j < t->nexplicit; j++)
dbg_prtmerge (1, t->name, t->explicit_texts[j]);
}
printf ("\nINDEX ENTRIES\n=============\n\n");
for (ti = 0; (y = (indexentry *) index234 (i->entries, ti)) != NULL; ti++)
{
printf ("\n");
printf ("{\n");
dbg_prtwordlist (1, y->text);
printf ("}\n");
}
}
static void
dbg_prtmerge (int is_explicit, wchar_t * tag, word * text)
{
printf ("\\IM: %splicit: \"", is_explicit ? "ex" : "im");
for (; *tag; tag++)
putchar (*tag);
printf ("\" {\n");
dbg_prtwordlist (1, text);
printf ("}\n");
}
static void
dbg_prtwordlist (int level, word * w)
{
for (; w; w = w->next)
{
wchar_t *wp;
printf ("%*sword %d ", level * 4, "", w->type);
if (w->text)
{
printf ("\"");
for (wp = w->text; *wp; wp++)
putchar (*wp);
printf ("\"");
}
else
printf ("(no text)");
if (w->alt)
{
printf (" alt = {\n");
dbg_prtwordlist (level + 1, w->alt);
printf ("%*s}", level * 4, "");
}
printf ("\n");
}
}

1516
Docs/src/bin/halibut/input.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,179 @@
/*
* keywords.c: keep track of all cross-reference keywords
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "halibut.h"
static int
kwcmp (void *av, void *bv)
{
const keyword *a = (const keyword *) av;
const keyword *b = (const keyword *) bv;
return ustrcmp (a->key, b->key);
}
static int
kwfind (void *av, void *bv)
{
wchar_t *a = (wchar_t *) av;
const keyword *b = (const keyword *) bv;
return ustrcmp (a, b->key);
}
keyword *
kw_lookup (keywordlist * kl, wchar_t * str)
{
return find234 (kl->keys, str, kwfind);
}
/*
* This function reads through source form and collects the
* keywords. They get collected in a heap, sorted by Unicode
* collation, last at the top (so that we can Heapsort them when we
* finish).
*/
keywordlist *
get_keywords (paragraph * source)
{
int errors = FALSE;
keywordlist *kl = mknew (keywordlist);
numberstate *n = number_init ();
int prevpara = para_NotParaType;
number_cfg (n, source);
kl->size = 0;
kl->keys = newtree234 (kwcmp);
kl->nlooseends = kl->looseendssize = 0;
kl->looseends = NULL;
for (; source; source = source->next)
{
wchar_t *p, *q;
p = q = source->keyword;
/*
* Look for the section type override (`example',
* `question' or whatever - to replace `chapter' or
* `section' on a per-section basis).
*/
if (q)
{
q = uadv (q); /* point q at the word beyond */
if (!*q)
q = NULL;
}
/*
* Number the chapter / section / list-item / whatever.
* This also sets up the `parent', `child' and `sibling'
* links.
*/
source->kwtext = number_mktext (n, source, q, prevpara, &errors);
prevpara = source->type;
if (p && *p)
{
if (source->kwtext || source->type == para_Biblio)
{
keyword *kw, *ret;
kw = mknew (keyword);
kw->key = p;
kw->text = source->kwtext;
kw->para = source;
ret = add234 (kl->keys, kw);
if (ret != kw)
{
error (err_multikw, &source->fpos, &ret->para->fpos, p);
sfree (kw);
/* FIXME: what happens to kw->text? Does it leak? */
}
}
}
else
{
if (kl->nlooseends >= kl->looseendssize)
{
kl->looseendssize = kl->nlooseends + 32;
kl->looseends = resize (kl->looseends, kl->looseendssize);
}
kl->looseends[kl->nlooseends++] = source->kwtext;
}
}
number_free (n);
if (errors)
{
free_keywords (kl);
return NULL;
}
return kl;
}
void
free_keywords (keywordlist * kl)
{
keyword *kw;
while (kl->nlooseends)
free_word_list (kl->looseends[--kl->nlooseends]);
sfree (kl->looseends);
while ((kw = index234 (kl->keys, 0)) != NULL)
{
delpos234 (kl->keys, 0);
free_word_list (kw->text);
sfree (kw);
}
freetree234 (kl->keys);
sfree (kl);
}
void
subst_keywords (paragraph * source, keywordlist * kl)
{
for (; source; source = source->next)
{
word *ptr;
for (ptr = source->words; ptr; ptr = ptr->next)
{
if (ptr->type == word_UpperXref || ptr->type == word_LowerXref)
{
keyword *kw;
word **endptr, *close, *subst;
kw = kw_lookup (kl, ptr->text);
if (!kw)
{
error (err_nosuchkw, &ptr->fpos, ptr->text);
subst = NULL;
}
else
subst = dup_word_list (kw->text);
if (subst && ptr->type == word_LowerXref &&
kw->para->type != para_Biblio &&
kw->para->type != para_BiblioCited)
ustrlow (subst->text);
close = mknew (word);
close->text = NULL;
close->alt = NULL;
close->type = word_XrefEnd;
close->fpos = ptr->fpos;
close->next = ptr->next;
ptr->next = subst;
for (endptr = &ptr->next; *endptr; endptr = &(*endptr)->next)
(*endptr)->fpos = ptr->fpos;
*endptr = close;
ptr = close;
}
}
}
}

View file

@ -0,0 +1,18 @@
/*
* licence.c: licence text
*/
#include <stdio.h>
static char *licencetext[] = {
"FIXME: licence text goes here",
NULL
};
void
licence (void)
{
char **p;
for (p = licencetext; *p; p++)
puts (*p);
}

343
Docs/src/bin/halibut/main.c Normal file
View file

@ -0,0 +1,343 @@
/*
* main.c: command line parsing and top level
*/
#include <stdio.h>
#include <stdlib.h>
#include "halibut.h"
static void dbg_prtsource (paragraph * sourceform);
static void dbg_prtwordlist (int level, word * w);
static void dbg_prtkws (keywordlist * kws);
int
main (int argc, char **argv)
{
char **infiles;
char *outfile;
int nfiles;
int nogo;
int errs;
int reportcols;
int debug;
/*
* Set up initial (default) parameters.
*/
infiles = mknewa (char *, argc);
outfile = NULL;
nfiles = 0;
nogo = errs = FALSE;
reportcols = 0;
debug = 0;
if (argc == 1)
{
usage ();
exit (EXIT_SUCCESS);
}
/*
* Parse command line arguments.
*/
while (--argc)
{
char *p = *++argv;
if (*p == '-')
{
/*
* An option.
*/
while (p && *++p)
{
char c = *p;
switch (c)
{
case '-':
/*
* Long option.
*/
{
char *opt, *val;
opt = p++; /* opt will have _one_ leading - */
while (*p && *p != '=')
p++; /* find end of option */
if (*p == '=')
{
*p++ = '\0';
val = p;
}
else
val = NULL;
if (!strcmp (opt, "-help"))
{
help ();
nogo = TRUE;
}
else if (!strcmp (opt, "-version"))
{
showversion ();
nogo = TRUE;
}
else if (!strcmp (opt, "-licence") ||
!strcmp (opt, "-license"))
{
licence ();
nogo = TRUE;
}
else if (!strcmp (opt, "-output"))
{
if (!val)
errs = TRUE, error (err_optnoarg, opt);
else
outfile = val;
}
else if (!strcmp (opt, "-precise"))
{
reportcols = 1;
}
else
{
errs = TRUE, error (err_nosuchopt, opt);
}
}
p = NULL;
break;
case 'h':
case 'V':
case 'L':
case 'P':
case 'd':
/*
* Option requiring no parameter.
*/
switch (c)
{
case 'h':
help ();
nogo = TRUE;
break;
case 'V':
showversion ();
nogo = TRUE;
break;
case 'L':
licence ();
nogo = TRUE;
break;
case 'P':
reportcols = 1;
break;
case 'd':
debug = TRUE;
break;
}
break;
case 'o':
/*
* Option requiring parameter.
*/
p++;
if (!*p && argc > 1)
--argc, p = *++argv;
else if (!*p)
{
char opt[2];
opt[0] = c;
opt[1] = '\0';
errs = TRUE, error (err_optnoarg, opt);
}
/*
* Now c is the option and p is the parameter.
*/
switch (c)
{
case 'o':
outfile = p;
break;
}
p = NULL; /* prevent continued processing */
break;
default:
/*
* Unrecognised option.
*/
{
char opt[2];
opt[0] = c;
opt[1] = '\0';
errs = TRUE, error (err_nosuchopt, opt);
}
}
}
}
else
{
/*
* A non-option argument.
*/
infiles[nfiles++] = p;
}
}
if (errs)
exit (EXIT_FAILURE);
if (nogo)
exit (EXIT_SUCCESS);
/*
* Do the work.
*/
if (nfiles == 0)
{
error (err_noinput);
usage ();
exit (EXIT_FAILURE);
}
{
input in;
paragraph *sourceform, *p;
indexdata *idx;
keywordlist *keywords;
in.filenames = infiles;
in.nfiles = nfiles;
in.currfp = NULL;
in.currindex = 0;
in.npushback = in.pushbacksize = 0;
in.pushback = NULL;
in.reportcols = reportcols;
in.stack = NULL;
idx = make_index ();
sourceform = read_input (&in, idx);
if (!sourceform)
exit (EXIT_FAILURE);
sfree (in.pushback);
mark_attr_ends (sourceform);
sfree (infiles);
keywords = get_keywords (sourceform);
if (!keywords)
exit (EXIT_FAILURE);
gen_citations (sourceform, keywords);
subst_keywords (sourceform, keywords);
for (p = sourceform; p; p = p->next)
if (p->type == para_IM)
index_merge (idx, TRUE, p->keyword, p->words);
build_index (idx);
if (debug)
{
index_debug (idx);
dbg_prtkws (keywords);
dbg_prtsource (sourceform);
}
text_backend (sourceform, keywords, idx);
xhtml_backend (sourceform, keywords, idx);
whlp_backend (sourceform, keywords, idx);
free_para_list (sourceform);
free_keywords (keywords);
cleanup_index (idx);
}
return 0;
}
static void
dbg_prtsource (paragraph * sourceform)
{
/*
* Output source form in debugging format.
*/
paragraph *p;
for (p = sourceform; p; p = p->next)
{
wchar_t *wp;
printf ("para %d ", p->type);
if (p->keyword)
{
wp = p->keyword;
while (*wp)
{
putchar ('\"');
for (; *wp; wp++)
putchar (*wp);
putchar ('\"');
if (*++wp)
printf (", ");
}
}
else
printf ("(no keyword)");
printf (" {\n");
dbg_prtwordlist (1, p->words);
printf ("}\n");
}
}
static void
dbg_prtkws (keywordlist * kws)
{
/*
* Output keywords in debugging format.
*/
int i;
keyword *kw;
for (i = 0; (kw = index234 (kws->keys, i)) != NULL; i++)
{
wchar_t *wp;
printf ("keyword ");
wp = kw->key;
while (*wp)
{
putchar ('\"');
for (; *wp; wp++)
putchar (*wp);
putchar ('\"');
if (*++wp)
printf (", ");
}
printf (" {\n");
dbg_prtwordlist (1, kw->text);
printf ("}\n");
}
}
static void
dbg_prtwordlist (int level, word * w)
{
for (; w; w = w->next)
{
wchar_t *wp;
printf ("%*sword %d ", level * 4, "", w->type);
if (w->text)
{
printf ("\"");
for (wp = w->text; *wp; wp++)
putchar (*wp);
printf ("\"");
}
else
printf ("(no text)");
if (w->alt)
{
printf (" alt = {\n");
dbg_prtwordlist (level + 1, w->alt);
printf ("%*s}", level * 4, "");
}
printf ("\n");
}
}

View file

@ -0,0 +1,22 @@
OBJS = biblio.o bk_text.o bk_whlp.o bk_xhtml.o contents.o error.o help.o index.o input.o keywords.o licence.o main.o malloc.o misc.o style.o tree234.o ustring.o version.o winhelp.o
LIBS =
# -- Programs --
MAKE = make
CC = gcc
RM = del
# -- Compilers and linker flags --
DEFINES =
CFLAGS = -Wall -W $(DEFINES)
LFLAGS = -s
all : halibut
halibut : $(OBJS)
$(CC) $(CFLAGS) $(LFLAGS) -o ..\halibut.exe $(OBJS) $(LIBS)
clean ::
$(RM) *.o
$(RM) ..\halibut.exe

View file

@ -0,0 +1,173 @@
/*
* malloc.c: safe wrappers around malloc, realloc, free, strdup
*/
#include <stdlib.h>
#include <stdarg.h>
#include "halibut.h"
#ifdef LOGALLOC
#define LOGPARAMS char *file, int line,
static FILE *logallocfp = NULL;
static int logline = 2; /* off by 1: `null pointer is' */
static void
loginc (void)
{
}
static void
logallocinit (void)
{
if (!logallocfp)
{
logallocfp = fopen ("malloc.log", "w");
if (!logallocfp)
{
fprintf (stderr, "panic: unable to open malloc.log\n");
exit (10);
}
setvbuf (logallocfp, NULL, _IOLBF, BUFSIZ);
fprintf (logallocfp, "null pointer is %p\n", NULL);
}
}
static void
logprintf (char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (logallocfp, fmt, ap);
va_end (ap);
}
#define LOGPRINT(x) ( logallocinit(), logprintf x )
#define LOGINC do { loginc(); logline++; } while (0)
#else
#define LOGPARAMS
#define LOGPRINT(x)
#define LOGINC ((void)0)
#endif
/*
* smalloc should guarantee to return a useful pointer - Halibut
* can do nothing except die when it's out of memory anyway.
*/
void *(smalloc) (LOGPARAMS int size)
{
void *p;
LOGINC;
LOGPRINT (("%s %d malloc(%ld)", file, line, (long) size));
p = malloc (size);
if (!p)
fatal (err_nomemory);
LOGPRINT ((" returns %p\n", p));
return p;
}
/*
* sfree should guaranteeably deal gracefully with freeing NULL
*/
void (sfree) (LOGPARAMS void *p)
{
if (p)
{
LOGINC;
LOGPRINT (("%s %d free(%p)\n", file, line, p));
free (p);
}
}
/*
* srealloc should guaranteeably be able to realloc NULL
*/
void *(srealloc) (LOGPARAMS void *p, int size)
{
void *q;
if (p)
{
LOGINC;
LOGPRINT (("%s %d realloc(%p,%ld)", file, line, p, (long) size));
q = realloc (p, size);
LOGPRINT ((" returns %p\n", q));
}
else
{
LOGINC;
LOGPRINT (("%s %d malloc(%ld)", file, line, (long) size));
q = malloc (size);
LOGPRINT ((" returns %p\n", q));
}
if (!q)
fatal (err_nomemory);
return q;
}
/*
* dupstr is like strdup, but with the never-return-NULL property
* of smalloc (and also reliably defined in all environments :-)
*/
char *
dupstr (char *s)
{
char *r = smalloc (1 + strlen (s));
strcpy (r, s);
return r;
}
/*
* Duplicate a linked list of words
*/
word *
dup_word_list (word * w)
{
word *head, **eptr = &head;
while (w)
{
word *newwd = mknew (word);
*newwd = *w; /* structure copy */
newwd->text = ustrdup (w->text);
if (w->alt)
newwd->alt = dup_word_list (w->alt);
*eptr = newwd;
newwd->next = NULL;
eptr = &newwd->next;
w = w->next;
}
return head;
}
/*
* Free a linked list of words
*/
void
free_word_list (word * w)
{
word *t;
while (w)
{
t = w;
w = w->next;
sfree (t->text);
if (t->alt)
free_word_list (t->alt);
sfree (t);
}
}
/*
* Free a linked list of paragraphs
*/
void
free_para_list (paragraph * p)
{
paragraph *t;
while (p)
{
t = p;
p = p->next;
sfree (t->keyword);
free_word_list (t->words);
sfree (t);
}
}

377
Docs/src/bin/halibut/misc.c Normal file
View file

@ -0,0 +1,377 @@
/*
* misc.c: miscellaneous useful items
*/
#include "halibut.h"
struct stackTag
{
void **data;
int sp;
int size;
};
stack
stk_new (void)
{
stack s;
s = mknew (struct stackTag);
s->sp = 0;
s->size = 0;
s->data = NULL;
return s;
}
void
stk_free (stack s)
{
sfree (s->data);
sfree (s);
}
void
stk_push (stack s, void *item)
{
if (s->size <= s->sp)
{
s->size = s->sp + 32;
s->data = resize (s->data, s->size);
}
s->data[s->sp++] = item;
}
void *
stk_pop (stack s)
{
if (s->sp > 0)
return s->data[--s->sp];
else
return NULL;
}
/*
* Small routines to amalgamate a string from an input source.
*/
const rdstring empty_rdstring = { 0, 0, NULL };
const rdstringc empty_rdstringc = { 0, 0, NULL };
void
rdadd (rdstring * rs, wchar_t c)
{
if (rs->pos >= rs->size - 1)
{
rs->size = rs->pos + 128;
rs->text = resize (rs->text, rs->size);
}
rs->text[rs->pos++] = c;
rs->text[rs->pos] = 0;
}
void
rdadds (rdstring * rs, wchar_t * p)
{
int len = ustrlen (p);
if (rs->pos >= rs->size - len)
{
rs->size = rs->pos + len + 128;
rs->text = resize (rs->text, rs->size);
}
ustrcpy (rs->text + rs->pos, p);
rs->pos += len;
}
wchar_t *
rdtrim (rdstring * rs)
{
rs->text = resize (rs->text, rs->pos + 1);
return rs->text;
}
void
rdaddc (rdstringc * rs, char c)
{
if (rs->pos >= rs->size - 1)
{
rs->size = rs->pos + 128;
rs->text = resize (rs->text, rs->size);
}
rs->text[rs->pos++] = c;
rs->text[rs->pos] = 0;
}
void
rdaddsc (rdstringc * rs, char *p)
{
int len = strlen (p);
if (rs->pos >= rs->size - len)
{
rs->size = rs->pos + len + 128;
rs->text = resize (rs->text, rs->size);
}
strcpy (rs->text + rs->pos, p);
rs->pos += len;
}
char *
rdtrimc (rdstringc * rs)
{
rs->text = resize (rs->text, rs->pos + 1);
return rs->text;
}
int
compare_wordlists (word * a, word * b)
{
int t;
while (a && b)
{
if (a->type != b->type)
return (a->type < b->type ? -1 : +1); /* FIXME? */
t = a->type;
if ((t != word_Normal && t != word_Code &&
t != word_WeakCode && t != word_Emph) || a->alt || b->alt)
{
int c;
if (a->text && b->text)
{
c = ustricmp (a->text, b->text);
if (c)
return c;
}
c = compare_wordlists (a->alt, b->alt);
if (c)
return c;
a = a->next;
b = b->next;
}
else
{
wchar_t *ap = a->text, *bp = b->text;
while (*ap && *bp)
{
wchar_t ac = utolower (*ap), bc = utolower (*bp);
if (ac != bc)
return (ac < bc ? -1 : +1);
if (!*++ap && a->next && a->next->type == t && !a->next->alt)
a = a->next, ap = a->text;
if (!*++bp && b->next && b->next->type == t && !b->next->alt)
b = b->next, bp = b->text;
}
if (*ap || *bp)
return (*ap ? +1 : -1);
a = a->next;
b = b->next;
}
}
if (a || b)
return (a ? +1 : -1);
else
return 0;
}
void
mark_attr_ends (paragraph * sourceform)
{
paragraph *p;
word *w, *wp;
for (p = sourceform; p; p = p->next)
{
wp = NULL;
for (w = p->words; w; w = w->next)
{
if (isattr (w->type))
{
int before = (wp && isattr (wp->type) &&
sameattr (wp->type, w->type));
int after = (w->next && isattr (w->next->type) &&
sameattr (w->next->type, w->type));
w->aux |= (before ?
(after ? attr_Always : attr_Last) :
(after ? attr_First : attr_Only));
}
wp = w;
}
}
}
wrappedline *
wrap_para (word * text, int width, int subsequentwidth,
int (*widthfn) (word *))
{
wrappedline *head = NULL, **ptr = &head;
int nwords, wordsize;
struct wrapword
{
word *begin, *end;
int width;
int spacewidth;
int cost;
int nwords;
}
*wrapwords;
int i, j, n;
/*
* Break the line up into wrappable components.
*/
nwords = wordsize = 0;
wrapwords = NULL;
while (text)
{
if (nwords >= wordsize)
{
wordsize = nwords + 64;
wrapwords = srealloc (wrapwords, wordsize * sizeof (*wrapwords));
}
wrapwords[nwords].width = 0;
wrapwords[nwords].begin = text;
while (text)
{
wrapwords[nwords].width += widthfn (text);
wrapwords[nwords].end = text->next;
if (text->next && (text->next->type == word_WhiteSpace ||
text->next->type == word_EmphSpace ||
text->breaks))
break;
text = text->next;
}
if (text && text->next && (text->next->type == word_WhiteSpace ||
text->next->type == word_EmphSpace))
{
wrapwords[nwords].spacewidth = widthfn (text->next);
text = text->next;
}
else
{
wrapwords[nwords].spacewidth = 0;
}
nwords++;
if (text)
text = text->next;
}
/*
* Perform the dynamic wrapping algorithm: work backwards from
* nwords-1, determining the optimal wrapping for each terminal
* subsequence of the paragraph.
*/
for (i = nwords; i--;)
{
int best = -1;
int bestcost = 0;
int cost;
int linelen = 0, spacewidth = 0;
int seenspace;
int thiswidth = (i == 0 ? width : subsequentwidth);
j = 0;
seenspace = 0;
while (i + j < nwords)
{
/*
* See what happens if we put j+1 words on this line.
*/
if (spacewidth)
seenspace = 1;
linelen += spacewidth + wrapwords[i + j].width;
spacewidth = wrapwords[i + j].spacewidth;
j++;
if (linelen > thiswidth)
{
/*
* If we're over the width limit, abandon ship,
* _unless_ there is no best-effort yet (which will
* only happen if the first word is too long all by
* itself).
*/
if (best > 0)
break;
}
if (i + j == nwords)
{
/*
* Special case: if we're at the very end of the
* paragraph, we don't score penalty points for the
* white space left on the line.
*/
cost = 0;
}
else
{
cost = (thiswidth - linelen) * (thiswidth - linelen);
cost += wrapwords[i + j].cost;
}
/*
* We compare bestcost >= cost, not bestcost > cost,
* because in cases where the costs are identical we
* want to try to look like the greedy algorithm,
* because readers are likely to have spent a lot of
* time looking at greedy-wrapped paragraphs and
* there's no point violating the Principle of Least
* Surprise if it doesn't actually gain anything.
*/
if (best < 0 || bestcost >= cost)
{
bestcost = cost;
best = j;
}
}
/*
* Now we know the optimal answer for this terminal
* subsequence, so put it in wrapwords.
*/
wrapwords[i].cost = bestcost;
wrapwords[i].nwords = best;
}
/*
* We've wrapped the paragraph. Now build the output
* `wrappedline' list.
*/
i = 0;
while (i < nwords)
{
wrappedline *w = mknew (wrappedline);
*ptr = w;
ptr = &w->next;
w->next = NULL;
n = wrapwords[i].nwords;
w->begin = wrapwords[i].begin;
w->end = wrapwords[i + n - 1].end;
/*
* Count along the words to find nspaces and shortfall.
*/
w->nspaces = 0;
w->shortfall = width;
for (j = 0; j < n; j++)
{
w->shortfall -= wrapwords[i + j].width;
if (j < n - 1 && wrapwords[i + j].spacewidth)
{
w->nspaces++;
w->shortfall -= wrapwords[i + j].spacewidth;
}
}
i += n;
}
sfree (wrapwords);
return head;
}
void
wrap_free (wrappedline * w)
{
while (w)
{
wrappedline *t = w->next;
sfree (w);
w = t;
}
}

View file

@ -0,0 +1,7 @@
/*
* style.c: load and keep track of user style preferences
*/
#include <stdio.h>
#include <stdlib.h>
#include "halibut.h"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,203 @@
/*
* tree234.h: header defining functions in tree234.c.
*
* This file is copyright 1999-2001 Simon Tatham.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TREE234_H
#define TREE234_H
/*
* This typedef is opaque outside tree234.c itself.
*/
typedef struct tree234_Tag tree234;
typedef int (*cmpfn234) (void *, void *);
typedef void *(*copyfn234) (void *state, void *element);
/*
* Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and
* lookups by key will fail: you can only look things up by numeric
* index, and you have to use addpos234() and delpos234().
*/
tree234 *newtree234 (cmpfn234 cmp);
/*
* Free a 2-3-4 tree (not including freeing the elements).
*/
void freetree234 (tree234 * t);
/*
* Add an element e to a sorted 2-3-4 tree t. Returns e on success,
* or if an existing element compares equal, returns that.
*/
void *add234 (tree234 * t, void *e);
/*
* Add an element e to an unsorted 2-3-4 tree t. Returns e on
* success, NULL on failure. (Failure should only occur if the
* index is out of range or the tree is sorted.)
*
* Index range can be from 0 to the tree's current element count,
* inclusive.
*/
void *addpos234 (tree234 * t, void *e, int index);
/*
* Look up the element at a given numeric index in a 2-3-4 tree.
* Returns NULL if the index is out of range.
*
* One obvious use for this function is in iterating over the whole
* of a tree (sorted or unsorted):
*
* for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p);
*
* or
*
* int maxcount = count234(tree);
* for (i = 0; i < maxcount; i++) {
* p = index234(tree, i);
* assert(p != NULL);
* consume(p);
* }
*/
void *index234 (tree234 * t, int index);
/*
* Find an element e in a sorted 2-3-4 tree t. Returns NULL if not
* found. e is always passed as the first argument to cmp, so cmp
* can be an asymmetric function if desired. cmp can also be passed
* as NULL, in which case the compare function from the tree proper
* will be used.
*
* Three of these functions are special cases of findrelpos234. The
* non-`pos' variants lack the `index' parameter: if the parameter
* is present and non-NULL, it must point to an integer variable
* which will be filled with the numeric index of the returned
* element.
*
* The non-`rel' variants lack the `relation' parameter. This
* parameter allows you to specify what relation the element you
* provide has to the element you're looking for. This parameter
* can be:
*
* REL234_EQ - find only an element that compares equal to e
* REL234_LT - find the greatest element that compares < e
* REL234_LE - find the greatest element that compares <= e
* REL234_GT - find the smallest element that compares > e
* REL234_GE - find the smallest element that compares >= e
*
* Non-`rel' variants assume REL234_EQ.
*
* If `rel' is REL234_GT or REL234_LT, the `e' parameter may be
* NULL. In this case, REL234_GT will return the smallest element
* in the tree, and REL234_LT will return the greatest. This gives
* an alternative means of iterating over a sorted tree, instead of
* using index234:
*
* // to loop forwards
* for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;)
* consume(p);
*
* // to loop backwards
* for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;)
* consume(p);
*/
enum
{
REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE
};
void *find234 (tree234 * t, void *e, cmpfn234 cmp);
void *findrel234 (tree234 * t, void *e, cmpfn234 cmp, int relation);
void *findpos234 (tree234 * t, void *e, cmpfn234 cmp, int *index);
void *findrelpos234 (tree234 * t, void *e, cmpfn234 cmp, int relation,
int *index);
/*
* Delete an element e in a 2-3-4 tree. Does not free the element,
* merely removes all links to it from the tree nodes.
*
* delpos234 deletes the element at a particular tree index: it
* works on both sorted and unsorted trees.
*
* del234 deletes the element passed to it, so it only works on
* sorted trees. (It's equivalent to using findpos234 to determine
* the index of an element, and then passing that index to
* delpos234.)
*
* Both functions return a pointer to the element they delete, for
* the user to free or pass on elsewhere or whatever. If the index
* is out of range (delpos234) or the element is already not in the
* tree (del234) then they return NULL.
*/
void *del234 (tree234 * t, void *e);
void *delpos234 (tree234 * t, int index);
/*
* Return the total element count of a tree234.
*/
int count234 (tree234 * t);
/*
* Split a tree234 into two valid tree234s.
*
* splitpos234 splits at a given index. If `before' is TRUE, the
* items at and after that index are left in t and the ones before
* are returned; if `before' is FALSE, the items before that index
* are left in t and the rest are returned.
*
* split234 splits at a given key. You can pass any of the
* relations used with findrel234, except for REL234_EQ. The items
* in the tree that satisfy the relation are returned; the
* remainder are left.
*/
tree234 *splitpos234 (tree234 * t, int index, int before);
tree234 *split234 (tree234 * t, void *e, cmpfn234 cmp, int rel);
/*
* Join two tree234s together into a single one.
*
* All the elements in t1 are placed to the left of all the
* elements in t2. If the trees are sorted, there will be a test to
* ensure that this satisfies the ordering criterion, and NULL will
* be returned otherwise. If the trees are unsorted, there is no
* restriction on the use of join234.
*
* The tree returned is t1 (join234) or t2 (join234r), if the
* operation is successful.
*/
tree234 *join234 (tree234 * t1, tree234 * t2);
tree234 *join234r (tree234 * t1, tree234 * t2);
/*
* Make a complete copy of a tree234. Element pointers will be
* reused unless copyfn is non-NULL, in which case it will be used
* to copy each element. (copyfn takes two `void *' parameters; the
* first is private state and the second is the element. A simple
* copy routine probably won't need private state.)
*/
tree234 *copytree234 (tree234 * t, copyfn234 copyfn, void *copyfnstate);
#endif /* TREE234_H */

View file

@ -0,0 +1,216 @@
/*
* ustring.c: Unicode string routines
*/
#include <wchar.h>
#include <time.h>
#include "halibut.h"
wchar_t *
ustrdup (wchar_t * s)
{
wchar_t *r;
if (s)
{
r = mknewa (wchar_t, 1 + ustrlen (s));
ustrcpy (r, s);
}
else
{
r = mknew (wchar_t);
*r = 0;
}
return r;
}
char *
ustrtoa (wchar_t * s, char *outbuf, int size)
{
char *p;
if (!s)
{
*outbuf = '\0';
return outbuf;
}
for (p = outbuf; *s && p < outbuf + size; p++, s++)
*p = *s;
if (p < outbuf + size)
*p = '\0';
else
outbuf[size - 1] = '\0';
return outbuf;
}
int
ustrlen (wchar_t * s)
{
int len = 0;
while (*s++)
len++;
return len;
}
wchar_t *
uadv (wchar_t * s)
{
return s + 1 + ustrlen (s);
}
wchar_t *
ustrcpy (wchar_t * dest, wchar_t * source)
{
wchar_t *ret = dest;
do
{
*dest++ = *source;
}
while (*source++);
return ret;
}
int
ustrcmp (wchar_t * lhs, wchar_t * rhs)
{
if (!lhs && !rhs)
return 0;
if (!lhs)
return -1;
if (!rhs)
return +1;
while (*lhs && *rhs && *lhs == *rhs)
lhs++, rhs++;
if (*lhs < *rhs)
return -1;
else if (*lhs > *rhs)
return 1;
return 0;
}
wchar_t
utolower (wchar_t c)
{
if (c == L'\0')
return c; /* this property needed by ustricmp */
/* FIXME: this doesn't even come close */
if (c >= 'A' && c <= 'Z')
c += 'a' - 'A';
return c;
}
int
ustricmp (wchar_t * lhs, wchar_t * rhs)
{
wchar_t lc, rc;
while ((lc = utolower (*lhs)) == (rc = utolower (*rhs)) && lc && rc)
lhs++, rhs++;
if (!lc && !rc)
return 0;
if (lc < rc)
return -1;
else
return 1;
}
wchar_t *
ustrlow (wchar_t * s)
{
wchar_t *p = s;
while (*p)
{
*p = utolower (*p);
p++;
}
return s;
}
int
utoi (wchar_t * s)
{
int sign = +1;
int n;
if (*s == L'-')
{
s++;
sign = -1;
}
n = 0;
while (*s && *s >= L'0' && *s <= L'9')
{
n *= 10;
n += (*s - '0');
s++;
}
return n;
}
int
utob (wchar_t * s)
{
if (!ustricmp (s, L"yes") || !ustricmp (s, L"y") ||
!ustricmp (s, L"true") || !ustricmp (s, L"t"))
return TRUE;
return FALSE;
}
int
uisdigit (wchar_t c)
{
return c >= L'0' && c <= L'9';
}
#define USTRFTIME_DELTA 128
wchar_t *
ustrftime (wchar_t * wfmt, struct tm * timespec)
{
void *blk = NULL;
wchar_t *wblk, *wp;
char *fmt, *text, *p;
size_t size = 0;
size_t len;
/*
* strftime has the entertaining property that it returns 0
* _either_ on out-of-space _or_ on successful generation of
* the empty string. Hence we must ensure our format can never
* generate the empty string. Somebody throw a custard pie at
* whoever was responsible for that. Please?
*/
if (wfmt)
{
len = ustrlen (wfmt);
fmt = mknewa (char, 2 + len);
ustrtoa (wfmt, fmt + 1, len + 1);
fmt[0] = ' ';
}
else
fmt = " %c";
while (1)
{
size += USTRFTIME_DELTA;
blk = resize ((char *) blk, size);
len = strftime ((char *) blk, size - 1, fmt, timespec);
if (len > 0)
break;
}
/* Note: +1 for the terminating 0, -1 for the initial space in fmt */
wblk = resize ((wchar_t *) blk, len);
text = mknewa (char, len);
strftime (text, len, fmt + 1, timespec);
/*
* We operate in the C locale, so this all ought to be kosher
* ASCII. If we ever move outside ASCII machines, we may need
* to make this more portable...
*/
for (wp = wblk, p = text; *p; p++, wp++)
*wp = *p;
*wp = 0;
if (wfmt)
sfree (fmt);
sfree (text);
return wblk;
}

View file

@ -0,0 +1,13 @@
/*
* version.c: version string
*/
#include <stdio.h>
#ifndef VERSION
#define VER "anonymous build (" __DATE__ " " __TIME__ ")"
#else
#define VER "version " VERSION
#endif
const char *const version = VER;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,173 @@
/*
* winhelp.h header file for winhelp.c
*/
typedef struct WHLP_tag *WHLP;
typedef struct WHLP_TOPIC_tag *WHLP_TOPIC;
/*
* Initialise a new WHlp context and begin accumulating data in it.
*/
WHLP whlp_new (void);
/*
* Close a WHlp context and write out the help file it has created.
*/
void whlp_close (WHLP h, char *filename);
/*
* Abandon and free a WHlp context without writing out anything.
*/
void whlp_abandon (WHLP h);
/*
* Specify the title and copyright notice of a help file. Also
* specify Help macros to be run on loading.
*/
void whlp_title (WHLP h, char *title);
void whlp_copyright (WHLP h, char *copyright);
void whlp_start_macro (WHLP h, char *macro);
/*
* Register a help topic. Irritatingly, due to weird phase-order
* issues with the whole file format, you have to register all your
* topics _before_ actually outputting your text. This seems likely
* to require two passes over the source document.
*
* If you want to specify a particular context string (for
* reference from other programs, to provide context-sensitive
* help), you can supply it here. Otherwise, just pass NULL and a
* nondescript one will be allocated automatically.
*
* If you specify two context strings which clash under the Windows
* help file hash algorithm, this function will return NULL and
* provide a pointer to the other context string that this one
* clashed with, and you must tell your user to fix the clash.
* Sadly this is the only way to do it; despite HLP files having a
* perfectly good method of mapping arbitrary strings to things,
* they didn't see fit to use that method for help contexts, so
* instead they hash the context names and expect the hashes to be
* unique. Sigh.
*
* On success (i.e. in any circumstance other than a hash clash), a
* valid WHLP_TOPIC is returned for later use.
*/
WHLP_TOPIC whlp_register_topic (WHLP h, char *context_name, char **clash);
/*
* Link two topics together in a browse sequence. Automatically
* takes care of the forward and reverse links.
*/
void whlp_browse_link (WHLP h, WHLP_TOPIC before, WHLP_TOPIC after);
/*
* After calling whlp_register_topic for all topics, you should
* call this, which will sort out all loose ends and allocate
* context names for all anonymous topics. Then you can start
* writing actual text.
*/
void whlp_prepare (WHLP h);
/*
* Create a link from an index term to a topic.
*/
void whlp_index_term (WHLP h, char *index, WHLP_TOPIC topic);
/*
* Call this if you need the id of a topic and you don't already
* know it (for example, if whlp_prepare has allocated it
* anonymously for you). You might need this, for example, in
* creating macros for button-bar bindings.
*
* The string returned will be freed when the WHLP context is
* closed. You should not free it yourself.
*
* Do not call this before calling whlp_prepare().
*/
char *whlp_topic_id (WHLP_TOPIC topic);
/*
* Call this to specify which help topic will be the first one
* displayed when the help file is loaded.
*/
void whlp_primary_topic (WHLP h, WHLP_TOPIC topic);
/*
* Call this when about to begin writing out the text for a topic.
*
* Any additional arguments are Help macros, terminated with a
* NULL. So the minimum call sequence is
*
* whlp_begin_topic(helpfile, mytopic, "Title", NULL);
*/
void whlp_begin_topic (WHLP h, WHLP_TOPIC topic, char *title, ...);
/*
* Call this to set up a font descriptor. You supply the font name,
* the font size (in half-points), the graphic rendition flags
* (bold, italic etc), and the general font family (for Windows to
* select a fallback font if yours is unavailable). You can also
* specify a foreground colour for the text (but unfortunately not
* a background).
*
* Font descriptors are identified in whlp_set_font() by small
* integers, which are allocated from 0 upwards in the order you
* call whlp_create_font(). For your convenience,
* whlp_create_font() returns the integer allocated to each font
* descriptor you create, but you could work this out just as
* easily yourself by counting.
*/
enum
{
WHLP_FONT_BOLD = 1,
WHLP_FONT_ITALIC = 2,
WHLP_FONT_UNDERLINE = 4,
WHLP_FONT_STRIKEOUT = 8,
WHLP_FONT_DOUBLEUND = 16,
WHLP_FONT_SMALLCAPS = 32
};
enum
{
WHLP_FONTFAM_FIXED = 1,
WHLP_FONTFAM_SERIF = 2,
WHLP_FONTFAM_SANS = 3,
WHLP_FONTFAM_SCRIPT = 4,
WHLP_FONTFAM_DECOR = 5
};
int whlp_create_font (WHLP h, char *font, int family, int halfpoints,
int rendition, int r, int g, int b);
/*
* Routines to output paragraphs and actual text (at last).
*
* You should start by calling whlp_para_attr() to set any
* paragraph attributes that differ from the standard settings.
* Next call whlp_begin_para() to start the paragraph. Then call
* the various in-paragraph functions until you have output the
* whole paragraph, and finally call whlp_end_para() to finish it
* off.
*/
enum
{
WHLP_PARA_SPACEABOVE = 1, WHLP_PARA_SPACEBELOW, WHLP_PARA_SPACELINES,
WHLP_PARA_LEFTINDENT, WHLP_PARA_RIGHTINDENT, WHLP_PARA_FIRSTLINEINDENT,
WHLP_PARA_ALIGNMENT
};
enum
{
WHLP_ALIGN_LEFT, WHLP_ALIGN_RIGHT, WHLP_ALIGN_CENTRE
};
enum
{
WHLP_PARA_SCROLL, WHLP_PARA_NONSCROLL
};
void whlp_para_attr (WHLP h, int attr_id, int attr_param);
void whlp_set_tabstop (WHLP h, int tabstop, int alignment);
void whlp_begin_para (WHLP h, int para_type);
void whlp_end_para (WHLP h);
void whlp_set_font (WHLP h, int font_id);
void whlp_text (WHLP h, char *text);
void whlp_start_hyperlink (WHLP h, WHLP_TOPIC target);
void whlp_end_hyperlink (WHLP h);
void whlp_tab (WHLP h);