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
11 struct 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 stack listitem_stack;
22 wchar_t *chaptertext; /* the word for a chapter */
23 wchar_t *sectiontext; /* the word for a section */
24 wchar_t *apptext; /* the word for an appendix */
25 };
26
number_init(void)27 numberstate *number_init(void) {
28 numberstate *ret = snew(numberstate);
29 ret->chapternum = 0;
30 ret->appendixnum = -1;
31 ret->ischapter = 1;
32 ret->oklevel = -1; /* not even in a chapter yet */
33 ret->maxsectlevel = 32;
34 ret->sectionlevels = snewn(ret->maxsectlevel, int);
35 ret->currentsects = snewn(ret->maxsectlevel+1, paragraph *);
36 memset(ret->currentsects, 0, (ret->maxsectlevel+1)*sizeof(paragraph *));
37 ret->lastsect = NULL;
38 ret->listitem = -1;
39 ret->listitem_stack = stk_new();
40 return ret;
41 }
42
number_free(numberstate * state)43 void number_free(numberstate *state) {
44 stk_free(state->listitem_stack);
45 sfree(state->sectionlevels);
46 sfree(state->currentsects);
47 sfree(state);
48 }
49
dotext(word *** wret,wchar_t * text)50 static void dotext(word ***wret, wchar_t *text) {
51 word *mnewword = snew(word);
52 mnewword->text = ustrdup(text);
53 mnewword->type = word_Normal;
54 mnewword->alt = NULL;
55 mnewword->next = NULL;
56 mnewword->breaks = FALSE;
57 mnewword->aux = 0;
58 **wret = mnewword;
59 *wret = &mnewword->next;
60 }
61
dospace(word *** wret)62 static void dospace(word ***wret) {
63 word *mnewword = snew(word);
64 mnewword->text = NULL;
65 mnewword->type = word_WhiteSpace;
66 mnewword->alt = NULL;
67 mnewword->next = NULL;
68 mnewword->breaks = FALSE;
69 mnewword->aux = 0;
70 **wret = mnewword;
71 *wret = &mnewword->next;
72 }
73
donumber(word *** wret,int num)74 static void donumber(word ***wret, int num) {
75 wchar_t text[20];
76 wchar_t *p = text + lenof(text);
77 *--p = L'\0';
78 while (num != 0) {
79 assert(p > text);
80 *--p = L"0123456789"[num % 10];
81 num /= 10;
82 }
83 dotext(wret, p);
84 }
85
doanumber(word *** wret,int num)86 static void doanumber(word ***wret, int num) {
87 wchar_t text[20];
88 wchar_t *p;
89 int nletters, aton;
90 nletters = 1;
91 aton = 25;
92 while (num > aton) {
93 nletters++;
94 num -= aton+1;
95 if (aton < INT_MAX/26)
96 aton = (aton+1) * 26 - 1;
97 else
98 aton = INT_MAX;
99 }
100 p = text + lenof(text);
101 *--p = L'\0';
102 while (nletters--) {
103 assert(p > text);
104 *--p = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num % 26];
105 num /= 26;
106 }
107 dotext(wret, p);
108 }
109
number_cfg(numberstate * state,paragraph * source)110 void number_cfg(numberstate *state, paragraph *source) {
111 /*
112 * Defaults
113 */
114 state->chaptertext = L"Chapter";
115 state->sectiontext = L"Section";
116 state->apptext = L"Appendix";
117
118 for (; source; source = source->next) {
119 if (source->type == para_Config) {
120 if (!ustricmp(source->keyword, L"chapter")) {
121 state->chaptertext = uadv(source->keyword);
122 } else if (!ustricmp(source->keyword, L"section")) {
123 state->sectiontext = uadv(source->keyword);
124 } else if (!ustricmp(source->keyword, L"appendix")) {
125 state->apptext = uadv(source->keyword);
126 }
127 }
128 }
129 }
130
number_mktext(numberstate * state,paragraph * p,wchar_t * category,int * prev,int * errflag)131 word *number_mktext(numberstate *state, paragraph *p, wchar_t *category,
132 int *prev, int *errflag) {
133 word *ret = NULL;
134 word **ret2 = &ret;
135 word **pret = &ret;
136 int i, level, thistype;
137 struct listitem_stack_entry {
138 int listitem;
139 int prev;
140 } *lse;
141
142 level = -2; /* default for non-section-heading */
143 thistype = p->type;
144 switch (p->type) {
145 case para_Chapter:
146 state->chapternum++;
147 for (i = 0; i < state->maxsectlevel; i++)
148 state->sectionlevels[i] = 0;
149 dotext(&pret, category ? category : state->chaptertext);
150 dospace(&pret);
151 ret2 = pret;
152 donumber(&pret, state->chapternum);
153 state->ischapter = 1;
154 state->oklevel = 0;
155 level = -1;
156 break;
157 case para_Heading:
158 case para_Subsect:
159 level = (p->type == para_Heading ? 0 : p->aux);
160 if (level > state->oklevel) {
161 err_sectjump(&p->fpos);
162 *errflag = TRUE;
163 ret = NULL;
164 break;
165 }
166 state->oklevel = level+1;
167 if (state->maxsectlevel <= level) {
168 state->maxsectlevel = level + 32;
169 state->sectionlevels = sresize(state->sectionlevels,
170 state->maxsectlevel, int);
171 }
172 state->sectionlevels[level]++;
173 for (i = level+1; i < state->maxsectlevel; i++)
174 state->sectionlevels[i] = 0;
175 dotext(&pret, category ? category : state->sectiontext);
176 dospace(&pret);
177 ret2 = pret;
178 if (state->ischapter)
179 donumber(&pret, state->chapternum);
180 else
181 doanumber(&pret, state->appendixnum);
182 for (i = 0; i <= level; i++) {
183 dotext(&pret, L".");
184 if (state->sectionlevels[i] == 0)
185 state->sectionlevels[i] = 1;
186 donumber(&pret, state->sectionlevels[i]);
187 }
188 break;
189 case para_Appendix:
190 state->appendixnum++;
191 for (i = 0; i < state->maxsectlevel; i++)
192 state->sectionlevels[i] = 0;
193 dotext(&pret, category ? category : state->apptext);
194 dospace(&pret);
195 ret2 = pret;
196 doanumber(&pret, state->appendixnum);
197 state->ischapter = 0;
198 state->oklevel = 0;
199 level = -1;
200 break;
201 case para_UnnumberedChapter:
202 level = -1;
203 break;
204 case para_NumberedList:
205 ret2 = pret;
206 if (*prev != para_NumberedList)
207 state->listitem = 0;
208 state->listitem++;
209 donumber(&pret, state->listitem);
210 break;
211 case para_LcontPush:
212 lse = snew(struct listitem_stack_entry);
213 lse->listitem = state->listitem;
214 lse->prev = *prev;
215 stk_push(state->listitem_stack, lse);
216 state->listitem = 0;
217 break;
218 case para_LcontPop:
219 lse = (struct listitem_stack_entry *)stk_pop(state->listitem_stack);
220 state->listitem = lse->listitem;
221 thistype = lse->prev;
222 sfree(lse);
223 break;
224 }
225
226 /*
227 * Now set up parent, child and sibling links.
228 */
229 p->parent = p->child = p->sibling = NULL;
230 if (level != -2) {
231 if (state->currentsects[level+1])
232 state->currentsects[level+1]->sibling = p;
233 if (level >= 0 && state->currentsects[level]) {
234 p->parent = state->currentsects[level];
235 if (!state->currentsects[level]->child)
236 state->currentsects[level]->child = p;
237 }
238 state->currentsects[level+1] = state->lastsect = p;
239 for (i = level+2; i < state->maxsectlevel+1; i++)
240 state->currentsects[i] = NULL;
241 } else {
242 p->parent = state->lastsect;
243 }
244
245 p->kwtext2 = *ret2;
246 *prev = thistype;
247 return ret;
248 }
249