1 /* $Id: roff_term.c,v 1.26 2025/07/16 14:33:08 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2010, 2014, 2015, 2017-2021, 2025 4 * Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include "config.h" 19 20 #include <sys/types.h> 21 22 #include <assert.h> 23 #include <stdio.h> 24 #include <string.h> 25 26 #include "mandoc.h" 27 #include "roff.h" 28 #include "out.h" 29 #include "term.h" 30 31 #define ROFF_TERM_ARGS struct termp *p, const struct roff_node *n 32 33 typedef void (*roff_term_pre_fp)(ROFF_TERM_ARGS); 34 35 static void roff_term_pre_br(ROFF_TERM_ARGS); 36 static void roff_term_pre_ce(ROFF_TERM_ARGS); 37 static void roff_term_pre_ft(ROFF_TERM_ARGS); 38 static void roff_term_pre_ll(ROFF_TERM_ARGS); 39 static void roff_term_pre_mc(ROFF_TERM_ARGS); 40 static void roff_term_pre_po(ROFF_TERM_ARGS); 41 static void roff_term_pre_sp(ROFF_TERM_ARGS); 42 static void roff_term_pre_ta(ROFF_TERM_ARGS); 43 static void roff_term_pre_ti(ROFF_TERM_ARGS); 44 45 static const roff_term_pre_fp roff_term_pre_acts[ROFF_MAX] = { 46 roff_term_pre_br, /* br */ 47 roff_term_pre_ce, /* ce */ 48 roff_term_pre_br, /* fi */ 49 roff_term_pre_ft, /* ft */ 50 roff_term_pre_ll, /* ll */ 51 roff_term_pre_mc, /* mc */ 52 roff_term_pre_br, /* nf */ 53 roff_term_pre_po, /* po */ 54 roff_term_pre_ce, /* rj */ 55 roff_term_pre_sp, /* sp */ 56 roff_term_pre_ta, /* ta */ 57 roff_term_pre_ti, /* ti */ 58 }; 59 60 61 void 62 roff_term_pre(struct termp *p, const struct roff_node *n) 63 { 64 assert(n->tok < ROFF_MAX); 65 (*roff_term_pre_acts[n->tok])(p, n); 66 } 67 68 static void 69 roff_term_pre_br(ROFF_TERM_ARGS) 70 { 71 term_newln(p); 72 if (p->flags & TERMP_BRIND) { 73 p->tcol->offset = p->tcol->rmargin; 74 p->tcol->rmargin = p->maxrmargin; 75 p->trailspace = 0; 76 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); 77 p->flags |= TERMP_NOSPACE; 78 } 79 } 80 81 static void 82 roff_term_pre_ce(ROFF_TERM_ARGS) 83 { 84 const struct roff_node *nc1, *nc2; 85 86 roff_term_pre_br(p, n); 87 p->flags |= n->tok == ROFF_ce ? TERMP_CENTER : TERMP_RIGHT; 88 nc1 = n->child->next; 89 while (nc1 != NULL) { 90 nc2 = nc1; 91 do { 92 nc2 = nc2->next; 93 } while (nc2 != NULL && (nc2->type != ROFFT_TEXT || 94 (nc2->flags & NODE_LINE) == 0)); 95 while (nc1 != nc2) { 96 if (nc1->type == ROFFT_TEXT) 97 term_word(p, nc1->string); 98 else 99 roff_term_pre(p, nc1); 100 nc1 = nc1->next; 101 } 102 p->flags |= TERMP_NOSPACE; 103 term_flushln(p); 104 } 105 p->flags &= ~(TERMP_CENTER | TERMP_RIGHT); 106 } 107 108 static void 109 roff_term_pre_ft(ROFF_TERM_ARGS) 110 { 111 const char *cp; 112 113 cp = n->child->string; 114 switch (mandoc_font(cp, (int)strlen(cp))) { 115 case ESCAPE_FONTBOLD: 116 case ESCAPE_FONTCB: 117 term_fontrepl(p, TERMFONT_BOLD); 118 break; 119 case ESCAPE_FONTITALIC: 120 case ESCAPE_FONTCI: 121 term_fontrepl(p, TERMFONT_UNDER); 122 break; 123 case ESCAPE_FONTBI: 124 term_fontrepl(p, TERMFONT_BI); 125 break; 126 case ESCAPE_FONTPREV: 127 term_fontlast(p); 128 break; 129 case ESCAPE_FONTROMAN: 130 case ESCAPE_FONTCR: 131 term_fontrepl(p, TERMFONT_NONE); 132 break; 133 default: 134 break; 135 } 136 } 137 138 static void 139 roff_term_pre_ll(ROFF_TERM_ARGS) 140 { 141 term_setwidth(p, n->child != NULL ? n->child->string : NULL); 142 } 143 144 static void 145 roff_term_pre_mc(ROFF_TERM_ARGS) 146 { 147 if (p->col) { 148 p->flags |= TERMP_NOBREAK; 149 term_flushln(p); 150 p->flags &= ~(TERMP_NOBREAK | TERMP_NOSPACE); 151 } 152 if (n->child != NULL) { 153 p->mc = n->child->string; 154 p->flags |= TERMP_NEWMC; 155 } else 156 p->flags |= TERMP_ENDMC; 157 } 158 159 static void 160 roff_term_pre_po(ROFF_TERM_ARGS) 161 { 162 struct roffsu su; 163 164 /* Page offsets in basic units. */ 165 static int polast; /* Previously requested. */ 166 static int po; /* Currently requested. */ 167 static int pouse; /* Currently used. */ 168 int pomin; /* Minimum to be used. */ 169 int pomax; /* Maximum to be used. */ 170 int ponew; /* Newly requested. */ 171 172 /* Revert the currently active page offset. */ 173 p->tcol->offset -= pouse; 174 175 /* Determine the requested page offset. */ 176 if (n->child != NULL && 177 a2roffsu(n->child->string, &su, SCALE_EM) != NULL) { 178 ponew = term_hspan(p, &su); 179 if (*n->child->string == '+' || 180 *n->child->string == '-') 181 ponew += po; 182 } else 183 ponew = polast; 184 185 /* Remember both the previous and the newly requested offset. */ 186 polast = po; 187 po = ponew; 188 189 /* Truncate to the range [-offset, 60], remember, and apply it. */ 190 pomin = -p->tcol->offset; 191 pomax = term_len(p, 60); 192 pouse = po > pomax ? pomax : po < pomin ? pomin : po; 193 p->tcol->offset += pouse; 194 } 195 196 static void 197 roff_term_pre_sp(ROFF_TERM_ARGS) 198 { 199 struct roffsu su; 200 int len; 201 202 if (n->child != NULL) { 203 if (a2roffsu(n->child->string, &su, SCALE_VS) == NULL) 204 su.scale = 1.0; 205 len = term_vspan(p, &su); 206 } else 207 len = 1; 208 209 if (len < 0) 210 p->skipvsp -= len; 211 else 212 while (len--) 213 term_vspace(p); 214 215 roff_term_pre_br(p, n); 216 } 217 218 static void 219 roff_term_pre_ta(ROFF_TERM_ARGS) 220 { 221 term_tab_set(p, NULL); 222 for (n = n->child; n != NULL; n = n->next) 223 term_tab_set(p, n->string); 224 } 225 226 static void 227 roff_term_pre_ti(ROFF_TERM_ARGS) 228 { 229 struct roffsu su; 230 const char *cp; /* Request argument. */ 231 size_t maxoff; /* Maximum indentation in basic units. */ 232 int len; /* Request argument in basic units. */ 233 int sign; 234 235 roff_term_pre_br(p, n); 236 237 if (n->child == NULL) 238 return; 239 cp = n->child->string; 240 if (*cp == '+') { 241 sign = 1; 242 cp++; 243 } else if (*cp == '-') { 244 sign = -1; 245 cp++; 246 } else 247 sign = 0; 248 249 if (a2roffsu(cp, &su, SCALE_EM) == NULL) 250 return; 251 len = term_hspan(p, &su); 252 maxoff = term_len(p, 72); 253 254 switch (sign) { 255 case 1: 256 if (p->tcol->offset + len <= maxoff) 257 p->ti = len; 258 else if (p->tcol->offset < maxoff) 259 p->ti = maxoff - p->tcol->offset; 260 else 261 p->ti = 0; 262 break; 263 case -1: 264 if ((size_t)len < p->tcol->offset) 265 p->ti = -len; 266 else 267 p->ti = -p->tcol->offset; 268 break; 269 default: 270 if ((size_t)len > maxoff) 271 len = maxoff; 272 p->ti = len - p->tcol->offset; 273 break; 274 } 275 p->tcol->offset += p->ti; 276 } 277