]> git.cameronkatri.com Git - mandoc.git/blob - tbl_layout.c
Merge, with considerable changes, tbl.bsd.lv's layout-handling code.
[mandoc.git] / tbl_layout.c
1 /* $Id: tbl_layout.c,v 1.1 2010/12/29 14:38:14 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 <ctype.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "mandoc.h"
23 #include "libmandoc.h"
24 #include "libroff.h"
25
26 struct tbl_phrase {
27 char name;
28 enum tbl_cellt key;
29 };
30
31 #define KEYS_MAX 17
32
33 static const struct tbl_phrase keys[KEYS_MAX] = {
34 { 'c', TBL_CELL_CENTRE },
35 { 'C', TBL_CELL_CENTRE },
36 { 'r', TBL_CELL_RIGHT },
37 { 'R', TBL_CELL_RIGHT },
38 { 'l', TBL_CELL_LEFT },
39 { 'L', TBL_CELL_LEFT },
40 { 'n', TBL_CELL_NUMBER },
41 { 'N', TBL_CELL_NUMBER },
42 { 's', TBL_CELL_SPAN },
43 { 'S', TBL_CELL_SPAN },
44 { 'a', TBL_CELL_LONG },
45 { 'A', TBL_CELL_LONG },
46 { '^', TBL_CELL_DOWN },
47 { '-', TBL_CELL_HORIZ },
48 { '_', TBL_CELL_HORIZ },
49 { '=', TBL_CELL_DHORIZ },
50 { '|', TBL_CELL_VERT }
51 };
52
53 static int mods(struct tbl *, struct tbl_cell *,
54 int, const char *, int *);
55 static int cell(struct tbl *, struct tbl_row *,
56 int, const char *, int *);
57 static void row(struct tbl *, int, const char *, int *);
58
59 static int
60 mods(struct tbl *tbl, struct tbl_cell *cp,
61 int ln, const char *p, int *pos)
62 {
63 char buf[5];
64 int i;
65
66 mod:
67 /*
68 * XXX: since, at least for now, modifiers are non-conflicting
69 * (are separable by value, regardless of position), we let
70 * modifiers come in any order. The existing tbl doesn't let
71 * this happen.
72 */
73 switch (p[*pos]) {
74 case ('\0'):
75 /* FALLTHROUGH */
76 case (' '):
77 /* FALLTHROUGH */
78 case ('\t'):
79 /* FALLTHROUGH */
80 case (','):
81 /* FALLTHROUGH */
82 case ('.'):
83 return(1);
84 default:
85 break;
86 }
87
88 /* Parse numerical spacing from modifier string. */
89
90 if (isdigit((unsigned char)p[*pos])) {
91 for (i = 0; i < 4; i++) {
92 if ( ! isdigit((unsigned char)p[*pos + i]))
93 break;
94 buf[i] = p[*pos + i];
95 }
96 buf[i] = '\0';
97
98 /* No greater than 4 digits. */
99
100 if (4 == i) {
101 TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos);
102 return(0);
103 }
104
105 *pos += i;
106 cp->spacing = atoi(buf);
107
108 goto mod;
109 /* NOTREACHED */
110 }
111
112 /* TODO: GNU has many more extensions. */
113
114 switch (p[(*pos)++]) {
115 case ('z'):
116 /* FALLTHROUGH */
117 case ('Z'):
118 cp->flags |= TBL_CELL_WIGN;
119 goto mod;
120 case ('u'):
121 /* FALLTHROUGH */
122 case ('U'):
123 cp->flags |= TBL_CELL_UP;
124 goto mod;
125 case ('e'):
126 /* FALLTHROUGH */
127 case ('E'):
128 cp->flags |= TBL_CELL_EQUAL;
129 goto mod;
130 case ('t'):
131 /* FALLTHROUGH */
132 case ('T'):
133 cp->flags |= TBL_CELL_TALIGN;
134 goto mod;
135 case ('d'):
136 /* FALLTHROUGH */
137 case ('D'):
138 cp->flags |= TBL_CELL_BALIGN;
139 goto mod;
140 case ('f'):
141 /* FALLTHROUGH */
142 case ('B'):
143 /* FALLTHROUGH */
144 case ('I'):
145 /* FALLTHROUGH */
146 case ('b'):
147 /* FALLTHROUGH */
148 case ('i'):
149 break;
150 default:
151 TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1);
152 return(0);
153 }
154
155 switch (p[(*pos)++]) {
156 case ('b'):
157 /* FALLTHROUGH */
158 case ('B'):
159 cp->flags |= TBL_CELL_BOLD;
160 goto mod;
161 case ('i'):
162 /* FALLTHROUGH */
163 case ('I'):
164 cp->flags |= TBL_CELL_ITALIC;
165 goto mod;
166 default:
167 break;
168 }
169
170 TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1);
171 return(0);
172 }
173
174 static int
175 cell(struct tbl *tbl, struct tbl_row *rp,
176 int ln, const char *p, int *pos)
177 {
178 struct tbl_cell *cp;
179 int i;
180 enum tbl_cellt c;
181
182 /* Parse the column position (`r', `R', `|', ...). */
183
184 for (i = 0; i < KEYS_MAX; i++)
185 if (p[*pos] == keys[i].name)
186 break;
187
188 if (KEYS_MAX == i) {
189 TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos);
190 return(0);
191 }
192
193 (*pos)++;
194 c = keys[i].key;
195
196 /* Extra check for the double-vertical. */
197
198 if (TBL_CELL_VERT == c && '|' == p[*pos]) {
199 (*pos)++;
200 c = TBL_CELL_DVERT;
201 }
202
203 /* Disallow adjacent spacers. */
204
205 if (rp->last && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) &&
206 (TBL_CELL_VERT == rp->last->pos ||
207 TBL_CELL_DVERT == rp->last->pos)) {
208 TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1);
209 return(0);
210 }
211
212 /* Allocate cell then parse its modifiers. */
213
214 cp = mandoc_calloc(1, sizeof(struct tbl_cell));
215 cp->pos = c;
216
217 if (rp->last) {
218 rp->last->next = cp;
219 rp->last = cp;
220 } else
221 rp->last = rp->first = cp;
222
223 return(mods(tbl, cp, ln, p, pos));
224 }
225
226
227 static void
228 row(struct tbl *tbl, int ln, const char *p, int *pos)
229 {
230 struct tbl_row *rp;
231
232 row: /*
233 * EBNF describing this section:
234 *
235 * row ::= row_list [:space:]* [.]?[\n]
236 * row_list ::= [:space:]* row_elem row_tail
237 * row_tail ::= [:space:]*[,] row_list |
238 * epsilon
239 * row_elem ::= [\t\ ]*[:alpha:]+
240 */
241
242 rp = mandoc_calloc(1, sizeof(struct tbl_row));
243 if (tbl->last) {
244 tbl->last->next = rp;
245 tbl->last = rp;
246 } else
247 tbl->last = tbl->first = rp;
248
249 cell:
250 while (isspace((unsigned char)p[*pos]))
251 (*pos)++;
252
253 /* Safely exit layout context. */
254
255 if ('.' == p[*pos]) {
256 tbl->part = TBL_PART_DATA;
257 if (NULL == tbl->first)
258 TBL_MSG(tbl, MANDOCERR_TBLNOLAYOUT, ln, *pos);
259 (*pos)++;
260 return;
261 }
262
263 /* End (and possibly restart) a row. */
264
265 if (',' == p[*pos]) {
266 (*pos)++;
267 goto row;
268 } else if ('\0' == p[*pos])
269 return;
270
271 if ( ! cell(tbl, rp, ln, p, pos))
272 return;
273
274 goto cell;
275 /* NOTREACHED */
276 }
277
278
279 int
280 tbl_layout(struct tbl *tbl, int ln, const char *p)
281 {
282 int pos;
283
284 pos = 0;
285 row(tbl, ln, p, &pos);
286
287 /* Always succeed. */
288 return(1);
289 }