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