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