Man-page back end for Halibut. Also, a couple of additional markup
[sgt/halibut] / contents.c
CommitLineData
d7482997 1/*
2 * contents.c: build a table of contents
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8#include <limits.h>
9#include "halibut.h"
10
11struct numberstate_Tag {
12 int chapternum;
13 int appendixnum;
14 int ischapter;
15 int *sectionlevels;
16 paragraph **currentsects;
17 paragraph *lastsect;
18 int oklevel;
19 int maxsectlevel;
20 int listitem;
21 wchar_t *chaptertext; /* the word for a chapter */
22 wchar_t *sectiontext; /* the word for a section */
23 wchar_t *apptext; /* the word for an appendix */
24};
25
26numberstate *number_init(void) {
27 numberstate *ret = mknew(numberstate);
28 ret->chapternum = 0;
29 ret->appendixnum = -1;
30 ret->ischapter = 1;
31 ret->oklevel = -1; /* not even in a chapter yet */
32 ret->maxsectlevel = 32;
33 ret->sectionlevels = mknewa(int, ret->maxsectlevel);
34 ret->currentsects = mknewa(paragraph *, ret->maxsectlevel+1);
35 memset(ret->currentsects, 0, (ret->maxsectlevel+1)*sizeof(paragraph *));
36 ret->lastsect = NULL;
37 ret->listitem = -1;
38 return ret;
39}
40
41void number_free(numberstate *state) {
42 sfree(state->sectionlevels);
43 sfree(state->currentsects);
44 sfree(state);
45}
46
47static void dotext(word ***wret, wchar_t *text) {
48 word *mnewword = mknew(word);
49 mnewword->text = ustrdup(text);
50 mnewword->type = word_Normal;
51 mnewword->alt = NULL;
52 mnewword->next = NULL;
53 **wret = mnewword;
54 *wret = &mnewword->next;
55}
56
57static void dospace(word ***wret) {
58 word *mnewword = mknew(word);
59 mnewword->text = NULL;
60 mnewword->type = word_WhiteSpace;
61 mnewword->alt = NULL;
62 mnewword->next = NULL;
63 **wret = mnewword;
64 *wret = &mnewword->next;
65}
66
67static void donumber(word ***wret, int num) {
68 wchar_t text[20];
69 wchar_t *p = text + sizeof(text);
70 *--p = L'\0';
71 while (num != 0) {
72 assert(p > text);
73 *--p = L"0123456789"[num % 10];
74 num /= 10;
75 }
76 dotext(wret, p);
77}
78
79static void doanumber(word ***wret, int num) {
80 wchar_t text[20];
81 wchar_t *p;
82 int nletters, aton;
83 nletters = 1;
84 aton = 25;
85 while (num > aton) {
86 nletters++;
87 num -= aton+1;
88 if (aton < INT_MAX/26)
89 aton = (aton+1) * 26 - 1;
90 else
91 aton = INT_MAX;
92 }
93 p = text + sizeof(text);
94 *--p = L'\0';
95 while (nletters--) {
96 assert(p > text);
97 *--p = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num % 26];
98 num /= 26;
99 }
100 dotext(wret, p);
101}
102
103void number_cfg(numberstate *state, paragraph *source) {
104 /*
105 * Defaults
106 */
107 state->chaptertext = L"Chapter";
108 state->sectiontext = L"Section";
109 state->apptext = L"Appendix";
110
111 for (; source; source = source->next) {
112 if (source->type == para_Config) {
113 if (!ustricmp(source->keyword, L"chapter")) {
114 state->chaptertext = uadv(source->keyword);
115 } else if (!ustricmp(source->keyword, L"section")) {
116 state->sectiontext = uadv(source->keyword);
117 } else if (!ustricmp(source->keyword, L"appendix")) {
118 state->apptext = uadv(source->keyword);
119 }
120 }
121 }
122}
123
124word *number_mktext(numberstate *state, paragraph *p, wchar_t *category,
125 int prev, int *errflag) {
126 word *ret = NULL;
127 word **ret2 = &ret;
128 word **pret = &ret;
129 int i, level;
130
131 level = -2; /* default for non-section-heading */
132 switch (p->type) {
133 case para_Chapter:
134 state->chapternum++;
135 for (i = 0; i < state->maxsectlevel; i++)
136 state->sectionlevels[i] = 0;
137 dotext(&pret, category ? category : state->chaptertext);
138 dospace(&pret);
139 ret2 = pret;
140 donumber(&pret, state->chapternum);
141 state->ischapter = 1;
142 state->oklevel = 0;
143 level = -1;
144 break;
145 case para_Heading:
146 case para_Subsect:
147 level = (p->type == para_Heading ? 0 : p->aux);
148 if (level > state->oklevel) {
149 error(err_sectjump, &p->fpos);
150 *errflag = TRUE;
151 ret = NULL;
152 break;
153 }
154 state->oklevel = level+1;
155 if (state->maxsectlevel <= level) {
156 state->maxsectlevel = level + 32;
157 state->sectionlevels = resize(state->sectionlevels,
158 state->maxsectlevel);
159 }
160 state->sectionlevels[level]++;
161 for (i = level+1; i < state->maxsectlevel; i++)
162 state->sectionlevels[i] = 0;
163 dotext(&pret, category ? category : state->sectiontext);
164 dospace(&pret);
165 ret2 = pret;
166 if (state->ischapter)
167 donumber(&pret, state->chapternum);
168 else
169 doanumber(&pret, state->appendixnum);
170 for (i = 0; i <= level; i++) {
171 dotext(&pret, L".");
172 if (state->sectionlevels[i] == 0)
173 state->sectionlevels[i] = 1;
174 donumber(&pret, state->sectionlevels[i]);
175 }
176 break;
177 case para_Appendix:
178 state->appendixnum++;
179 for (i = 0; i < state->maxsectlevel; i++)
180 state->sectionlevels[i] = 0;
181 dotext(&pret, category ? category : state->apptext);
182 dospace(&pret);
183 ret2 = pret;
184 doanumber(&pret, state->appendixnum);
185 state->ischapter = 0;
186 state->oklevel = 0;
187 level = -1;
188 break;
189 case para_UnnumberedChapter:
190 level = -1;
191 break;
192 case para_NumberedList:
193 ret2 = pret;
194 if (prev != para_NumberedList)
195 state->listitem = 0;
196 state->listitem++;
197 donumber(&pret, state->listitem);
198 break;
199 }
200
201 /*
202 * Now set up parent, child and sibling links.
203 */
204 p->parent = p->child = p->sibling = NULL;
205 if (level != -2) {
206 if (state->currentsects[level+1])
207 state->currentsects[level+1]->sibling = p;
208 if (level >= 0 && state->currentsects[level]) {
209 p->parent = state->currentsects[level];
210 if (!state->currentsects[level]->child)
211 state->currentsects[level]->child = p;
212 }
213 state->currentsects[level+1] = state->lastsect = p;
214 for (i = level+2; i < state->maxsectlevel+1; i++)
215 state->currentsects[i] = NULL;
216 } else {
217 p->parent = state->lastsect;
218 }
219
220 p->kwtext2 = *ret2;
221 return ret;
222}