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