1 /* $Id: man_term.c,v 1.1 2009/04/06 20:30:40 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@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 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 #include <assert.h> 20 #include <err.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "term.h" 26 #include "man.h" 27 28 #define DECL_ARGS struct termp *p, \ 29 const struct man_node *n, \ 30 const struct man_meta *m 31 32 struct termact { 33 int (*pre)(DECL_ARGS); 34 void (*post)(DECL_ARGS); 35 }; 36 37 static int pre_B(DECL_ARGS); 38 static int pre_BI(DECL_ARGS); 39 static int pre_BR(DECL_ARGS); 40 static int pre_I(DECL_ARGS); 41 static int pre_IB(DECL_ARGS); 42 static int pre_IP(DECL_ARGS); 43 static int pre_IR(DECL_ARGS); 44 static int pre_PP(DECL_ARGS); 45 static int pre_RB(DECL_ARGS); 46 static int pre_RI(DECL_ARGS); 47 static int pre_SH(DECL_ARGS); 48 static int pre_SS(DECL_ARGS); 49 static int pre_TP(DECL_ARGS); 50 51 static void post_B(DECL_ARGS); 52 static void post_I(DECL_ARGS); 53 static void post_SH(DECL_ARGS); 54 static void post_SS(DECL_ARGS); 55 56 static const struct termact termacts[MAN_MAX] = { 57 { NULL, NULL }, /* __ */ 58 { NULL, NULL }, /* TH */ 59 { pre_SH, post_SH }, /* SH */ 60 { pre_SS, post_SS }, /* SS */ 61 { pre_TP, NULL }, /* TP */ 62 { pre_PP, NULL }, /* LP */ 63 { pre_PP, NULL }, /* PP */ 64 { pre_PP, NULL }, /* P */ 65 { pre_IP, NULL }, /* IP */ 66 { pre_PP, NULL }, /* HP */ /* FIXME */ 67 { NULL, NULL }, /* SM */ 68 { pre_B, post_B }, /* SB */ 69 { pre_BI, NULL }, /* BI */ 70 { pre_IB, NULL }, /* IB */ 71 { pre_BR, NULL }, /* BR */ 72 { pre_RB, NULL }, /* RB */ 73 { NULL, NULL }, /* R */ 74 { pre_B, post_B }, /* B */ 75 { pre_I, post_I }, /* I */ 76 { pre_IR, NULL }, /* IR */ 77 { pre_RI, NULL }, /* RI */ 78 { pre_PP, NULL }, /* br */ 79 { NULL, NULL }, /* na */ 80 { pre_I, post_I }, /* i */ 81 }; 82 83 static void print_head(struct termp *, 84 const struct man_meta *); 85 static void print_body(DECL_ARGS); 86 static void print_node(DECL_ARGS); 87 static void print_foot(struct termp *, 88 const struct man_meta *); 89 90 91 int 92 man_run(struct termp *p, const struct man *m) 93 { 94 95 print_head(p, man_meta(m)); 96 p->flags |= TERMP_NOSPACE; 97 print_body(p, man_node(m), man_meta(m)); 98 print_foot(p, man_meta(m)); 99 100 return(1); 101 } 102 103 104 /* ARGSUSED */ 105 static int 106 pre_I(DECL_ARGS) 107 { 108 109 p->flags |= TERMP_UNDER; 110 return(1); 111 } 112 113 114 /* ARGSUSED */ 115 static void 116 post_I(DECL_ARGS) 117 { 118 119 p->flags &= ~TERMP_UNDER; 120 } 121 122 123 /* ARGSUSED */ 124 static int 125 pre_IR(DECL_ARGS) 126 { 127 const struct man_node *nn; 128 int i; 129 130 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 131 if ( ! (i % 2)) 132 p->flags |= TERMP_UNDER; 133 if (i > 0) 134 p->flags |= TERMP_NOSPACE; 135 print_node(p, nn, m); 136 if ( ! (i % 2)) 137 p->flags &= ~TERMP_UNDER; 138 } 139 return(0); 140 } 141 142 143 /* ARGSUSED */ 144 static int 145 pre_IB(DECL_ARGS) 146 { 147 const struct man_node *nn; 148 int i; 149 150 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 151 p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER; 152 if (i > 0) 153 p->flags |= TERMP_NOSPACE; 154 print_node(p, nn, m); 155 p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER; 156 } 157 return(0); 158 } 159 160 161 /* ARGSUSED */ 162 static int 163 pre_RB(DECL_ARGS) 164 { 165 const struct man_node *nn; 166 int i; 167 168 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 169 if (i % 2) 170 p->flags |= TERMP_BOLD; 171 if (i > 0) 172 p->flags |= TERMP_NOSPACE; 173 print_node(p, nn, m); 174 if (i % 2) 175 p->flags &= ~TERMP_BOLD; 176 } 177 return(0); 178 } 179 180 181 /* ARGSUSED */ 182 static int 183 pre_RI(DECL_ARGS) 184 { 185 const struct man_node *nn; 186 int i; 187 188 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 189 if ( ! (i % 2)) 190 p->flags |= TERMP_UNDER; 191 if (i > 0) 192 p->flags |= TERMP_NOSPACE; 193 print_node(p, nn, m); 194 if ( ! (i % 2)) 195 p->flags &= ~TERMP_UNDER; 196 } 197 return(0); 198 } 199 200 201 /* ARGSUSED */ 202 static int 203 pre_BR(DECL_ARGS) 204 { 205 const struct man_node *nn; 206 int i; 207 208 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 209 if ( ! (i % 2)) 210 p->flags |= TERMP_BOLD; 211 if (i > 0) 212 p->flags |= TERMP_NOSPACE; 213 print_node(p, nn, m); 214 if ( ! (i % 2)) 215 p->flags &= ~TERMP_BOLD; 216 } 217 return(0); 218 } 219 220 221 /* ARGSUSED */ 222 static int 223 pre_BI(DECL_ARGS) 224 { 225 const struct man_node *nn; 226 int i; 227 228 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 229 p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD; 230 if (i > 0) 231 p->flags |= TERMP_NOSPACE; 232 print_node(p, nn, m); 233 p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD; 234 } 235 return(0); 236 } 237 238 239 /* ARGSUSED */ 240 static int 241 pre_B(DECL_ARGS) 242 { 243 244 p->flags |= TERMP_BOLD; 245 return(1); 246 } 247 248 249 /* ARGSUSED */ 250 static void 251 post_B(DECL_ARGS) 252 { 253 254 p->flags &= ~TERMP_BOLD; 255 } 256 257 258 /* ARGSUSED */ 259 static int 260 pre_PP(DECL_ARGS) 261 { 262 263 term_vspace(p); 264 p->offset = INDENT; 265 return(0); 266 } 267 268 269 /* ARGSUSED */ 270 static int 271 pre_IP(DECL_ARGS) 272 { 273 #if 0 274 const struct man_node *nn; 275 size_t offs; 276 #endif 277 278 term_vspace(p); 279 p->offset = INDENT; 280 281 #if 0 282 if (NULL == (nn = n->child)) 283 return(1); 284 if (MAN_TEXT != nn->type) 285 errx(1, "expected text line argument"); 286 287 if (nn->next) { 288 if (MAN_TEXT != nn->next->type) 289 errx(1, "expected text line argument"); 290 offs = (size_t)atoi(nn->next->string); 291 } else 292 offs = strlen(nn->string); 293 294 p->offset += offs; 295 #endif 296 p->flags |= TERMP_NOSPACE; 297 return(0); 298 } 299 300 301 /* ARGSUSED */ 302 static int 303 pre_TP(DECL_ARGS) 304 { 305 const struct man_node *nn; 306 size_t offs; 307 308 term_vspace(p); 309 p->offset = INDENT; 310 311 if (NULL == (nn = n->child)) 312 return(1); 313 314 if (nn->line == n->line) { 315 if (MAN_TEXT != nn->type) 316 errx(1, "expected text line argument"); 317 offs = (size_t)atoi(nn->string); 318 nn = nn->next; 319 } else 320 offs = INDENT; 321 322 for ( ; nn; nn = nn->next) 323 print_node(p, nn, m); 324 325 term_flushln(p); 326 p->flags |= TERMP_NOSPACE; 327 p->offset += offs; 328 return(0); 329 } 330 331 332 /* ARGSUSED */ 333 static int 334 pre_SS(DECL_ARGS) 335 { 336 337 term_vspace(p); 338 p->flags |= TERMP_BOLD; 339 return(1); 340 } 341 342 343 /* ARGSUSED */ 344 static void 345 post_SS(DECL_ARGS) 346 { 347 348 term_flushln(p); 349 p->flags &= ~TERMP_BOLD; 350 p->flags |= TERMP_NOSPACE; 351 } 352 353 354 /* ARGSUSED */ 355 static int 356 pre_SH(DECL_ARGS) 357 { 358 359 term_vspace(p); 360 p->offset = 0; 361 p->flags |= TERMP_BOLD; 362 return(1); 363 } 364 365 366 /* ARGSUSED */ 367 static void 368 post_SH(DECL_ARGS) 369 { 370 371 term_flushln(p); 372 p->offset = INDENT; 373 p->flags &= ~TERMP_BOLD; 374 p->flags |= TERMP_NOSPACE; 375 } 376 377 378 static void 379 print_node(DECL_ARGS) 380 { 381 int c, sz; 382 383 c = 1; 384 385 switch (n->type) { 386 case(MAN_ELEM): 387 if (termacts[n->tok].pre) 388 c = (*termacts[n->tok].pre)(p, n, m); 389 break; 390 case(MAN_TEXT): 391 if (0 == *n->string) { 392 term_vspace(p); 393 break; 394 } 395 /* 396 * Note! This is hacky. Here, we recognise the `\c' 397 * escape embedded in so many -man pages. It's supposed 398 * to remove the subsequent space, so we mark NOSPACE if 399 * it's encountered in the string. 400 */ 401 sz = (int)strlen(n->string); 402 term_word(p, n->string); 403 if (sz >= 2 && n->string[sz - 1] == 'c' && 404 n->string[sz - 2] == '\\') 405 p->flags |= TERMP_NOSPACE; 406 break; 407 default: 408 break; 409 } 410 411 if (c && n->child) 412 print_body(p, n->child, m); 413 414 switch (n->type) { 415 case (MAN_ELEM): 416 if (termacts[n->tok].post) 417 (*termacts[n->tok].post)(p, n, m); 418 break; 419 default: 420 break; 421 } 422 } 423 424 425 static void 426 print_body(DECL_ARGS) 427 { 428 print_node(p, n, m); 429 if ( ! n->next) 430 return; 431 print_body(p, n->next, m); 432 } 433 434 435 static void 436 print_foot(struct termp *p, const struct man_meta *meta) 437 { 438 struct tm *tm; 439 char *buf; 440 441 if (NULL == (buf = malloc(p->rmargin))) 442 err(1, "malloc"); 443 444 tm = localtime(&meta->date); 445 446 if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) 447 err(1, "strftime"); 448 449 term_vspace(p); 450 451 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 452 p->rmargin = p->maxrmargin - strlen(buf); 453 p->offset = 0; 454 455 if (meta->source) 456 term_word(p, meta->source); 457 if (meta->source) 458 term_word(p, ""); 459 term_flushln(p); 460 461 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 462 p->offset = p->rmargin; 463 p->rmargin = p->maxrmargin; 464 p->flags &= ~TERMP_NOBREAK; 465 466 term_word(p, buf); 467 term_flushln(p); 468 469 free(buf); 470 } 471 472 473 static void 474 print_head(struct termp *p, const struct man_meta *meta) 475 { 476 char *buf, *title; 477 478 p->rmargin = p->maxrmargin; 479 p->offset = 0; 480 481 if (NULL == (buf = malloc(p->rmargin))) 482 err(1, "malloc"); 483 if (NULL == (title = malloc(p->rmargin))) 484 err(1, "malloc"); 485 486 if (meta->vol) 487 (void)strlcpy(buf, meta->vol, p->rmargin); 488 else 489 *buf = 0; 490 491 (void)snprintf(title, p->rmargin, "%s(%d)", 492 meta->title, meta->msec); 493 494 p->offset = 0; 495 p->rmargin = (p->maxrmargin - strlen(buf)) / 2; 496 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 497 498 term_word(p, title); 499 term_flushln(p); 500 501 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 502 p->offset = p->rmargin; 503 p->rmargin = p->maxrmargin - strlen(title); 504 505 term_word(p, buf); 506 term_flushln(p); 507 508 p->offset = p->rmargin; 509 p->rmargin = p->maxrmargin; 510 p->flags &= ~TERMP_NOBREAK; 511 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 512 513 term_word(p, title); 514 term_flushln(p); 515 516 p->rmargin = p->maxrmargin; 517 p->offset = 0; 518 p->flags &= ~TERMP_NOSPACE; 519 520 free(title); 521 free(buf); 522 } 523 524