1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott (at) gmail.com> 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 MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 /* Format range. */ 27 struct format_range { 28 u_int index; 29 struct screen *s; 30 31 u_int start; 32 u_int end; 33 34 enum style_range_type type; 35 u_int argument; 36 char string[16]; 37 38 TAILQ_ENTRY(format_range) entry; 39 }; 40 TAILQ_HEAD(format_ranges, format_range); 41 42 /* Does this range match this style? */ 43 static int 44 format_is_type(struct format_range *fr, struct style *sy) 45 { 46 if (fr->type != sy->range_type) 47 return (0); 48 switch (fr->type) { 49 case STYLE_RANGE_NONE: 50 case STYLE_RANGE_LEFT: 51 case STYLE_RANGE_RIGHT: 52 return (1); 53 case STYLE_RANGE_PANE: 54 case STYLE_RANGE_WINDOW: 55 case STYLE_RANGE_SESSION: 56 return (fr->argument == sy->range_argument); 57 case STYLE_RANGE_USER: 58 return (strcmp(fr->string, sy->range_string) == 0); 59 } 60 return (1); 61 } 62 63 /* Free a range. */ 64 static void 65 format_free_range(struct format_ranges *frs, struct format_range *fr) 66 { 67 TAILQ_REMOVE(frs, fr, entry); 68 free(fr); 69 } 70 71 /* Fix range positions. */ 72 static void 73 format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset, 74 u_int start, u_int width) 75 { 76 struct format_range *fr, *fr1; 77 78 if (frs == NULL) 79 return; 80 81 TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) { 82 if (fr->s != s) 83 continue; 84 85 if (fr->end <= start || fr->start >= start + width) { 86 format_free_range(frs, fr); 87 continue; 88 } 89 90 if (fr->start < start) 91 fr->start = start; 92 if (fr->end > start + width) 93 fr->end = start + width; 94 if (fr->start == fr->end) { 95 format_free_range(frs, fr); 96 continue; 97 } 98 99 fr->start -= start; 100 fr->end -= start; 101 102 fr->start += offset; 103 fr->end += offset; 104 } 105 } 106 107 /* Draw a part of the format. */ 108 static void 109 format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy, 110 struct screen *s, struct format_ranges *frs, u_int offset, u_int start, 111 u_int width) 112 { 113 /* 114 * The offset is how far from the cursor on the target screen; start 115 * and width how much to copy from the source screen. 116 */ 117 screen_write_cursormove(octx, ocx + offset, ocy, 0); 118 screen_write_fast_copy(octx, s, start, 0, width, 1); 119 format_update_ranges(frs, s, offset, start, width); 120 } 121 122 /* Draw list part of format. */ 123 static void 124 format_draw_put_list(struct screen_write_ctx *octx, 125 u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list, 126 struct screen *list_left, struct screen *list_right, int focus_start, 127 int focus_end, struct format_ranges *frs) 128 { 129 u_int start, focus_centre; 130 131 /* If there is enough space for the list, draw it entirely. */ 132 if (width >= list->cx) { 133 format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width); 134 return; 135 } 136 137 /* The list needs to be trimmed. Try to keep the focus visible. */ 138 focus_centre = focus_start + (focus_end - focus_start) / 2; 139 if (focus_centre < width / 2) 140 start = 0; 141 else 142 start = focus_centre - width / 2; 143 if (start + width > list->cx) 144 start = list->cx - width; 145 146 /* Draw <> markers at either side if needed. */ 147 if (start != 0 && width > list_left->cx) { 148 screen_write_cursormove(octx, ocx + offset, ocy, 0); 149 screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1); 150 offset += list_left->cx; 151 start += list_left->cx; 152 width -= list_left->cx; 153 } 154 if (start + width < list->cx && width > list_right->cx) { 155 screen_write_cursormove(octx, ocx + offset + width - 156 list_right->cx, ocy, 0); 157 screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx, 158 1); 159 width -= list_right->cx; 160 } 161 162 /* Draw the list screen itself. */ 163 format_draw_put(octx, ocx, ocy, list, frs, offset, start, width); 164 } 165 166 /* Draw format with no list. */ 167 static void 168 format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx, 169 u_int ocy, struct screen *left, struct screen *centre, struct screen *right, 170 struct screen *abs_centre, struct format_ranges *frs) 171 { 172 u_int width_left, width_centre, width_right, width_abs_centre; 173 174 width_left = left->cx; 175 width_centre = centre->cx; 176 width_right = right->cx; 177 width_abs_centre = abs_centre->cx; 178 179 /* 180 * Try to keep as much of the left and right as possible at the expense 181 * of the centre. 182 */ 183 while (width_left + width_centre + width_right > available) { 184 if (width_centre > 0) 185 width_centre--; 186 else if (width_right > 0) 187 width_right--; 188 else 189 width_left--; 190 } 191 192 /* Write left. */ 193 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); 194 195 /* Write right at available - width_right. */ 196 format_draw_put(octx, ocx, ocy, right, frs, 197 available - width_right, 198 right->cx - width_right, 199 width_right); 200 201 /* 202 * Write centre halfway between 203 * width_left 204 * and 205 * available - width_right. 206 */ 207 format_draw_put(octx, ocx, ocy, centre, frs, 208 width_left 209 + ((available - width_right) - width_left) / 2 210 - width_centre / 2, 211 centre->cx / 2 - width_centre / 2, 212 width_centre); 213 214 /* 215 * Write abs_centre in the perfect centre of all horizontal space. 216 */ 217 if (width_abs_centre > available) 218 width_abs_centre = available; 219 format_draw_put(octx, ocx, ocy, abs_centre, frs, 220 (available - width_abs_centre) / 2, 221 0, 222 width_abs_centre); 223 } 224 225 /* Draw format with list on the left. */ 226 static void 227 format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, 228 u_int ocy, struct screen *left, struct screen *centre, struct screen *right, 229 struct screen *abs_centre, struct screen *list, struct screen *list_left, 230 struct screen *list_right, struct screen *after, int focus_start, 231 int focus_end, struct format_ranges *frs) 232 { 233 u_int width_left, width_centre, width_right; 234 u_int width_list, width_after, width_abs_centre; 235 struct screen_write_ctx ctx; 236 237 width_left = left->cx; 238 width_centre = centre->cx; 239 width_right = right->cx; 240 width_abs_centre = abs_centre->cx; 241 width_list = list->cx; 242 width_after = after->cx; 243 244 /* 245 * Trim first the centre, then the list, then the right, then after the 246 * list, then the left. 247 */ 248 while (width_left + 249 width_centre + 250 width_right + 251 width_list + 252 width_after > available) { 253 if (width_centre > 0) 254 width_centre--; 255 else if (width_list > 0) 256 width_list--; 257 else if (width_right > 0) 258 width_right--; 259 else if (width_after > 0) 260 width_after--; 261 else 262 width_left--; 263 } 264 265 /* If there is no list left, pass off to the no list function. */ 266 if (width_list == 0) { 267 screen_write_start(&ctx, left); 268 screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); 269 screen_write_stop(&ctx); 270 271 format_draw_none(octx, available, ocx, ocy, left, centre, 272 right, abs_centre, frs); 273 return; 274 } 275 276 /* Write left at 0. */ 277 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); 278 279 /* Write right at available - width_right. */ 280 format_draw_put(octx, ocx, ocy, right, frs, 281 available - width_right, 282 right->cx - width_right, 283 width_right); 284 285 /* Write after at width_left + width_list. */ 286 format_draw_put(octx, ocx, ocy, after, frs, 287 width_left + width_list, 288 0, 289 width_after); 290 291 /* 292 * Write centre halfway between 293 * width_left + width_list + width_after 294 * and 295 * available - width_right. 296 */ 297 format_draw_put(octx, ocx, ocy, centre, frs, 298 (width_left + width_list + width_after) 299 + ((available - width_right) 300 - (width_left + width_list + width_after)) / 2 301 - width_centre / 2, 302 centre->cx / 2 - width_centre / 2, 303 width_centre); 304 305 /* 306 * The list now goes from 307 * width_left 308 * to 309 * width_left + width_list. 310 * If there is no focus given, keep the left in focus. 311 */ 312 if (focus_start == -1 || focus_end == -1) 313 focus_start = focus_end = 0; 314 format_draw_put_list(octx, ocx, ocy, width_left, width_list, list, 315 list_left, list_right, focus_start, focus_end, frs); 316 317 /* 318 * Write abs_centre in the perfect centre of all horizontal space. 319 */ 320 if (width_abs_centre > available) 321 width_abs_centre = available; 322 format_draw_put(octx, ocx, ocy, abs_centre, frs, 323 (available - width_abs_centre) / 2, 324 0, 325 width_abs_centre); 326 } 327 328 /* Draw format with list in the centre. */ 329 static void 330 format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, 331 u_int ocy, struct screen *left, struct screen *centre, struct screen *right, 332 struct screen *abs_centre, struct screen *list, struct screen *list_left, 333 struct screen *list_right, struct screen *after, int focus_start, 334 int focus_end, struct format_ranges *frs) 335 { 336 u_int width_left, width_centre, width_right, middle; 337 u_int width_list, width_after, width_abs_centre; 338 struct screen_write_ctx ctx; 339 340 width_left = left->cx; 341 width_centre = centre->cx; 342 width_right = right->cx; 343 width_abs_centre = abs_centre->cx; 344 width_list = list->cx; 345 width_after = after->cx; 346 347 /* 348 * Trim first the list, then after the list, then the centre, then the 349 * right, then the left. 350 */ 351 while (width_left + 352 width_centre + 353 width_right + 354 width_list + 355 width_after > available) { 356 if (width_list > 0) 357 width_list--; 358 else if (width_after > 0) 359 width_after--; 360 else if (width_centre > 0) 361 width_centre--; 362 else if (width_right > 0) 363 width_right--; 364 else 365 width_left--; 366 } 367 368 /* If there is no list left, pass off to the no list function. */ 369 if (width_list == 0) { 370 screen_write_start(&ctx, centre); 371 screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); 372 screen_write_stop(&ctx); 373 374 format_draw_none(octx, available, ocx, ocy, left, centre, 375 right, abs_centre, frs); 376 return; 377 } 378 379 /* Write left at 0. */ 380 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); 381 382 /* Write right at available - width_right. */ 383 format_draw_put(octx, ocx, ocy, right, frs, 384 available - width_right, 385 right->cx - width_right, 386 width_right); 387 388 /* 389 * All three centre sections are offset from the middle of the 390 * available space. 391 */ 392 middle = (width_left + ((available - width_right) - width_left) / 2); 393 394 /* 395 * Write centre at 396 * middle - width_list / 2 - width_centre. 397 */ 398 format_draw_put(octx, ocx, ocy, centre, frs, 399 middle - width_list / 2 - width_centre, 400 0, 401 width_centre); 402 403 /* 404 * Write after at 405 * middle - width_list / 2 + width_list 406 */ 407 format_draw_put(octx, ocx, ocy, after, frs, 408 middle - width_list / 2 + width_list, 409 0, 410 width_after); 411 412 /* 413 * The list now goes from 414 * middle - width_list / 2 415 * to 416 * middle + width_list / 2 417 * If there is no focus given, keep the centre in focus. 418 */ 419 if (focus_start == -1 || focus_end == -1) 420 focus_start = focus_end = list->cx / 2; 421 format_draw_put_list(octx, ocx, ocy, middle - width_list / 2, 422 width_list, list, list_left, list_right, focus_start, focus_end, 423 frs); 424 425 /* 426 * Write abs_centre in the perfect centre of all horizontal space. 427 */ 428 if (width_abs_centre > available) 429 width_abs_centre = available; 430 format_draw_put(octx, ocx, ocy, abs_centre, frs, 431 (available - width_abs_centre) / 2, 432 0, 433 width_abs_centre); 434 } 435 436 /* Draw format with list on the right. */ 437 static void 438 format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, 439 u_int ocy, struct screen *left, struct screen *centre, struct screen *right, 440 struct screen *abs_centre, struct screen *list, 441 struct screen *list_left, struct screen *list_right, struct screen *after, 442 int focus_start, int focus_end, struct format_ranges *frs) 443 { 444 u_int width_left, width_centre, width_right; 445 u_int width_list, width_after, width_abs_centre; 446 struct screen_write_ctx ctx; 447 448 width_left = left->cx; 449 width_centre = centre->cx; 450 width_right = right->cx; 451 width_abs_centre = abs_centre->cx; 452 width_list = list->cx; 453 width_after = after->cx; 454 455 /* 456 * Trim first the centre, then the list, then the right, then 457 * after the list, then the left. 458 */ 459 while (width_left + 460 width_centre + 461 width_right + 462 width_list + 463 width_after > available) { 464 if (width_centre > 0) 465 width_centre--; 466 else if (width_list > 0) 467 width_list--; 468 else if (width_right > 0) 469 width_right--; 470 else if (width_after > 0) 471 width_after--; 472 else 473 width_left--; 474 } 475 476 /* If there is no list left, pass off to the no list function. */ 477 if (width_list == 0) { 478 screen_write_start(&ctx, right); 479 screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); 480 screen_write_stop(&ctx); 481 482 format_draw_none(octx, available, ocx, ocy, left, centre, 483 right, abs_centre, frs); 484 return; 485 } 486 487 /* Write left at 0. */ 488 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); 489 490 /* Write after at available - width_after. */ 491 format_draw_put(octx, ocx, ocy, after, frs, 492 available - width_after, 493 after->cx - width_after, 494 width_after); 495 496 /* 497 * Write right at 498 * available - width_right - width_list - width_after. 499 */ 500 format_draw_put(octx, ocx, ocy, right, frs, 501 available - width_right - width_list - width_after, 502 0, 503 width_right); 504 505 /* 506 * Write centre halfway between 507 * width_left 508 * and 509 * available - width_right - width_list - width_after. 510 */ 511 format_draw_put(octx, ocx, ocy, centre, frs, 512 width_left 513 + ((available - width_right - width_list - width_after) 514 - width_left) / 2 515 - width_centre / 2, 516 centre->cx / 2 - width_centre / 2, 517 width_centre); 518 519 /* 520 * The list now goes from 521 * available - width_list - width_after 522 * to 523 * available - width_after 524 * If there is no focus given, keep the right in focus. 525 */ 526 if (focus_start == -1 || focus_end == -1) 527 focus_start = focus_end = 0; 528 format_draw_put_list(octx, ocx, ocy, available - width_list - 529 width_after, width_list, list, list_left, list_right, focus_start, 530 focus_end, frs); 531 532 /* 533 * Write abs_centre in the perfect centre of all horizontal space. 534 */ 535 if (width_abs_centre > available) 536 width_abs_centre = available; 537 format_draw_put(octx, ocx, ocy, abs_centre, frs, 538 (available - width_abs_centre) / 2, 539 0, 540 width_abs_centre); 541 } 542 543 static void 544 format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available, 545 u_int ocx, u_int ocy, struct screen *left, struct screen *centre, 546 struct screen *right, struct screen *abs_centre, struct screen *list, 547 struct screen *list_left, struct screen *list_right, struct screen *after, 548 int focus_start, int focus_end, struct format_ranges *frs) 549 { 550 u_int width_left, width_centre, width_right, width_abs_centre; 551 u_int width_list, width_after, middle, abs_centre_offset; 552 553 width_left = left->cx; 554 width_centre = centre->cx; 555 width_right = right->cx; 556 width_abs_centre = abs_centre->cx; 557 width_list = list->cx; 558 width_after = after->cx; 559 560 /* 561 * Trim first centre, then the right, then the left. 562 */ 563 while (width_left + 564 width_centre + 565 width_right > available) { 566 if (width_centre > 0) 567 width_centre--; 568 else if (width_right > 0) 569 width_right--; 570 else 571 width_left--; 572 } 573 574 /* 575 * We trim list after and abs_centre independently, as we are drawing 576 * them over the rest. Trim first the list, then after the list, then 577 * abs_centre. 578 */ 579 while (width_list + width_after + width_abs_centre > available) { 580 if (width_list > 0) 581 width_list--; 582 else if (width_after > 0) 583 width_after--; 584 else 585 width_abs_centre--; 586 } 587 588 /* Write left at 0. */ 589 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); 590 591 /* Write right at available - width_right. */ 592 format_draw_put(octx, ocx, ocy, right, frs, 593 available - width_right, 594 right->cx - width_right, 595 width_right); 596 597 /* 598 * Keep writing centre at the relative centre. Only the list is written 599 * in the absolute centre of the horizontal space. 600 */ 601 middle = (width_left + ((available - width_right) - width_left) / 2); 602 603 /* 604 * Write centre at 605 * middle - width_centre. 606 */ 607 format_draw_put(octx, ocx, ocy, centre, frs, 608 middle - width_centre, 609 0, 610 width_centre); 611 612 /* 613 * If there is no focus given, keep the centre in focus. 614 */ 615 if (focus_start == -1 || focus_end == -1) 616 focus_start = focus_end = list->cx / 2; 617 618 /* 619 * We centre abs_centre and the list together, so their shared centre is 620 * in the perfect centre of horizontal space. 621 */ 622 abs_centre_offset = (available - width_list - width_abs_centre) / 2; 623 624 /* 625 * Write abs_centre before the list. 626 */ 627 format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset, 628 0, width_abs_centre); 629 abs_centre_offset += width_abs_centre; 630 631 /* 632 * Draw the list in the absolute centre 633 */ 634 format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list, 635 list, list_left, list_right, focus_start, focus_end, frs); 636 abs_centre_offset += width_list; 637 638 /* 639 * Write after at the end of the centre 640 */ 641 format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0, 642 width_after); 643 } 644 645 /* Get width and count of any leading #s. */ 646 static const char * 647 format_leading_hashes(const char *cp, u_int *n, u_int *width) 648 { 649 for (*n = 0; cp[*n] == '#'; (*n)++) 650 /* nothing */; 651 if (*n == 0) { 652 *width = 0; 653 return (cp); 654 } 655 if (cp[*n] != '[') { 656 if ((*n % 2) == 0) 657 *width = (*n / 2); 658 else 659 *width = (*n / 2) + 1; 660 return (cp + *n); 661 } 662 *width = (*n / 2); 663 if ((*n % 2) == 0) { 664 /* 665 * An even number of #s means that all #s are escaped, so not a 666 * style. The caller should not skip this. Return pointing to 667 * the [. 668 */ 669 return (cp + *n); 670 } 671 /* This is a style, so return pointing to the #. */ 672 return (cp + *n - 1); 673 } 674 675 /* Draw multiple characters. */ 676 static void 677 format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch, 678 u_int n) 679 { 680 u_int i; 681 682 utf8_set(&sy->gc.data, ch); 683 for (i = 0; i < n; i++) 684 screen_write_cell(ctx, &sy->gc); 685 } 686 687 /* Draw a format to a screen. */ 688 void 689 format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, 690 u_int available, const char *expanded, struct style_ranges *srs, 691 int default_colours) 692 { 693 enum { LEFT, 694 CENTRE, 695 RIGHT, 696 ABSOLUTE_CENTRE, 697 LIST, 698 LIST_LEFT, 699 LIST_RIGHT, 700 AFTER, 701 TOTAL } current = LEFT, last = LEFT; 702 const char *names[] = { "LEFT", 703 "CENTRE", 704 "RIGHT", 705 "ABSOLUTE_CENTRE", 706 "LIST", 707 "LIST_LEFT", 708 "LIST_RIGHT", 709 "AFTER" }; 710 size_t size = strlen(expanded); 711 struct screen *os = octx->s, s[TOTAL]; 712 struct screen_write_ctx ctx[TOTAL]; 713 u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL]; 714 u_int map[] = { LEFT, 715 LEFT, 716 CENTRE, 717 RIGHT, 718 ABSOLUTE_CENTRE }; 719 int focus_start = -1, focus_end = -1; 720 int list_state = -1, fill = -1, even; 721 enum style_align list_align = STYLE_ALIGN_DEFAULT; 722 struct grid_cell gc, current_default, base_default; 723 struct style sy, saved_sy; 724 struct utf8_data *ud = &sy.gc.data; 725 const char *cp, *end; 726 enum utf8_state more; 727 char *tmp; 728 struct format_range *fr = NULL, *fr1; 729 struct format_ranges frs; 730 struct style_range *sr; 731 732 memcpy(&base_default, base, sizeof base_default); 733 memcpy(¤t_default, base, sizeof current_default); 734 base = &base_default; 735 style_set(&sy, ¤t_default); 736 TAILQ_INIT(&frs); 737 log_debug("%s: %s", __func__, expanded); 738 739 /* 740 * We build three screens for left, right, centre alignment, one for 741 * the list, one for anything after the list and two for the list left 742 * and right markers. 743 */ 744 for (i = 0; i < TOTAL; i++) { 745 screen_init(&s[i], size, 1, 0); 746 screen_write_start(&ctx[i], &s[i]); 747 screen_write_clearendofline(&ctx[i], current_default.bg); 748 width[i] = 0; 749 } 750 751 /* 752 * Walk the string and add to the corresponding screens, 753 * parsing styles as we go. 754 */ 755 cp = expanded; 756 while (*cp != '\0') { 757 /* Handle sequences of #. */ 758 if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') { 759 for (n = 1; cp[n] == '#'; n++) 760 /* nothing */; 761 even = ((n % 2) == 0); 762 if (cp[n] != '[') { 763 cp += n; 764 if (even) 765 n = (n / 2); 766 else 767 n = (n / 2) + 1; 768 width[current] += n; 769 format_draw_many(&ctx[current], &sy, '#', n); 770 continue; 771 } 772 if (even) 773 cp += (n + 1); 774 else 775 cp += (n - 1); 776 if (sy.ignore) 777 continue; 778 format_draw_many(&ctx[current], &sy, '#', n / 2); 779 width[current] += (n / 2); 780 if (even) { 781 utf8_set(ud, '['); 782 screen_write_cell(&ctx[current], &sy.gc); 783 width[current]++; 784 } 785 continue; 786 } 787 788 /* Is this not a style? */ 789 if (cp[0] != '#' || cp[1] != '[' || sy.ignore) { 790 /* See if this is a UTF-8 character. */ 791 if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { 792 while (*++cp != '\0' && more == UTF8_MORE) 793 more = utf8_append(ud, *cp); 794 if (more != UTF8_DONE) 795 cp -= ud->have; 796 } 797 798 /* Not a UTF-8 character - ASCII or not valid. */ 799 if (more != UTF8_DONE) { 800 if (*cp < 0x20 || *cp > 0x7e) { 801 /* Ignore nonprintable characters. */ 802 cp++; 803 continue; 804 } 805 utf8_set(ud, *cp); 806 cp++; 807 } 808 809 /* Draw the cell to the current screen. */ 810 screen_write_cell(&ctx[current], &sy.gc); 811 width[current] += ud->width; 812 continue; 813 } 814 815 /* This is a style. Work out where the end is and parse it. */ 816 end = format_skip(cp + 2, "]"); 817 if (end == NULL) { 818 log_debug("%s: no terminating ] at '%s'", __func__, 819 cp + 2); 820 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) 821 format_free_range(&frs, fr); 822 goto out; 823 } 824 tmp = xstrndup(cp + 2, end - (cp + 2)); 825 style_copy(&saved_sy, &sy); 826 if (style_parse(&sy, ¤t_default, tmp) != 0) { 827 log_debug("%s: invalid style '%s'", __func__, tmp); 828 free(tmp); 829 cp = end + 1; 830 continue; 831 } 832 log_debug("%s: style '%s' -> '%s'", __func__, tmp, 833 style_tostring(&sy)); 834 free(tmp); 835 if (default_colours) { 836 sy.gc.bg = base->bg; 837 sy.gc.fg = base->fg; 838 } 839 840 /* If this style has a fill colour, store it for later. */ 841 if (sy.fill != 8) 842 fill = sy.fill; 843 844 /* If this style pushed or popped the default, update it. */ 845 if (sy.default_type == STYLE_DEFAULT_PUSH) { 846 memcpy(¤t_default, &saved_sy.gc, 847 sizeof current_default); 848 sy.default_type = STYLE_DEFAULT_BASE; 849 } else if (sy.default_type == STYLE_DEFAULT_POP) { 850 memcpy(¤t_default, base, sizeof current_default); 851 sy.default_type = STYLE_DEFAULT_BASE; 852 } else if (sy.default_type == STYLE_DEFAULT_SET) { 853 memcpy(&base_default, &saved_sy.gc, 854 sizeof base_default); 855 memcpy(¤t_default, &saved_sy.gc, 856 sizeof current_default); 857 sy.default_type = STYLE_DEFAULT_BASE; 858 } 859 860 /* Check the list state. */ 861 switch (sy.list) { 862 case STYLE_LIST_ON: 863 /* 864 * Entering the list, exiting a marker, or exiting the 865 * focus. 866 */ 867 if (list_state != 0) { 868 if (fr != NULL) { /* abort any region */ 869 free(fr); 870 fr = NULL; 871 } 872 list_state = 0; 873 list_align = sy.align; 874 } 875 876 /* End the focus if started. */ 877 if (focus_start != -1 && focus_end == -1) 878 focus_end = s[LIST].cx; 879 880 current = LIST; 881 break; 882 case STYLE_LIST_FOCUS: 883 /* Entering the focus. */ 884 if (list_state != 0) /* not inside the list */ 885 break; 886 if (focus_start == -1) /* focus already started */ 887 focus_start = s[LIST].cx; 888 break; 889 case STYLE_LIST_OFF: 890 /* Exiting or outside the list. */ 891 if (list_state == 0) { 892 if (fr != NULL) { /* abort any region */ 893 free(fr); 894 fr = NULL; 895 } 896 if (focus_start != -1 && focus_end == -1) 897 focus_end = s[LIST].cx; 898 899 map[list_align] = AFTER; 900 if (list_align == STYLE_ALIGN_LEFT) 901 map[STYLE_ALIGN_DEFAULT] = AFTER; 902 list_state = 1; 903 } 904 current = map[sy.align]; 905 break; 906 case STYLE_LIST_LEFT_MARKER: 907 /* Entering left marker. */ 908 if (list_state != 0) /* not inside the list */ 909 break; 910 if (s[LIST_LEFT].cx != 0) /* already have marker */ 911 break; 912 if (fr != NULL) { /* abort any region */ 913 free(fr); 914 fr = NULL; 915 } 916 if (focus_start != -1 && focus_end == -1) 917 focus_start = focus_end = -1; 918 current = LIST_LEFT; 919 break; 920 case STYLE_LIST_RIGHT_MARKER: 921 /* Entering right marker. */ 922 if (list_state != 0) /* not inside the list */ 923 break; 924 if (s[LIST_RIGHT].cx != 0) /* already have marker */ 925 break; 926 if (fr != NULL) { /* abort any region */ 927 free(fr); 928 fr = NULL; 929 } 930 if (focus_start != -1 && focus_end == -1) 931 focus_start = focus_end = -1; 932 current = LIST_RIGHT; 933 break; 934 } 935 if (current != last) { 936 log_debug("%s: change %s -> %s", __func__, 937 names[last], names[current]); 938 last = current; 939 } 940 941 /* 942 * Check if the range style has changed and if so end the 943 * current range and start a new one if needed. 944 */ 945 if (srs != NULL) { 946 if (fr != NULL && !format_is_type(fr, &sy)) { 947 if (s[current].cx != fr->start) { 948 fr->end = s[current].cx + 1; 949 TAILQ_INSERT_TAIL(&frs, fr, entry); 950 } else 951 free(fr); 952 fr = NULL; 953 } 954 if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) { 955 fr = xcalloc(1, sizeof *fr); 956 fr->index = current; 957 958 fr->s = &s[current]; 959 fr->start = s[current].cx; 960 961 fr->type = sy.range_type; 962 fr->argument = sy.range_argument; 963 strlcpy(fr->string, sy.range_string, 964 sizeof fr->string); 965 } 966 } 967 968 cp = end + 1; 969 } 970 free(fr); 971 972 for (i = 0; i < TOTAL; i++) { 973 screen_write_stop(&ctx[i]); 974 log_debug("%s: width %s is %u", __func__, names[i], width[i]); 975 } 976 if (focus_start != -1 && focus_end != -1) 977 log_debug("%s: focus %d-%d", __func__, focus_start, focus_end); 978 TAILQ_FOREACH(fr, &frs, entry) { 979 log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type, 980 fr->argument, names[fr->index], fr->start, fr->end); 981 } 982 983 /* Clear the available area. */ 984 if (fill != -1) { 985 memcpy(&gc, &grid_default_cell, sizeof gc); 986 gc.bg = fill; 987 for (i = 0; i < available; i++) 988 screen_write_putc(octx, &gc, ' '); 989 } 990 991 /* 992 * Draw the screens. How they are arranged depends on where the list 993 * appears. 994 */ 995 switch (list_align) { 996 case STYLE_ALIGN_DEFAULT: 997 /* No list. */ 998 format_draw_none(octx, available, ocx, ocy, &s[LEFT], 999 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs); 1000 break; 1001 case STYLE_ALIGN_LEFT: 1002 /* List is part of the left. */ 1003 format_draw_left(octx, available, ocx, ocy, &s[LEFT], 1004 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 1005 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 1006 focus_start, focus_end, &frs); 1007 break; 1008 case STYLE_ALIGN_CENTRE: 1009 /* List is part of the centre. */ 1010 format_draw_centre(octx, available, ocx, ocy, &s[LEFT], 1011 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 1012 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 1013 focus_start, focus_end, &frs); 1014 break; 1015 case STYLE_ALIGN_RIGHT: 1016 /* List is part of the right. */ 1017 format_draw_right(octx, available, ocx, ocy, &s[LEFT], 1018 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 1019 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 1020 focus_start, focus_end, &frs); 1021 break; 1022 case STYLE_ALIGN_ABSOLUTE_CENTRE: 1023 /* List is in the centre of the entire horizontal space. */ 1024 format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT], 1025 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 1026 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 1027 focus_start, focus_end, &frs); 1028 break; 1029 } 1030 1031 /* Create ranges to return. */ 1032 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) { 1033 sr = xcalloc(1, sizeof *sr); 1034 sr->type = fr->type; 1035 sr->argument = fr->argument; 1036 strlcpy(sr->string, fr->string, sizeof sr->string); 1037 sr->start = fr->start; 1038 sr->end = fr->end; 1039 TAILQ_INSERT_TAIL(srs, sr, entry); 1040 1041 switch (sr->type) { 1042 case STYLE_RANGE_NONE: 1043 break; 1044 case STYLE_RANGE_LEFT: 1045 log_debug("%s: range left at %u-%u", __func__, 1046 sr->start, sr->end); 1047 break; 1048 case STYLE_RANGE_RIGHT: 1049 log_debug("%s: range right at %u-%u", __func__, 1050 sr->start, sr->end); 1051 break; 1052 case STYLE_RANGE_PANE: 1053 log_debug("%s: range pane|%%%u at %u-%u", __func__, 1054 sr->argument, sr->start, sr->end); 1055 break; 1056 case STYLE_RANGE_WINDOW: 1057 log_debug("%s: range window|%u at %u-%u", __func__, 1058 sr->argument, sr->start, sr->end); 1059 break; 1060 case STYLE_RANGE_SESSION: 1061 log_debug("%s: range session|$%u at %u-%u", __func__, 1062 sr->argument, sr->start, sr->end); 1063 break; 1064 case STYLE_RANGE_USER: 1065 log_debug("%s: range user|%u at %u-%u", __func__, 1066 sr->argument, sr->start, sr->end); 1067 break; 1068 } 1069 format_free_range(&frs, fr); 1070 } 1071 1072 out: 1073 /* Free the screens. */ 1074 for (i = 0; i < TOTAL; i++) 1075 screen_free(&s[i]); 1076 1077 /* Restore the original cursor position. */ 1078 screen_write_cursormove(octx, ocx, ocy, 0); 1079 } 1080 1081 /* Get width, taking #[] into account. */ 1082 u_int 1083 format_width(const char *expanded) 1084 { 1085 const char *cp, *end; 1086 u_int n, leading_width, width = 0; 1087 struct utf8_data ud; 1088 enum utf8_state more; 1089 1090 cp = expanded; 1091 while (*cp != '\0') { 1092 if (*cp == '#') { 1093 end = format_leading_hashes(cp, &n, &leading_width); 1094 width += leading_width; 1095 cp = end; 1096 if (*cp == '#') { 1097 end = format_skip(cp + 2, "]"); 1098 if (end == NULL) 1099 return (0); 1100 cp = end + 1; 1101 } 1102 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 1103 while (*++cp != '\0' && more == UTF8_MORE) 1104 more = utf8_append(&ud, *cp); 1105 if (more == UTF8_DONE) 1106 width += ud.width; 1107 } else if (*cp > 0x1f && *cp < 0x7f) { 1108 width++; 1109 cp++; 1110 } else 1111 cp++; 1112 } 1113 return (width); 1114 } 1115 1116 /* 1117 * Trim on the left, taking #[] into account. Note, we copy the whole set of 1118 * unescaped #s, but only add their escaped size to width. This is because the 1119 * format_draw function will actually do the escaping when it runs 1120 */ 1121 char * 1122 format_trim_left(const char *expanded, u_int limit) 1123 { 1124 char *copy, *out; 1125 const char *cp = expanded, *end; 1126 u_int n, width = 0, leading_width; 1127 struct utf8_data ud; 1128 enum utf8_state more; 1129 1130 out = copy = xcalloc(2, strlen(expanded) + 1); 1131 while (*cp != '\0') { 1132 if (width >= limit) 1133 break; 1134 if (*cp == '#') { 1135 end = format_leading_hashes(cp, &n, &leading_width); 1136 if (leading_width > limit - width) 1137 leading_width = limit - width; 1138 if (leading_width != 0) { 1139 if (n == 1) 1140 *out++ = '#'; 1141 else { 1142 memset(out, '#', 2 * leading_width); 1143 out += 2 * leading_width; 1144 } 1145 width += leading_width; 1146 } 1147 cp = end; 1148 if (*cp == '#') { 1149 end = format_skip(cp + 2, "]"); 1150 if (end == NULL) 1151 break; 1152 memcpy(out, cp, end + 1 - cp); 1153 out += (end + 1 - cp); 1154 cp = end + 1; 1155 } 1156 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 1157 while (*++cp != '\0' && more == UTF8_MORE) 1158 more = utf8_append(&ud, *cp); 1159 if (more == UTF8_DONE) { 1160 if (width + ud.width <= limit) { 1161 memcpy(out, ud.data, ud.size); 1162 out += ud.size; 1163 } 1164 width += ud.width; 1165 } else { 1166 cp -= ud.have; 1167 cp++; 1168 } 1169 } else if (*cp > 0x1f && *cp < 0x7f) { 1170 if (width + 1 <= limit) 1171 *out++ = *cp; 1172 width++; 1173 cp++; 1174 } else 1175 cp++; 1176 } 1177 *out = '\0'; 1178 return (copy); 1179 } 1180 1181 /* Trim on the right, taking #[] into account. */ 1182 char * 1183 format_trim_right(const char *expanded, u_int limit) 1184 { 1185 char *copy, *out; 1186 const char *cp = expanded, *end; 1187 u_int width = 0, total_width, skip, n; 1188 u_int leading_width, copy_width; 1189 struct utf8_data ud; 1190 enum utf8_state more; 1191 1192 total_width = format_width(expanded); 1193 if (total_width <= limit) 1194 return (xstrdup(expanded)); 1195 skip = total_width - limit; 1196 1197 out = copy = xcalloc(2, strlen(expanded) + 1); 1198 while (*cp != '\0') { 1199 if (*cp == '#') { 1200 end = format_leading_hashes(cp, &n, &leading_width); 1201 copy_width = leading_width; 1202 if (width <= skip) { 1203 if (skip - width >= copy_width) 1204 copy_width = 0; 1205 else 1206 copy_width -= (skip - width); 1207 } 1208 if (copy_width != 0) { 1209 if (n == 1) 1210 *out++ = '#'; 1211 else { 1212 memset(out, '#', 2 * copy_width); 1213 out += 2 * copy_width; 1214 } 1215 } 1216 width += leading_width; 1217 cp = end; 1218 if (*cp == '#') { 1219 end = format_skip(cp + 2, "]"); 1220 if (end == NULL) 1221 break; 1222 memcpy(out, cp, end + 1 - cp); 1223 out += (end + 1 - cp); 1224 cp = end + 1; 1225 } 1226 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 1227 while (*++cp != '\0' && more == UTF8_MORE) 1228 more = utf8_append(&ud, *cp); 1229 if (more == UTF8_DONE) { 1230 if (width >= skip) { 1231 memcpy(out, ud.data, ud.size); 1232 out += ud.size; 1233 } 1234 width += ud.width; 1235 } else { 1236 cp -= ud.have; 1237 cp++; 1238 } 1239 } else if (*cp > 0x1f && *cp < 0x7f) { 1240 if (width >= skip) 1241 *out++ = *cp; 1242 width++; 1243 cp++; 1244 } else 1245 cp++; 1246 } 1247 *out = '\0'; 1248 return (copy); 1249 } 1250