/* $Id: macro.c,v 1.1 2019/03/26 19:17:29 schwarze Exp $ */ /* * Copyright (c) 2019 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "node.h" #include "macro.h" /* * The implementation of the macro line formatter, * a part of the mdoc(7) formatter. */ void macro_open(struct format *f, const char *name) { switch (f->linestate) { case LINE_TEXT: putchar('\n'); /* FALLTHROUGH */ case LINE_NEW: putchar('.'); f->linestate = LINE_MACRO; break; case LINE_MACRO: putchar(' '); break; } fputs(name, stdout); } void macro_close(struct format *f) { assert(f->linestate == LINE_MACRO); putchar('\n'); f->linestate = LINE_NEW; } void macro_line(struct format *f, const char *name) { macro_open(f, name); macro_close(f); } /* * If the next node is a text node starting with closing punctuation, * emit the closing punctuation as a trailing macro argument. */ void macro_closepunct(struct format *f, struct pnode *pn) { if ((pn = TAILQ_NEXT(pn, child)) != NULL && pn->node == NODE_TEXT && pn->bsz > 0 && (pn->b[0] == ',' || pn->b[0] == '.') && (pn->bsz == 1 || isspace((unsigned char)pn->b[1]))) { putchar(' '); putchar(pn->b[0]); pn->b++; pn->bsz--; } macro_close(f); } /* * Print an argument string on a macro line, collapsing whitespace. */ void macro_addarg(struct format *f, const char *arg, int flags) { const char *cp; assert(f->linestate == LINE_MACRO); /* Quote if requested and necessary. */ if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) { for (cp = arg; *cp != '\0'; cp++) if (isspace((unsigned char)*cp)) break; if (*cp != '\0') { if (flags & ARG_SPACE) { putchar(' '); flags &= ~ ARG_SPACE; } putchar('"'); flags = ARG_QUOTED; } } for (cp = arg; *cp != '\0'; cp++) { /* Collapse whitespace. */ if (isspace((unsigned char)*cp)) { flags |= ARG_SPACE; continue; } else if (flags & ARG_SPACE) { putchar(' '); flags &= ~ ARG_SPACE; } /* Escape us if we look like a macro. */ if ((flags & ARG_QUOTED) == 0 && (cp == arg || isspace((unsigned char)cp[-1])) && isupper((unsigned char)cp[0]) && islower((unsigned char)cp[1]) && (cp[2] == '\0' || cp[2] == ' ' || (islower((unsigned char)cp[2]) && (cp[3] == '\0' || cp[3] == ' ')))) fputs("\\&", stdout); if (*cp == '"') fputs("\\(dq", stdout); else if (flags & ARG_UPPER) putchar(toupper((unsigned char)*cp)); else putchar(*cp); if (*cp == '\\') putchar('e'); } } void macro_argline(struct format *f, const char *name, const char *arg) { macro_open(f, name); macro_addarg(f, arg, ARG_SPACE); macro_close(f); } /* * Recursively append text from the children of a node to a macro line. */ void macro_addnode(struct format *f, struct pnode *pn, int flags) { int quote_now; assert(f->linestate == LINE_MACRO); /* * If the only child is a text node, just add that text, * letting macro_addarg() decide about quoting. */ pn = TAILQ_FIRST(&pn->childq); if (pn != NULL && pn->node == NODE_TEXT && TAILQ_NEXT(pn, child) == NULL) { macro_addarg(f, pn->b, flags); return; } /* * If we want the argument quoted and are not already * in a quoted context, quote now. */ quote_now = 0; if (flags & ARG_SINGLE) { if ((flags & ARG_QUOTED) == 0) { if (flags & ARG_SPACE) { putchar(' '); flags &= ~ARG_SPACE; } putchar('"'); flags |= ARG_QUOTED; quote_now = 1; } flags &= ~ARG_SINGLE; } /* * Iterate to child and sibling nodes, * inserting whitespace between nodes. */ while (pn != NULL) { if (pn->node == NODE_TEXT) macro_addarg(f, pn->b, flags); else macro_addnode(f, pn, flags); pn = TAILQ_NEXT(pn, child); flags |= ARG_SPACE; } if (quote_now) putchar('"'); } void macro_nodeline(struct format *f, const char *name, struct pnode *pn, int flags) { macro_open(f, name); macro_addnode(f, pn, ARG_SPACE | flags); macro_close(f); }