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