xref: /openbsd-src/usr.bin/mandoc/man_macro.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $Id: man_macro.c,v 1.1 2009/04/06 20:30:40 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 <stdlib.h>
22 #include <string.h>
23 
24 #include "libman.h"
25 
26 #define	FL_NLINE	(1 << 0)
27 #define	FL_TLINE	(1 << 1)
28 
29 static	int		 man_args(struct man *, int,
30 				int *, char *, char **);
31 
32 static	int man_flags[MAN_MAX] = {
33 	0, /* __ */
34 	0, /* TH */
35 	0, /* SH */
36 	0, /* SS */
37 	FL_TLINE, /* TP */
38 	0, /* LP */
39 	0, /* PP */
40 	0, /* P */
41 	0, /* IP */
42 	0, /* HP */
43 	FL_NLINE, /* SM */
44 	FL_NLINE, /* SB */
45 	FL_NLINE, /* BI */
46 	FL_NLINE, /* IB */
47 	FL_NLINE, /* BR */
48 	FL_NLINE, /* RB */
49 	FL_NLINE, /* R */
50 	FL_NLINE, /* B */
51 	FL_NLINE, /* I */
52 	FL_NLINE, /* IR */
53 	FL_NLINE, /* RI */
54 	0, /* br */
55 	0, /* na */
56 	FL_NLINE, /* i */
57 };
58 
59 int
60 man_macro(struct man *man, int tok, int line,
61 		int ppos, int *pos, char *buf)
62 {
63 	int		 w, la;
64 	char		*p;
65 	struct man_node	*n;
66 
67 	if ( ! man_elem_alloc(man, line, ppos, tok))
68 		return(0);
69 	n = man->last;
70 	man->next = MAN_NEXT_CHILD;
71 
72 	for (;;) {
73 		la = *pos;
74 		w = man_args(man, line, pos, buf, &p);
75 
76 		if (-1 == w)
77 			return(0);
78 		if (0 == w)
79 			break;
80 
81 		if ( ! man_word_alloc(man, line, la, p))
82 			return(0);
83 		man->next = MAN_NEXT_SIBLING;
84 	}
85 
86 	if (n == man->last && (FL_NLINE & man_flags[tok])) {
87 		if (MAN_NLINE & man->flags)
88 			return(man_verr(man, line, ppos,
89 				"next-line scope already open"));
90 		man->flags |= MAN_NLINE;
91 		return(1);
92 	}
93 
94 	if (FL_TLINE & man_flags[tok]) {
95 		if (MAN_NLINE & man->flags)
96 			return(man_verr(man, line, ppos,
97 				"next-line scope already open"));
98 		man->flags |= MAN_NLINE;
99 		return(1);
100 	}
101 
102 	/*
103 	 * Note that when TH is pruned, we'll be back at the root, so
104 	 * make sure that we don't clobber as its sibling.
105 	 */
106 
107 	for ( ; man->last; man->last = man->last->parent) {
108 		if (man->last == n)
109 			break;
110 		if (man->last->type == MAN_ROOT)
111 			break;
112 		if ( ! man_valid_post(man))
113 			return(0);
114 		if ( ! man_action_post(man))
115 			return(0);
116 	}
117 
118 	assert(man->last);
119 
120 	/*
121 	 * Same here regarding whether we're back at the root.
122 	 */
123 
124 	if (man->last->type != MAN_ROOT && ! man_valid_post(man))
125 		return(0);
126 	if (man->last->type != MAN_ROOT && ! man_action_post(man))
127 		return(0);
128 	if (man->last->type != MAN_ROOT)
129 		man->next = MAN_NEXT_SIBLING;
130 
131 	return(1);
132 }
133 
134 
135 int
136 man_macroend(struct man *m)
137 {
138 
139 	for ( ; m->last && m->last != m->first;
140 			m->last = m->last->parent) {
141 		if ( ! man_valid_post(m))
142 			return(0);
143 		if ( ! man_action_post(m))
144 			return(0);
145 	}
146 	assert(m->last == m->first);
147 
148 	if ( ! man_valid_post(m))
149 		return(0);
150 	if ( ! man_action_post(m))
151 		return(0);
152 
153 	return(1);
154 }
155 
156 
157 /* ARGSUSED */
158 static int
159 man_args(struct man *m, int line,
160 		int *pos, char *buf, char **v)
161 {
162 
163 	if (0 == buf[*pos])
164 		return(0);
165 
166 	/* First parse non-quoted strings. */
167 
168 	if ('\"' != buf[*pos]) {
169 		*v = &buf[*pos];
170 
171 		while (buf[*pos]) {
172 			if (' ' == buf[*pos])
173 				if ('\\' != buf[*pos - 1])
174 					break;
175 			(*pos)++;
176 		}
177 
178 		if (0 == buf[*pos])
179 			return(1);
180 
181 		buf[(*pos)++] = 0;
182 
183 		if (0 == buf[*pos])
184 			return(1);
185 
186 		while (buf[*pos] && ' ' == buf[*pos])
187 			(*pos)++;
188 
189 		if (buf[*pos])
190 			return(1);
191 
192 		if ( ! man_vwarn(m, line, *pos, "trailing spaces"))
193 			return(-1);
194 
195 		return(1);
196 	}
197 
198 	/*
199 	 * If we're a quoted string (and quoted strings are allowed),
200 	 * then parse ahead to the next quote.  If none's found, it's an
201 	 * error.  After, parse to the next word.
202 	 */
203 
204 	*v = &buf[++(*pos)];
205 
206 	while (buf[*pos] && '\"' != buf[*pos])
207 		(*pos)++;
208 
209 	if (0 == buf[*pos]) {
210 		if ( ! man_vwarn(m, line, *pos, "unterminated quote"))
211 			return(-1);
212 		return(1);
213 	}
214 
215 	buf[(*pos)++] = 0;
216 	if (0 == buf[*pos])
217 		return(1);
218 
219 	while (buf[*pos] && ' ' == buf[*pos])
220 		(*pos)++;
221 
222 	if (buf[*pos])
223 		return(1);
224 
225 	if ( ! man_vwarn(m, line, *pos, "trailing spaces"))
226 		return(-1);
227 	return(1);
228 }
229