1 /* $OpenBSD: roff_term.c,v 1.23 2022/12/26 19:16:02 jmc Exp $ */ 2 /* 3 * Copyright (c) 2010,2014,2015,2017-2020 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 AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <sys/types.h> 18 19 #include <assert.h> 20 #include <stdio.h> 21 #include <string.h> 22 23 #include "mandoc.h" 24 #include "roff.h" 25 #include "out.h" 26 #include "term.h" 27 28 #define ROFF_TERM_ARGS struct termp *p, const struct roff_node *n 29 30 typedef void (*roff_term_pre_fp)(ROFF_TERM_ARGS); 31 32 static void roff_term_pre_br(ROFF_TERM_ARGS); 33 static void roff_term_pre_ce(ROFF_TERM_ARGS); 34 static void roff_term_pre_ft(ROFF_TERM_ARGS); 35 static void roff_term_pre_ll(ROFF_TERM_ARGS); 36 static void roff_term_pre_mc(ROFF_TERM_ARGS); 37 static void roff_term_pre_po(ROFF_TERM_ARGS); 38 static void roff_term_pre_sp(ROFF_TERM_ARGS); 39 static void roff_term_pre_ta(ROFF_TERM_ARGS); 40 static void roff_term_pre_ti(ROFF_TERM_ARGS); 41 42 static const roff_term_pre_fp roff_term_pre_acts[ROFF_MAX] = { 43 roff_term_pre_br, /* br */ 44 roff_term_pre_ce, /* ce */ 45 roff_term_pre_br, /* fi */ 46 roff_term_pre_ft, /* ft */ 47 roff_term_pre_ll, /* ll */ 48 roff_term_pre_mc, /* mc */ 49 roff_term_pre_br, /* nf */ 50 roff_term_pre_po, /* po */ 51 roff_term_pre_ce, /* rj */ 52 roff_term_pre_sp, /* sp */ 53 roff_term_pre_ta, /* ta */ 54 roff_term_pre_ti, /* ti */ 55 }; 56 57 58 void 59 roff_term_pre(struct termp *p, const struct roff_node *n) 60 { 61 assert(n->tok < ROFF_MAX); 62 (*roff_term_pre_acts[n->tok])(p, n); 63 } 64 65 static void 66 roff_term_pre_br(ROFF_TERM_ARGS) 67 { 68 term_newln(p); 69 if (p->flags & TERMP_BRIND) { 70 p->tcol->offset = p->tcol->rmargin; 71 p->tcol->rmargin = p->maxrmargin; 72 p->trailspace = 0; 73 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); 74 p->flags |= TERMP_NOSPACE; 75 } 76 } 77 78 static void 79 roff_term_pre_ce(ROFF_TERM_ARGS) 80 { 81 const struct roff_node *nc1, *nc2; 82 83 roff_term_pre_br(p, n); 84 p->flags |= n->tok == ROFF_ce ? TERMP_CENTER : TERMP_RIGHT; 85 nc1 = n->child->next; 86 while (nc1 != NULL) { 87 nc2 = nc1; 88 do { 89 nc2 = nc2->next; 90 } while (nc2 != NULL && (nc2->type != ROFFT_TEXT || 91 (nc2->flags & NODE_LINE) == 0)); 92 while (nc1 != nc2) { 93 if (nc1->type == ROFFT_TEXT) 94 term_word(p, nc1->string); 95 else 96 roff_term_pre(p, nc1); 97 nc1 = nc1->next; 98 } 99 p->flags |= TERMP_NOSPACE; 100 term_flushln(p); 101 } 102 p->flags &= ~(TERMP_CENTER | TERMP_RIGHT); 103 } 104 105 static void 106 roff_term_pre_ft(ROFF_TERM_ARGS) 107 { 108 const char *cp; 109 110 cp = n->child->string; 111 switch (mandoc_font(cp, (int)strlen(cp))) { 112 case ESCAPE_FONTBOLD: 113 case ESCAPE_FONTCB: 114 term_fontrepl(p, TERMFONT_BOLD); 115 break; 116 case ESCAPE_FONTITALIC: 117 case ESCAPE_FONTCI: 118 term_fontrepl(p, TERMFONT_UNDER); 119 break; 120 case ESCAPE_FONTBI: 121 term_fontrepl(p, TERMFONT_BI); 122 break; 123 case ESCAPE_FONTPREV: 124 term_fontlast(p); 125 break; 126 case ESCAPE_FONTROMAN: 127 case ESCAPE_FONTCR: 128 term_fontrepl(p, TERMFONT_NONE); 129 break; 130 default: 131 break; 132 } 133 } 134 135 static void 136 roff_term_pre_ll(ROFF_TERM_ARGS) 137 { 138 term_setwidth(p, n->child != NULL ? n->child->string : NULL); 139 } 140 141 static void 142 roff_term_pre_mc(ROFF_TERM_ARGS) 143 { 144 if (p->col) { 145 p->flags |= TERMP_NOBREAK; 146 term_flushln(p); 147 p->flags &= ~(TERMP_NOBREAK | TERMP_NOSPACE); 148 } 149 if (n->child != NULL) { 150 p->mc = n->child->string; 151 p->flags |= TERMP_NEWMC; 152 } else 153 p->flags |= TERMP_ENDMC; 154 } 155 156 static void 157 roff_term_pre_po(ROFF_TERM_ARGS) 158 { 159 struct roffsu su; 160 static int po, pouse, polast; 161 int ponew; 162 163 /* Revert the currently active page offset. */ 164 p->tcol->offset -= pouse; 165 166 /* Determine the requested page offset. */ 167 if (n->child != NULL && 168 a2roffsu(n->child->string, &su, SCALE_EM) != NULL) { 169 ponew = term_hen(p, &su); 170 if (*n->child->string == '+' || 171 *n->child->string == '-') 172 ponew += po; 173 } else 174 ponew = polast; 175 176 /* Remember both the previous and the newly requested offset. */ 177 polast = po; 178 po = ponew; 179 180 /* Truncate to the range [-offset, 60], remember, and apply it. */ 181 pouse = po >= 60 ? 60 : 182 po < -(int)p->tcol->offset ? -p->tcol->offset : po; 183 p->tcol->offset += pouse; 184 } 185 186 static void 187 roff_term_pre_sp(ROFF_TERM_ARGS) 188 { 189 struct roffsu su; 190 int len; 191 192 if (n->child != NULL) { 193 if (a2roffsu(n->child->string, &su, SCALE_VS) == NULL) 194 su.scale = 1.0; 195 len = term_vspan(p, &su); 196 } else 197 len = 1; 198 199 if (len < 0) 200 p->skipvsp -= len; 201 else 202 while (len--) 203 term_vspace(p); 204 205 roff_term_pre_br(p, n); 206 } 207 208 static void 209 roff_term_pre_ta(ROFF_TERM_ARGS) 210 { 211 term_tab_set(p, NULL); 212 for (n = n->child; n != NULL; n = n->next) 213 term_tab_set(p, n->string); 214 } 215 216 static void 217 roff_term_pre_ti(ROFF_TERM_ARGS) 218 { 219 struct roffsu su; 220 const char *cp; 221 const size_t maxoff = 72; 222 int len, sign; 223 224 roff_term_pre_br(p, n); 225 226 if (n->child == NULL) 227 return; 228 cp = n->child->string; 229 if (*cp == '+') { 230 sign = 1; 231 cp++; 232 } else if (*cp == '-') { 233 sign = -1; 234 cp++; 235 } else 236 sign = 0; 237 238 if (a2roffsu(cp, &su, SCALE_EM) == NULL) 239 return; 240 len = term_hen(p, &su); 241 242 switch (sign) { 243 case 1: 244 if (p->tcol->offset + len <= maxoff) 245 p->ti = len; 246 else if (p->tcol->offset < maxoff) 247 p->ti = maxoff - p->tcol->offset; 248 else 249 p->ti = 0; 250 break; 251 case -1: 252 if ((size_t)len < p->tcol->offset) 253 p->ti = -len; 254 else 255 p->ti = -p->tcol->offset; 256 break; 257 default: 258 if ((size_t)len > maxoff) 259 len = maxoff; 260 p->ti = len - p->tcol->offset; 261 break; 262 } 263 p->tcol->offset += p->ti; 264 } 265