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