]> git.cameronkatri.com Git - mandoc.git/blob - tbl.c
Tiny edit required after MDOC_HALT change.
[mandoc.git] / tbl.c
1 /* $Id: tbl.c,v 1.19 2011/01/02 20:34:05 kristaps Exp $ */
2 /*
3 * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <assert.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <time.h>
22
23 #include "mandoc.h"
24 #include "roff.h"
25 #include "libmandoc.h"
26 #include "libroff.h"
27
28 static void tbl_calc(struct tbl_node *);
29 static void tbl_calc_data(struct tbl_node *, struct tbl_dat *);
30 static void tbl_calc_data_literal(struct tbl_dat *);
31 static void tbl_calc_data_number(struct tbl_node *, struct tbl_dat *);
32 static void tbl_calc_data_spanner(struct tbl_dat *);
33
34 enum rofferr
35 tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs)
36 {
37 int len;
38 const char *cp;
39
40 cp = &p[offs];
41 len = (int)strlen(cp);
42
43 /*
44 * If we're in the options section and we don't have a
45 * terminating semicolon, assume we've moved directly into the
46 * layout section. No need to report a warning: this is,
47 * apparently, standard behaviour.
48 */
49
50 if (TBL_PART_OPTS == tbl->part && len)
51 if (';' != cp[len - 1])
52 tbl->part = TBL_PART_LAYOUT;
53
54 /* Now process each logical section of the table. */
55
56 switch (tbl->part) {
57 case (TBL_PART_OPTS):
58 return(tbl_option(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
59 case (TBL_PART_LAYOUT):
60 return(tbl_layout(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
61 case (TBL_PART_DATA):
62 break;
63 }
64
65 /*
66 * This only returns zero if the line is empty, so we ignore it
67 * and continue on.
68 */
69 return(tbl_data(tbl, ln, p) ? ROFF_TBL : ROFF_IGN);
70 }
71
72 struct tbl_node *
73 tbl_alloc(int pos, int line, void *data, const mandocmsg msg)
74 {
75 struct tbl_node *p;
76
77 p = mandoc_calloc(1, sizeof(struct tbl_node));
78 p->line = line;
79 p->pos = pos;
80 p->data = data;
81 p->msg = msg;
82 p->part = TBL_PART_OPTS;
83 p->opts.tab = '\t';
84 p->opts.linesize = 12;
85 p->opts.decimal = '.';
86 return(p);
87 }
88
89 void
90 tbl_free(struct tbl_node *p)
91 {
92 struct tbl_row *rp;
93 struct tbl_cell *cp;
94 struct tbl_span *sp;
95 struct tbl_dat *dp;
96 struct tbl_head *hp;
97
98 while (NULL != (rp = p->first_row)) {
99 p->first_row = rp->next;
100 while (rp->first) {
101 cp = rp->first;
102 rp->first = cp->next;
103 free(cp);
104 }
105 free(rp);
106 }
107
108 while (NULL != (sp = p->first_span)) {
109 p->first_span = sp->next;
110 while (sp->first) {
111 dp = sp->first;
112 sp->first = dp->next;
113 if (dp->string)
114 free(dp->string);
115 free(dp);
116 }
117 free(sp);
118 }
119
120 while (NULL != (hp = p->first_head)) {
121 p->first_head = hp->next;
122 free(hp);
123 }
124
125 free(p);
126 }
127
128 void
129 tbl_restart(int line, int pos, struct tbl_node *tbl)
130 {
131
132 tbl->part = TBL_PART_LAYOUT;
133 tbl->line = line;
134 tbl->pos = pos;
135
136 if (NULL == tbl->first_span || NULL == tbl->first_span->first)
137 TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos);
138 }
139
140 const struct tbl_span *
141 tbl_span(const struct tbl_node *tbl)
142 {
143
144 assert(tbl);
145 return(tbl->last_span);
146 }
147
148 void
149 tbl_end(struct tbl_node *tbl)
150 {
151
152 if (NULL == tbl->first_span || NULL == tbl->first_span->first)
153 TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos);
154 else
155 tbl_calc(tbl);
156
157 if (tbl->last_span)
158 tbl->last_span->flags |= TBL_SPAN_LAST;
159 }
160
161 static void
162 tbl_calc(struct tbl_node *tbl)
163 {
164 struct tbl_span *sp;
165 struct tbl_dat *dp;
166 struct tbl_head *hp;
167
168 /* Calculate width as the max of column cells' widths. */
169
170 for (sp = tbl->first_span; sp; sp = sp->next) {
171 switch (sp->pos) {
172 case (TBL_DATA_HORIZ):
173 /* FALLTHROUGH */
174 case (TBL_DATA_DHORIZ):
175 continue;
176 default:
177 break;
178 }
179 for (dp = sp->first; dp; dp = dp->next)
180 tbl_calc_data(tbl, dp);
181 }
182
183 /* Calculate width as the simple spanner value. */
184
185 for (hp = tbl->first_head; hp; hp = hp->next)
186 switch (hp->pos) {
187 case (TBL_HEAD_VERT):
188 hp->width = 1;
189 break;
190 case (TBL_HEAD_DVERT):
191 hp->width = 2;
192 break;
193 default:
194 break;
195 }
196 }
197
198 static void
199 tbl_calc_data(struct tbl_node *tbl, struct tbl_dat *data)
200 {
201
202 if (NULL == data->layout)
203 return;
204
205 /* Branch down into data sub-types. */
206
207 switch (data->layout->pos) {
208 case (TBL_CELL_HORIZ):
209 /* FALLTHROUGH */
210 case (TBL_CELL_DHORIZ):
211 tbl_calc_data_spanner(data);
212 break;
213 case (TBL_CELL_LONG):
214 /* FALLTHROUGH */
215 case (TBL_CELL_CENTRE):
216 /* FALLTHROUGH */
217 case (TBL_CELL_LEFT):
218 /* FALLTHROUGH */
219 case (TBL_CELL_RIGHT):
220 tbl_calc_data_literal(data);
221 break;
222 case (TBL_CELL_NUMBER):
223 tbl_calc_data_number(tbl, data);
224 break;
225 default:
226 abort();
227 /* NOTREACHED */
228 }
229 }
230
231 static void
232 tbl_calc_data_spanner(struct tbl_dat *data)
233 {
234
235 /* N.B., these are horiz spanners (not vert) so always 1. */
236 data->layout->head->width = 1;
237 }
238
239 static void
240 tbl_calc_data_number(struct tbl_node *tbl, struct tbl_dat *data)
241 {
242 int sz, d;
243 char *dp, pnt;
244
245 /*
246 * First calculate number width and decimal place (last + 1 for
247 * no-decimal numbers). If the stored decimal is subsequent
248 * ours, make our size longer by that difference
249 * (right-"shifting"); similarly, if ours is subsequent the
250 * stored, then extend the stored size by the difference.
251 * Finally, re-assign the stored values.
252 */
253
254 /* TODO: use spacing modifier. */
255
256 assert(data->string);
257 sz = (int)strlen(data->string);
258 pnt = tbl->opts.decimal;
259
260 if (NULL == (dp = strchr(data->string, pnt)))
261 d = sz + 1;
262 else
263 d = (int)(dp - data->string) + 1;
264
265 sz += 2;
266
267 if (data->layout->head->decimal > d) {
268 sz += data->layout->head->decimal - d;
269 d = data->layout->head->decimal;
270 } else
271 data->layout->head->width +=
272 d - data->layout->head->decimal;
273
274 if (sz > data->layout->head->width)
275 data->layout->head->width = sz;
276 if (d > data->layout->head->decimal)
277 data->layout->head->decimal = d;
278 }
279
280 static void
281 tbl_calc_data_literal(struct tbl_dat *data)
282 {
283 int sz, bufsz;
284
285 /*
286 * Calculate our width and use the spacing, with a minimum
287 * spacing dictated by position (centre, e.g,. gets a space on
288 * either side, while right/left get a single adjacent space).
289 */
290
291 assert(data->string);
292 sz = (int)strlen(data->string);
293
294 switch (data->layout->pos) {
295 case (TBL_CELL_LONG):
296 /* FALLTHROUGH */
297 case (TBL_CELL_CENTRE):
298 bufsz = 2;
299 break;
300 default:
301 bufsz = 1;
302 break;
303 }
304
305 if (data->layout->spacing)
306 bufsz = bufsz > data->layout->spacing ?
307 bufsz : data->layout->spacing;
308
309 sz += bufsz;
310 if (data->layout->head->width < sz)
311 data->layout->head->width = sz;
312 }