/* * contents.c: build a table of contents */ #include #include #include #include #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; }