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