Annotation of docbook2mdoc/macro.c, Revision 1.7
1.7 ! schwarze 1: /* $Id: macro.c,v 1.6 2019/04/06 22:37:57 schwarze Exp $ */
1.1 schwarze 2: /*
3: * Copyright (c) 2019 Ingo Schwarze <schwarze@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 above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <stdio.h>
1.6 schwarze 20: #include <string.h>
1.1 schwarze 21:
22: #include "node.h"
23: #include "macro.h"
24:
25: /*
26: * The implementation of the macro line formatter,
27: * a part of the mdoc(7) formatter.
28: */
29:
30: void
31: macro_open(struct format *f, const char *name)
32: {
33: switch (f->linestate) {
34: case LINE_TEXT:
35: putchar('\n');
36: /* FALLTHROUGH */
37: case LINE_NEW:
38: putchar('.');
39: f->linestate = LINE_MACRO;
40: break;
41: case LINE_MACRO:
42: putchar(' ');
1.6 schwarze 43: if (f->spc == 0)
44: fputs("Ns ", stdout);
1.1 schwarze 45: break;
46: }
47: fputs(name, stdout);
48: }
49:
50: void
51: macro_close(struct format *f)
52: {
1.4 schwarze 53: if (f->linestate == LINE_NEW)
54: return;
1.1 schwarze 55: putchar('\n');
56: f->linestate = LINE_NEW;
57: }
58:
59: void
60: macro_line(struct format *f, const char *name)
61: {
1.7 ! schwarze 62: macro_close(f);
1.1 schwarze 63: macro_open(f, name);
64: macro_close(f);
65: }
66:
67: /*
1.6 schwarze 68: * At the end of a macro, decide whether the line needs to remain open
69: * because the next node follows without intervening whitespace;
70: * otherwise, close the line.
1.1 schwarze 71: */
72: void
73: macro_closepunct(struct format *f, struct pnode *pn)
74: {
1.6 schwarze 75: char *cp;
76:
77: if ((pn = TAILQ_NEXT(pn, child)) != NULL && pn->spc == 0) {
78:
79: /*
80: * If a non-text node follows without intervening
81: * whitespace, the handler of that node will decide
82: * whether and how to suppress whitespace. To allow
83: * that, the macro line needs to remain open.
84: */
85:
86: if (pn->node != NODE_TEXT && pn->node != NODE_ESCAPE)
87: return;
88:
89: /*
90: * Give closing punctuation
91: * in the form of trailing macro arguments.
92: */
93:
94: while (*pn->b != '\0' &&
95: strchr("!),.:;?]", *pn->b) != NULL) {
96: putchar(' ');
97: putchar(*pn->b);
98: pn->b++;
99: pn->bsz--;
100: }
101:
102: /*
103: * Text follows without intervening whitespace.
104: * Append the first word with .Ns.
105: */
106:
107: if (*pn->b != '\0' && isspace((unsigned char)*pn->b) == 0) {
108: fputs(" Ns", stdout);
109: for (cp = pn->b; *cp != '\0'; cp++)
110: if (isspace((unsigned char)*cp))
111: break;
112: *cp = '\0';
113: macro_addarg(f, pn->b, ARG_SPACE);
114: pn->bsz -= cp - pn->b;
115: pn->b = cp;
116: if (pn->bsz > 0) {
117: pn->b++;
118: pn->bsz--;
119: pn->spc = 1;
120: }
121: }
122:
123: /* Skip whitespace after the first word. */
124:
125: while (isspace((unsigned char)*pn->b)) {
126: pn->b++;
127: pn->bsz--;
128: pn->spc = 1;
129: }
1.1 schwarze 130: }
131: macro_close(f);
132: }
133:
134: /*
135: * Print an argument string on a macro line, collapsing whitespace.
136: */
137: void
138: macro_addarg(struct format *f, const char *arg, int flags)
139: {
140: const char *cp;
141:
142: assert(f->linestate == LINE_MACRO);
143:
144: /* Quote if requested and necessary. */
145:
146: if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) {
147: for (cp = arg; *cp != '\0'; cp++)
148: if (isspace((unsigned char)*cp))
149: break;
150: if (*cp != '\0') {
151: if (flags & ARG_SPACE) {
152: putchar(' ');
153: flags &= ~ ARG_SPACE;
154: }
155: putchar('"');
156: flags = ARG_QUOTED;
157: }
158: }
159:
160: for (cp = arg; *cp != '\0'; cp++) {
161:
162: /* Collapse whitespace. */
163:
164: if (isspace((unsigned char)*cp)) {
165: flags |= ARG_SPACE;
166: continue;
167: } else if (flags & ARG_SPACE) {
168: putchar(' ');
169: flags &= ~ ARG_SPACE;
170: }
171:
172: /* Escape us if we look like a macro. */
173:
174: if ((flags & ARG_QUOTED) == 0 &&
175: (cp == arg || isspace((unsigned char)cp[-1])) &&
176: isupper((unsigned char)cp[0]) &&
177: islower((unsigned char)cp[1]) &&
178: (cp[2] == '\0' || cp[2] == ' ' ||
179: (islower((unsigned char)cp[2]) &&
180: (cp[3] == '\0' || cp[3] == ' '))))
181: fputs("\\&", stdout);
182:
183: if (*cp == '"')
184: fputs("\\(dq", stdout);
185: else if (flags & ARG_UPPER)
186: putchar(toupper((unsigned char)*cp));
187: else
188: putchar(*cp);
189: if (*cp == '\\')
190: putchar('e');
191: }
192: }
193:
194: void
195: macro_argline(struct format *f, const char *name, const char *arg)
196: {
197: macro_open(f, name);
198: macro_addarg(f, arg, ARG_SPACE);
199: macro_close(f);
200: }
201:
202: /*
203: * Recursively append text from the children of a node to a macro line.
204: */
205: void
206: macro_addnode(struct format *f, struct pnode *pn, int flags)
207: {
1.5 schwarze 208: struct pnode *nc;
1.1 schwarze 209: int quote_now;
210:
211: assert(f->linestate == LINE_MACRO);
212:
213: /*
1.2 schwarze 214: * If this node or its only child is a text node, just add
215: * that text, letting macro_addarg() decide about quoting.
1.1 schwarze 216: */
217:
1.5 schwarze 218: while ((nc = TAILQ_FIRST(&pn->childq)) != NULL &&
219: TAILQ_NEXT(nc, child) == NULL)
220: pn = nc;
221:
222: if (pn->node == NODE_TEXT || pn->node == NODE_ESCAPE) {
1.1 schwarze 223: macro_addarg(f, pn->b, flags);
224: return;
225: }
226:
227: /*
228: * If we want the argument quoted and are not already
229: * in a quoted context, quote now.
230: */
231:
232: quote_now = 0;
233: if (flags & ARG_SINGLE) {
234: if ((flags & ARG_QUOTED) == 0) {
235: if (flags & ARG_SPACE) {
236: putchar(' ');
237: flags &= ~ARG_SPACE;
238: }
239: putchar('"');
240: flags |= ARG_QUOTED;
241: quote_now = 1;
242: }
243: flags &= ~ARG_SINGLE;
244: }
245:
246: /*
247: * Iterate to child and sibling nodes,
248: * inserting whitespace between nodes.
249: */
250:
1.5 schwarze 251: while (nc != NULL) {
252: macro_addnode(f, nc, flags);
253: nc = TAILQ_NEXT(nc, child);
1.1 schwarze 254: flags |= ARG_SPACE;
255: }
256: if (quote_now)
257: putchar('"');
258: }
259:
260: void
261: macro_nodeline(struct format *f, const char *name, struct pnode *pn, int flags)
262: {
263: macro_open(f, name);
264: macro_addnode(f, pn, ARG_SPACE | flags);
265: macro_close(f);
1.2 schwarze 266: }
267:
268:
269: /*
270: * Print a word on the current text line if one is open, or on a new text
271: * line otherwise. The flag ARG_SPACE inserts spaces between words.
272: */
273: void
274: print_text(struct format *f, const char *word, int flags) {
275: switch (f->linestate) {
276: case LINE_NEW:
277: break;
278: case LINE_TEXT:
279: if (flags & ARG_SPACE)
280: putchar(' ');
281: break;
282: case LINE_MACRO:
283: macro_close(f);
284: break;
285: }
286: fputs(word, stdout);
287: f->linestate = LINE_TEXT;
288: }
289:
290: /*
291: * Recursively print the content of a node on a text line.
292: */
293: void
294: print_textnode(struct format *f, struct pnode *n)
295: {
296: struct pnode *nc;
297:
1.3 schwarze 298: if (n->node == NODE_TEXT || n->node == NODE_ESCAPE)
1.2 schwarze 299: print_text(f, n->b, ARG_SPACE);
300: else
301: TAILQ_FOREACH(nc, &n->childq, child)
302: print_textnode(f, nc);
1.1 schwarze 303: }
CVSweb