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