-man linked to mandoc in documentation.
[mandoc.git] / man.c
1 /* $Id: man.c,v 1.3 2009/03/23 15:41:09 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "libman.h"
28
29 const char *const __man_macronames[MAN_MAX] = {
30 "\\\"", "TH", "SH", "SS",
31 "TP", "LP", "PP", "P",
32 "IP", "HP", "SM", "SB",
33 "BI", "IB", "BR", "RB",
34 "R", "B", "I", "IR"
35 };
36
37 const char * const *man_macronames = __man_macronames;
38
39 static struct man_node *man_node_alloc(int, int, enum man_type);
40 static int man_node_append(struct man *,
41 struct man_node *);
42 static int man_ptext(struct man *, int, char *);
43 static int man_pmacro(struct man *, int, char *);
44 static void man_free1(struct man *);
45 static void man_alloc1(struct man *);
46
47
48 const struct man_node *
49 man_node(const struct man *m)
50 {
51
52 return(MAN_HALT & m->flags ? NULL : m->first);
53 }
54
55
56 const struct man_meta *
57 man_meta(const struct man *m)
58 {
59
60 return(MAN_HALT & m->flags ? NULL : &m->meta);
61 }
62
63
64 void
65 man_reset(struct man *man)
66 {
67
68 man_free1(man);
69 man_alloc1(man);
70 }
71
72
73 void
74 man_free(struct man *man)
75 {
76
77 man_free1(man);
78
79 if (man->htab)
80 man_hash_free(man->htab);
81 free(man);
82 }
83
84
85 struct man *
86 man_alloc(void)
87 {
88 struct man *p;
89
90 p = calloc(1, sizeof(struct man));
91 if (NULL == p)
92 err(1, "calloc");
93
94 man_alloc1(p);
95
96 p->htab = man_hash_alloc();
97 return(p);
98 }
99
100
101 int
102 man_endparse(struct man *m)
103 {
104
105 if (MAN_HALT & m->flags)
106 return(0);
107 else if (man_macroend(m))
108 return(1);
109 m->flags |= MAN_HALT;
110 return(0);
111 }
112
113
114 int
115 man_parseln(struct man *m, int ln, char *buf)
116 {
117
118 return('.' == *buf ?
119 man_pmacro(m, ln, buf) :
120 man_ptext(m, ln, buf));
121 }
122
123
124 static void
125 man_free1(struct man *man)
126 {
127
128 if (man->first)
129 man_node_freelist(man->first);
130 if (man->meta.title)
131 free(man->meta.title);
132 if (man->meta.os)
133 free(man->meta.os);
134 if (man->meta.vol)
135 free(man->meta.vol);
136 }
137
138
139 static void
140 man_alloc1(struct man *m)
141 {
142
143 bzero(&m->meta, sizeof(struct man_meta));
144 m->flags = 0;
145 m->last = calloc(1, sizeof(struct man_node));
146 if (NULL == m->last)
147 err(1, "calloc");
148 m->first = m->last;
149 m->last->type = MAN_ROOT;
150 m->next = MAN_NEXT_CHILD;
151 }
152
153
154 static int
155 man_node_append(struct man *man, struct man_node *p)
156 {
157
158 assert(man->last);
159 assert(man->first);
160 assert(MAN_ROOT != p->type);
161
162 switch (man->next) {
163 case (MAN_NEXT_SIBLING):
164 man->last->next = p;
165 p->prev = man->last;
166 p->parent = man->last->parent;
167 break;
168 case (MAN_NEXT_CHILD):
169 man->last->child = p;
170 p->parent = man->last;
171 break;
172 default:
173 abort();
174 /* NOTREACHED */
175 }
176
177 #if 0
178 if ( ! man_valid_pre(man, p))
179 return(0);
180 if ( ! man_action_pre(man, p))
181 return(0);
182 #endif
183
184 man->last = p;
185
186 switch (p->type) {
187 case (MAN_TEXT):
188 #if 0
189 if ( ! man_valid_post(man))
190 return(0);
191 if ( ! man_action_post(man))
192 return(0);
193 #endif
194 break;
195 default:
196 break;
197 }
198
199 return(1);
200 }
201
202
203 static struct man_node *
204 man_node_alloc(int line, int pos, enum man_type type)
205 {
206 struct man_node *p;
207
208 if (NULL == (p = calloc(1, sizeof(struct man_node))))
209 err(1, "malloc");
210 p->line = line;
211 p->pos = pos;
212 p->type = type;
213
214 return(p);
215 }
216
217
218 int
219 man_elem_alloc(struct man *man, int line, int pos, int tok)
220 {
221 struct man_node *p;
222
223 p = man_node_alloc(line, pos, MAN_ELEM);
224 p->tok = tok;
225
226 return(man_node_append(man, p));
227 }
228
229
230 int
231 man_word_alloc(struct man *man,
232 int line, int pos, const char *word)
233 {
234 struct man_node *p;
235
236 p = man_node_alloc(line, pos, MAN_TEXT);
237 if (NULL == (p->string = strdup(word)))
238 err(1, "strdup");
239
240 return(man_node_append(man, p));
241 }
242
243
244 void
245 man_node_free(struct man_node *p)
246 {
247
248 if (p->string)
249 free(p->string);
250 free(p);
251 }
252
253
254 void
255 man_node_freelist(struct man_node *p)
256 {
257
258 if (p->child)
259 man_node_freelist(p->child);
260 if (p->next)
261 man_node_freelist(p->next);
262
263 man_node_free(p);
264 }
265
266
267 static int
268 man_ptext(struct man *m, int line, char *buf)
269 {
270
271 if ( ! man_word_alloc(m, line, 0, buf))
272 return(0);
273 m->next = MAN_NEXT_SIBLING;
274 return(1);
275 }
276
277
278 int
279 man_pmacro(struct man *m, int ln, char *buf)
280 {
281 int i, c;
282 char mac[5];
283
284 /* Comments and empties are quickly ignored. */
285
286 if (0 == buf[1])
287 return(1);
288
289 if (' ' == buf[1]) {
290 i = 2;
291 while (buf[i] && ' ' == buf[i])
292 i++;
293 if (0 == buf[i])
294 return(1);
295 warnx("invalid syntax");
296 return(0);
297 }
298
299 if (buf[1] && '\\' == buf[1])
300 if (buf[2] && '\"' == buf[2])
301 return(1);
302
303 /* Copy the first word into a nil-terminated buffer. */
304
305 for (i = 1; i < 5; i++) {
306 if (0 == (mac[i - 1] = buf[i]))
307 break;
308 else if (' ' == buf[i])
309 break;
310 }
311
312 mac[i - 1] = 0;
313
314 if (i == 5 || i <= 1) {
315 warnx("unknown macro: %s", mac);
316 goto err;
317 }
318
319 if (MAN_MAX == (c = man_hash_find(m->htab, mac))) {
320 warnx("unknown macro: %s", mac);
321 goto err;
322 }
323
324 /* The macro is sane. Jump to the next word. */
325
326 while (buf[i] && ' ' == buf[i])
327 i++;
328
329 /* Begin recursive parse sequence. */
330
331 if ( ! man_macro(m, c, ln, 1, &i, buf))
332 goto err;
333
334 return(1);
335
336 err: /* Error out. */
337
338 m->flags |= MAN_HALT;
339 return(0);
340 }
341