Annotation of docbook2mdoc/macro.c, Revision 1.16
1.16 ! schwarze 1: /* $Id: macro.c,v 1.15 2019/04/23 21:09:42 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: {
1.14 schwarze 33: if (f->parastate == PARA_WANT) {
34: if (f->linestate != LINE_NEW) {
35: putchar('\n');
36: f->linestate = LINE_NEW;
37: }
38: puts(".Pp");
39: }
1.1 schwarze 40: switch (f->linestate) {
1.9 schwarze 41: case LINE_MACRO:
42: if (f->flags & FMT_NOSPC) {
43: fputs(" Ns ", stdout);
44: break;
45: }
46: if (f->flags & (FMT_CHILD | FMT_IMPL)) {
47: putchar(' ');
48: break;
49: }
50: /* FALLTHROUGH */
1.1 schwarze 51: case LINE_TEXT:
52: putchar('\n');
53: /* FALLTHROUGH */
54: case LINE_NEW:
55: putchar('.');
56: f->linestate = LINE_MACRO;
1.9 schwarze 57: f->flags = 0;
1.1 schwarze 58: break;
59: }
60: fputs(name, stdout);
1.9 schwarze 61: f->flags &= FMT_IMPL;
62: f->flags |= FMT_ARG;
1.14 schwarze 63: f->parastate = PARA_MID;
1.1 schwarze 64: }
65:
66: void
67: macro_close(struct format *f)
68: {
1.9 schwarze 69: if (f->linestate != LINE_NEW)
70: putchar('\n');
1.1 schwarze 71: f->linestate = LINE_NEW;
1.9 schwarze 72: f->flags = 0;
1.1 schwarze 73: }
74:
75: void
76: macro_line(struct format *f, const char *name)
77: {
1.7 schwarze 78: macro_close(f);
1.1 schwarze 79: macro_open(f, name);
80: macro_close(f);
81: }
82:
83: /*
84: * Print an argument string on a macro line, collapsing whitespace.
85: */
86: void
87: macro_addarg(struct format *f, const char *arg, int flags)
88: {
89: const char *cp;
1.13 schwarze 90: int quote_now;
1.1 schwarze 91:
92: assert(f->linestate == LINE_MACRO);
93:
94: /* Quote if requested and necessary. */
95:
1.13 schwarze 96: quote_now = 0;
1.1 schwarze 97: if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) {
98: for (cp = arg; *cp != '\0'; cp++)
99: if (isspace((unsigned char)*cp))
100: break;
101: if (*cp != '\0') {
102: if (flags & ARG_SPACE) {
103: putchar(' ');
104: flags &= ~ ARG_SPACE;
105: }
106: putchar('"');
107: flags = ARG_QUOTED;
1.13 schwarze 108: quote_now = 1;
1.1 schwarze 109: }
110: }
111:
112: for (cp = arg; *cp != '\0'; cp++) {
113:
114: /* Collapse whitespace. */
115:
116: if (isspace((unsigned char)*cp)) {
117: flags |= ARG_SPACE;
118: continue;
119: } else if (flags & ARG_SPACE) {
120: putchar(' ');
121: flags &= ~ ARG_SPACE;
122: }
123:
124: /* Escape us if we look like a macro. */
125:
1.12 schwarze 126: if ((flags & (ARG_QUOTED | ARG_UPPER)) == 0 &&
1.1 schwarze 127: (cp == arg || isspace((unsigned char)cp[-1])) &&
128: isupper((unsigned char)cp[0]) &&
129: islower((unsigned char)cp[1]) &&
130: (cp[2] == '\0' || cp[2] == ' ' ||
1.12 schwarze 131: ((cp[3] == '\0' || cp[3] == ' ') &&
132: (strncmp(cp, "Brq", 3) == 0 ||
133: strncmp(cp, "Bro", 3) == 0 ||
134: strncmp(cp, "Brc", 3) == 0 ||
135: strncmp(cp, "Bsx", 3) == 0))))
1.1 schwarze 136: fputs("\\&", stdout);
137:
138: if (*cp == '"')
139: fputs("\\(dq", stdout);
140: else if (flags & ARG_UPPER)
141: putchar(toupper((unsigned char)*cp));
142: else
143: putchar(*cp);
144: if (*cp == '\\')
145: putchar('e');
146: }
1.13 schwarze 147: if (quote_now)
148: putchar('"');
1.14 schwarze 149: f->parastate = PARA_MID;
1.1 schwarze 150: }
151:
152: void
153: macro_argline(struct format *f, const char *name, const char *arg)
154: {
155: macro_open(f, name);
156: macro_addarg(f, arg, ARG_SPACE);
157: macro_close(f);
158: }
159:
160: /*
161: * Recursively append text from the children of a node to a macro line.
162: */
163: void
1.8 schwarze 164: macro_addnode(struct format *f, struct pnode *n, int flags)
1.1 schwarze 165: {
1.5 schwarze 166: struct pnode *nc;
1.15 schwarze 167: int is_text, quote_now;
1.1 schwarze 168:
169: assert(f->linestate == LINE_MACRO);
170:
171: /*
1.2 schwarze 172: * If this node or its only child is a text node, just add
173: * that text, letting macro_addarg() decide about quoting.
1.1 schwarze 174: */
175:
1.8 schwarze 176: while ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
1.5 schwarze 177: TAILQ_NEXT(nc, child) == NULL)
1.8 schwarze 178: n = nc;
1.5 schwarze 179:
1.8 schwarze 180: if (n->node == NODE_TEXT || n->node == NODE_ESCAPE) {
181: macro_addarg(f, n->b, flags);
1.14 schwarze 182: f->parastate = PARA_MID;
1.1 schwarze 183: return;
184: }
185:
186: /*
187: * If we want the argument quoted and are not already
188: * in a quoted context, quote now.
189: */
190:
191: quote_now = 0;
192: if (flags & ARG_SINGLE) {
193: if ((flags & ARG_QUOTED) == 0) {
194: if (flags & ARG_SPACE) {
195: putchar(' ');
196: flags &= ~ARG_SPACE;
197: }
198: putchar('"');
199: flags |= ARG_QUOTED;
200: quote_now = 1;
201: }
202: flags &= ~ARG_SINGLE;
203: }
204:
205: /*
206: * Iterate to child and sibling nodes,
207: * inserting whitespace between nodes.
208: */
209:
1.5 schwarze 210: while (nc != NULL) {
211: macro_addnode(f, nc, flags);
1.15 schwarze 212: is_text = pnode_class(nc->node) == CLASS_TEXT;
1.5 schwarze 213: nc = TAILQ_NEXT(nc, child);
1.15 schwarze 214: if (nc == NULL || pnode_class(nc->node) != CLASS_TEXT)
215: is_text = 0;
1.16 ! schwarze 216: if (is_text && (nc->flags & NFLAG_SPC) == 0)
1.15 schwarze 217: flags &= ~ARG_SPACE;
218: else
219: flags |= ARG_SPACE;
1.1 schwarze 220: }
221: if (quote_now)
222: putchar('"');
1.14 schwarze 223: f->parastate = PARA_MID;
1.1 schwarze 224: }
225:
226: void
1.8 schwarze 227: macro_nodeline(struct format *f, const char *name, struct pnode *n, int flags)
1.1 schwarze 228: {
229: macro_open(f, name);
1.8 schwarze 230: macro_addnode(f, n, ARG_SPACE | flags);
1.1 schwarze 231: macro_close(f);
1.2 schwarze 232: }
233:
234:
235: /*
236: * Print a word on the current text line if one is open, or on a new text
237: * line otherwise. The flag ARG_SPACE inserts spaces between words.
238: */
239: void
1.10 schwarze 240: print_text(struct format *f, const char *word, int flags)
241: {
1.14 schwarze 242: if (f->parastate == PARA_WANT) {
243: if (f->linestate != LINE_NEW) {
244: putchar('\n');
245: f->linestate = LINE_NEW;
246: }
247: puts(".Pp");
248: }
1.2 schwarze 249: switch (f->linestate) {
250: case LINE_NEW:
251: break;
252: case LINE_TEXT:
253: if (flags & ARG_SPACE)
254: putchar(' ');
255: break;
256: case LINE_MACRO:
257: macro_close(f);
258: break;
259: }
1.11 schwarze 260: if (f->linestate == LINE_NEW && (*word == '.' || *word == '\''))
261: fputs("\\&", stdout);
1.10 schwarze 262: while (*word != '\0') {
263: putchar(*word);
264: if (*word++ == '\\')
265: putchar('e');
266: }
1.2 schwarze 267: f->linestate = LINE_TEXT;
1.14 schwarze 268: f->parastate = PARA_MID;
1.9 schwarze 269: f->flags = 0;
1.2 schwarze 270: }
271:
272: /*
273: * Recursively print the content of a node on a text line.
274: */
275: void
276: print_textnode(struct format *f, struct pnode *n)
277: {
278: struct pnode *nc;
279:
1.3 schwarze 280: if (n->node == NODE_TEXT || n->node == NODE_ESCAPE)
1.2 schwarze 281: print_text(f, n->b, ARG_SPACE);
282: else
283: TAILQ_FOREACH(nc, &n->childq, child)
284: print_textnode(f, nc);
1.1 schwarze 285: }
CVSweb