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