1 /* $OpenBSD: format-draw.c,v 1.28 2023/08/17 14:10:28 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@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; 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(¤t_default, base, sizeof current_default); 733 style_set(&sy, ¤t_default); 734 TAILQ_INIT(&frs); 735 log_debug("%s: %s", __func__, expanded); 736 737 /* 738 * We build three screens for left, right, centre alignment, one for 739 * the list, one for anything after the list and two for the list left 740 * and right markers. 741 */ 742 for (i = 0; i < TOTAL; i++) { 743 screen_init(&s[i], size, 1, 0); 744 screen_write_start(&ctx[i], &s[i]); 745 screen_write_clearendofline(&ctx[i], current_default.bg); 746 width[i] = 0; 747 } 748 749 /* 750 * Walk the string and add to the corresponding screens, 751 * parsing styles as we go. 752 */ 753 cp = expanded; 754 while (*cp != '\0') { 755 /* Handle sequences of #. */ 756 if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') { 757 for (n = 1; cp[n] == '#'; n++) 758 /* nothing */; 759 even = ((n % 2) == 0); 760 if (cp[n] != '[') { 761 cp += n; 762 if (even) 763 n = (n / 2); 764 else 765 n = (n / 2) + 1; 766 width[current] += n; 767 format_draw_many(&ctx[current], &sy, '#', n); 768 continue; 769 } 770 if (even) 771 cp += (n + 1); 772 else 773 cp += (n - 1); 774 if (sy.ignore) 775 continue; 776 format_draw_many(&ctx[current], &sy, '#', n / 2); 777 width[current] += (n / 2); 778 if (even) { 779 utf8_set(ud, '['); 780 screen_write_cell(&ctx[current], &sy.gc); 781 width[current]++; 782 } 783 continue; 784 } 785 786 /* Is this not a style? */ 787 if (cp[0] != '#' || cp[1] != '[' || sy.ignore) { 788 /* See if this is a UTF-8 character. */ 789 if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { 790 while (*++cp != '\0' && more == UTF8_MORE) 791 more = utf8_append(ud, *cp); 792 if (more != UTF8_DONE) 793 cp -= ud->have; 794 } 795 796 /* Not a UTF-8 character - ASCII or not valid. */ 797 if (more != UTF8_DONE) { 798 if (*cp < 0x20 || *cp > 0x7e) { 799 /* Ignore nonprintable characters. */ 800 cp++; 801 continue; 802 } 803 utf8_set(ud, *cp); 804 cp++; 805 } 806 807 /* Draw the cell to the current screen. */ 808 screen_write_cell(&ctx[current], &sy.gc); 809 width[current] += ud->width; 810 continue; 811 } 812 813 /* This is a style. Work out where the end is and parse it. */ 814 end = format_skip(cp + 2, "]"); 815 if (end == NULL) { 816 log_debug("%s: no terminating ] at '%s'", __func__, 817 cp + 2); 818 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) 819 format_free_range(&frs, fr); 820 goto out; 821 } 822 tmp = xstrndup(cp + 2, end - (cp + 2)); 823 style_copy(&saved_sy, &sy); 824 if (style_parse(&sy, ¤t_default, tmp) != 0) { 825 log_debug("%s: invalid style '%s'", __func__, tmp); 826 free(tmp); 827 cp = end + 1; 828 continue; 829 } 830 log_debug("%s: style '%s' -> '%s'", __func__, tmp, 831 style_tostring(&sy)); 832 free(tmp); 833 if (default_colours) { 834 sy.gc.bg = base->bg; 835 sy.gc.fg = base->fg; 836 } 837 838 /* If this style has a fill colour, store it for later. */ 839 if (sy.fill != 8) 840 fill = sy.fill; 841 842 /* If this style pushed or popped the default, update it. */ 843 if (sy.default_type == STYLE_DEFAULT_PUSH) { 844 memcpy(¤t_default, &saved_sy.gc, 845 sizeof current_default); 846 sy.default_type = STYLE_DEFAULT_BASE; 847 } else if (sy.default_type == STYLE_DEFAULT_POP) { 848 memcpy(¤t_default, base, sizeof current_default); 849 sy.default_type = STYLE_DEFAULT_BASE; 850 } 851 852 /* Check the list state. */ 853 switch (sy.list) { 854 case STYLE_LIST_ON: 855 /* 856 * Entering the list, exiting a marker, or exiting the 857 * focus. 858 */ 859 if (list_state != 0) { 860 if (fr != NULL) { /* abort any region */ 861 free(fr); 862 fr = NULL; 863 } 864 list_state = 0; 865 list_align = sy.align; 866 } 867 868 /* End the focus if started. */ 869 if (focus_start != -1 && focus_end == -1) 870 focus_end = s[LIST].cx; 871 872 current = LIST; 873 break; 874 case STYLE_LIST_FOCUS: 875 /* Entering the focus. */ 876 if (list_state != 0) /* not inside the list */ 877 break; 878 if (focus_start == -1) /* focus already started */ 879 focus_start = s[LIST].cx; 880 break; 881 case STYLE_LIST_OFF: 882 /* Exiting or outside the list. */ 883 if (list_state == 0) { 884 if (fr != NULL) { /* abort any region */ 885 free(fr); 886 fr = NULL; 887 } 888 if (focus_start != -1 && focus_end == -1) 889 focus_end = s[LIST].cx; 890 891 map[list_align] = AFTER; 892 if (list_align == STYLE_ALIGN_LEFT) 893 map[STYLE_ALIGN_DEFAULT] = AFTER; 894 list_state = 1; 895 } 896 current = map[sy.align]; 897 break; 898 case STYLE_LIST_LEFT_MARKER: 899 /* Entering left marker. */ 900 if (list_state != 0) /* not inside the list */ 901 break; 902 if (s[LIST_LEFT].cx != 0) /* already have marker */ 903 break; 904 if (fr != NULL) { /* abort any region */ 905 free(fr); 906 fr = NULL; 907 } 908 if (focus_start != -1 && focus_end == -1) 909 focus_start = focus_end = -1; 910 current = LIST_LEFT; 911 break; 912 case STYLE_LIST_RIGHT_MARKER: 913 /* Entering right marker. */ 914 if (list_state != 0) /* not inside the list */ 915 break; 916 if (s[LIST_RIGHT].cx != 0) /* already have marker */ 917 break; 918 if (fr != NULL) { /* abort any region */ 919 free(fr); 920 fr = NULL; 921 } 922 if (focus_start != -1 && focus_end == -1) 923 focus_start = focus_end = -1; 924 current = LIST_RIGHT; 925 break; 926 } 927 if (current != last) { 928 log_debug("%s: change %s -> %s", __func__, 929 names[last], names[current]); 930 last = current; 931 } 932 933 /* 934 * Check if the range style has changed and if so end the 935 * current range and start a new one if needed. 936 */ 937 if (srs != NULL) { 938 if (fr != NULL && !format_is_type(fr, &sy)) { 939 if (s[current].cx != fr->start) { 940 fr->end = s[current].cx + 1; 941 TAILQ_INSERT_TAIL(&frs, fr, entry); 942 } else 943 free(fr); 944 fr = NULL; 945 } 946 if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) { 947 fr = xcalloc(1, sizeof *fr); 948 fr->index = current; 949 950 fr->s = &s[current]; 951 fr->start = s[current].cx; 952 953 fr->type = sy.range_type; 954 fr->argument = sy.range_argument; 955 strlcpy(fr->string, sy.range_string, 956 sizeof fr->string); 957 } 958 } 959 960 cp = end + 1; 961 } 962 free(fr); 963 964 for (i = 0; i < TOTAL; i++) { 965 screen_write_stop(&ctx[i]); 966 log_debug("%s: width %s is %u", __func__, names[i], width[i]); 967 } 968 if (focus_start != -1 && focus_end != -1) 969 log_debug("%s: focus %d-%d", __func__, focus_start, focus_end); 970 TAILQ_FOREACH(fr, &frs, entry) { 971 log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type, 972 fr->argument, names[fr->index], fr->start, fr->end); 973 } 974 975 /* Clear the available area. */ 976 if (fill != -1) { 977 memcpy(&gc, &grid_default_cell, sizeof gc); 978 gc.bg = fill; 979 for (i = 0; i < available; i++) 980 screen_write_putc(octx, &gc, ' '); 981 } 982 983 /* 984 * Draw the screens. How they are arranged depends on where the list 985 * appears. 986 */ 987 switch (list_align) { 988 case STYLE_ALIGN_DEFAULT: 989 /* No list. */ 990 format_draw_none(octx, available, ocx, ocy, &s[LEFT], 991 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs); 992 break; 993 case STYLE_ALIGN_LEFT: 994 /* List is part of the left. */ 995 format_draw_left(octx, available, ocx, ocy, &s[LEFT], 996 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 997 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 998 focus_start, focus_end, &frs); 999 break; 1000 case STYLE_ALIGN_CENTRE: 1001 /* List is part of the centre. */ 1002 format_draw_centre(octx, available, ocx, ocy, &s[LEFT], 1003 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 1004 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 1005 focus_start, focus_end, &frs); 1006 break; 1007 case STYLE_ALIGN_RIGHT: 1008 /* List is part of the right. */ 1009 format_draw_right(octx, available, ocx, ocy, &s[LEFT], 1010 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 1011 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 1012 focus_start, focus_end, &frs); 1013 break; 1014 case STYLE_ALIGN_ABSOLUTE_CENTRE: 1015 /* List is in the centre of the entire horizontal space. */ 1016 format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT], 1017 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST], 1018 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], 1019 focus_start, focus_end, &frs); 1020 break; 1021 } 1022 1023 /* Create ranges to return. */ 1024 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) { 1025 sr = xcalloc(1, sizeof *sr); 1026 sr->type = fr->type; 1027 sr->argument = fr->argument; 1028 strlcpy(sr->string, fr->string, sizeof sr->string); 1029 sr->start = fr->start; 1030 sr->end = fr->end; 1031 TAILQ_INSERT_TAIL(srs, sr, entry); 1032 1033 switch (sr->type) { 1034 case STYLE_RANGE_NONE: 1035 break; 1036 case STYLE_RANGE_LEFT: 1037 log_debug("%s: range left at %u-%u", __func__, 1038 sr->start, sr->end); 1039 break; 1040 case STYLE_RANGE_RIGHT: 1041 log_debug("%s: range right at %u-%u", __func__, 1042 sr->start, sr->end); 1043 break; 1044 case STYLE_RANGE_PANE: 1045 log_debug("%s: range pane|%%%u at %u-%u", __func__, 1046 sr->argument, sr->start, sr->end); 1047 break; 1048 case STYLE_RANGE_WINDOW: 1049 log_debug("%s: range window|%u at %u-%u", __func__, 1050 sr->argument, sr->start, sr->end); 1051 break; 1052 case STYLE_RANGE_SESSION: 1053 log_debug("%s: range session|$%u at %u-%u", __func__, 1054 sr->argument, sr->start, sr->end); 1055 break; 1056 case STYLE_RANGE_USER: 1057 log_debug("%s: range user|%u at %u-%u", __func__, 1058 sr->argument, sr->start, sr->end); 1059 break; 1060 } 1061 format_free_range(&frs, fr); 1062 } 1063 1064 out: 1065 /* Free the screens. */ 1066 for (i = 0; i < TOTAL; i++) 1067 screen_free(&s[i]); 1068 1069 /* Restore the original cursor position. */ 1070 screen_write_cursormove(octx, ocx, ocy, 0); 1071 } 1072 1073 /* Get width, taking #[] into account. */ 1074 u_int 1075 format_width(const char *expanded) 1076 { 1077 const char *cp, *end; 1078 u_int n, leading_width, width = 0; 1079 struct utf8_data ud; 1080 enum utf8_state more; 1081 1082 cp = expanded; 1083 while (*cp != '\0') { 1084 if (*cp == '#') { 1085 end = format_leading_hashes(cp, &n, &leading_width); 1086 width += leading_width; 1087 cp = end; 1088 if (*cp == '#') { 1089 end = format_skip(cp + 2, "]"); 1090 if (end == NULL) 1091 return (0); 1092 cp = end + 1; 1093 } 1094 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 1095 while (*++cp != '\0' && more == UTF8_MORE) 1096 more = utf8_append(&ud, *cp); 1097 if (more == UTF8_DONE) 1098 width += ud.width; 1099 else 1100 cp -= ud.have; 1101 } else if (*cp > 0x1f && *cp < 0x7f) { 1102 width++; 1103 cp++; 1104 } else 1105 cp++; 1106 } 1107 return (width); 1108 } 1109 1110 /* 1111 * Trim on the left, taking #[] into account. Note, we copy the whole set of 1112 * unescaped #s, but only add their escaped size to width. This is because the 1113 * format_draw function will actually do the escaping when it runs 1114 */ 1115 char * 1116 format_trim_left(const char *expanded, u_int limit) 1117 { 1118 char *copy, *out; 1119 const char *cp = expanded, *end; 1120 u_int n, width = 0, leading_width; 1121 struct utf8_data ud; 1122 enum utf8_state more; 1123 1124 out = copy = xcalloc(2, strlen(expanded) + 1); 1125 while (*cp != '\0') { 1126 if (width >= limit) 1127 break; 1128 if (*cp == '#') { 1129 end = format_leading_hashes(cp, &n, &leading_width); 1130 if (leading_width > limit - width) 1131 leading_width = limit - width; 1132 if (leading_width != 0) { 1133 if (n == 1) 1134 *out++ = '#'; 1135 else { 1136 memset(out, '#', 2 * leading_width); 1137 out += 2 * leading_width; 1138 } 1139 width += leading_width; 1140 } 1141 cp = end; 1142 if (*cp == '#') { 1143 end = format_skip(cp + 2, "]"); 1144 if (end == NULL) 1145 break; 1146 memcpy(out, cp, end + 1 - cp); 1147 out += (end + 1 - cp); 1148 cp = end + 1; 1149 } 1150 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 1151 while (*++cp != '\0' && more == UTF8_MORE) 1152 more = utf8_append(&ud, *cp); 1153 if (more == UTF8_DONE) { 1154 if (width + ud.width <= limit) { 1155 memcpy(out, ud.data, ud.size); 1156 out += ud.size; 1157 } 1158 width += ud.width; 1159 } else { 1160 cp -= ud.have; 1161 cp++; 1162 } 1163 } else if (*cp > 0x1f && *cp < 0x7f) { 1164 if (width + 1 <= limit) 1165 *out++ = *cp; 1166 width++; 1167 cp++; 1168 } else 1169 cp++; 1170 } 1171 *out = '\0'; 1172 return (copy); 1173 } 1174 1175 /* Trim on the right, taking #[] into account. */ 1176 char * 1177 format_trim_right(const char *expanded, u_int limit) 1178 { 1179 char *copy, *out; 1180 const char *cp = expanded, *end; 1181 u_int width = 0, total_width, skip, n; 1182 u_int leading_width, copy_width; 1183 struct utf8_data ud; 1184 enum utf8_state more; 1185 1186 total_width = format_width(expanded); 1187 if (total_width <= limit) 1188 return (xstrdup(expanded)); 1189 skip = total_width - limit; 1190 1191 out = copy = xcalloc(2, strlen(expanded) + 1); 1192 while (*cp != '\0') { 1193 if (*cp == '#') { 1194 end = format_leading_hashes(cp, &n, &leading_width); 1195 copy_width = leading_width; 1196 if (width <= skip) { 1197 if (skip - width >= copy_width) 1198 copy_width = 0; 1199 else 1200 copy_width -= (skip - width); 1201 } 1202 if (copy_width != 0) { 1203 if (n == 1) 1204 *out++ = '#'; 1205 else { 1206 memset(out, '#', 2 * copy_width); 1207 out += 2 * copy_width; 1208 } 1209 } 1210 width += leading_width; 1211 cp = end; 1212 if (*cp == '#') { 1213 end = format_skip(cp + 2, "]"); 1214 if (end == NULL) 1215 break; 1216 memcpy(out, cp, end + 1 - cp); 1217 out += (end + 1 - cp); 1218 cp = end + 1; 1219 } 1220 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 1221 while (*++cp != '\0' && more == UTF8_MORE) 1222 more = utf8_append(&ud, *cp); 1223 if (more == UTF8_DONE) { 1224 if (width >= skip) { 1225 memcpy(out, ud.data, ud.size); 1226 out += ud.size; 1227 } 1228 width += ud.width; 1229 } else { 1230 cp -= ud.have; 1231 cp++; 1232 } 1233 } else if (*cp > 0x1f && *cp < 0x7f) { 1234 if (width >= skip) 1235 *out++ = *cp; 1236 width++; 1237 cp++; 1238 } else 1239 cp++; 1240 } 1241 *out = '\0'; 1242 return (copy); 1243 } 1244