summaryrefslogtreecommitdiff
path: root/app/tools/halibut/contents.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/tools/halibut/contents.c')
-rw-r--r--app/tools/halibut/contents.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/app/tools/halibut/contents.c b/app/tools/halibut/contents.c
new file mode 100644
index 0000000..2acf81e
--- /dev/null
+++ b/app/tools/halibut/contents.c
@@ -0,0 +1,244 @@
+/*
+ * 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;
+ stack listitem_stack;
+ 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 = snew(numberstate);
+ ret->chapternum = 0;
+ ret->appendixnum = -1;
+ ret->ischapter = 1;
+ ret->oklevel = -1; /* not even in a chapter yet */
+ ret->maxsectlevel = 32;
+ ret->sectionlevels = snewn(ret->maxsectlevel, int);
+ ret->currentsects = snewn(ret->maxsectlevel+1, paragraph *);
+ memset(ret->currentsects, 0, (ret->maxsectlevel+1)*sizeof(paragraph *));
+ ret->lastsect = NULL;
+ ret->listitem = -1;
+ ret->listitem_stack = stk_new();
+ return ret;
+}
+
+void number_free(numberstate *state) {
+ stk_free(state->listitem_stack);
+ sfree(state->sectionlevels);
+ sfree(state->currentsects);
+ sfree(state);
+}
+
+static void dotext(word ***wret, wchar_t *text) {
+ word *mnewword = snew(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 = snew(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 + lenof(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 + lenof(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, thistype;
+ struct listitem_stack_entry {
+ int listitem;
+ int prev;
+ } *lse;
+
+ level = -2; /* default for non-section-heading */
+ thistype = p->type;
+ 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 = sresize(state->sectionlevels,
+ state->maxsectlevel, int);
+ }
+ 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;
+ case para_LcontPush:
+ lse = snew(struct listitem_stack_entry);
+ lse->listitem = state->listitem;
+ lse->prev = *prev;
+ stk_push(state->listitem_stack, lse);
+ state->listitem = 0;
+ break;
+ case para_LcontPop:
+ lse = (struct listitem_stack_entry *)stk_pop(state->listitem_stack);
+ state->listitem = lse->listitem;
+ thistype = lse->prev;
+ sfree(lse);
+ 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;
+ *prev = thistype;
+ return ret;
+}