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