1 /* $OpenBSD: format-draw.c,v 1.20 2020/05/16 16:26:34 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 a format to a screen. */ 490 void 491 format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, 492 u_int available, const char *expanded, struct style_ranges *srs) 493 { 494 enum { LEFT, 495 CENTRE, 496 RIGHT, 497 LIST, 498 LIST_LEFT, 499 LIST_RIGHT, 500 AFTER, 501 TOTAL } current = LEFT, last = LEFT; 502 const char *names[] = { "LEFT", 503 "CENTRE", 504 "RIGHT", 505 "LIST", 506 "LIST_LEFT", 507 "LIST_RIGHT", 508 "AFTER" }; 509 size_t size = strlen(expanded); 510 struct screen *os = octx->s, s[TOTAL]; 511 struct screen_write_ctx ctx[TOTAL]; 512 u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL]; 513 u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; 514 int focus_start = -1, focus_end = -1; 515 int list_state = -1, fill = -1; 516 enum style_align list_align = STYLE_ALIGN_DEFAULT; 517 struct grid_cell gc, current_default; 518 struct style sy, saved_sy; 519 struct utf8_data *ud = &sy.gc.data; 520 const char *cp, *end; 521 enum utf8_state more; 522 char *tmp; 523 struct format_range *fr = NULL, *fr1; 524 struct format_ranges frs; 525 struct style_range *sr; 526 527 memcpy(¤t_default, base, sizeof current_default); 528 style_set(&sy, ¤t_default); 529 TAILQ_INIT(&frs); 530 log_debug("%s: %s", __func__, expanded); 531 532 /* 533 * We build three screens for left, right, centre alignment, one for 534 * the list, one for anything after the list and two for the list left 535 * and right markers. 536 */ 537 for (i = 0; i < TOTAL; i++) { 538 screen_init(&s[i], size, 1, 0); 539 screen_write_start(&ctx[i], &s[i]); 540 screen_write_clearendofline(&ctx[i], current_default.bg); 541 width[i] = 0; 542 } 543 544 /* 545 * Walk the string and add to the corresponding screens, 546 * parsing styles as we go. 547 */ 548 cp = expanded; 549 while (*cp != '\0') { 550 if (cp[0] != '#' || cp[1] != '[' || sy.ignore) { 551 /* See if this is a UTF-8 character. */ 552 if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { 553 while (*++cp != '\0' && more == UTF8_MORE) 554 more = utf8_append(ud, *cp); 555 if (more != UTF8_DONE) 556 cp -= ud->have; 557 } 558 559 /* Not a UTF-8 character - ASCII or not valid. */ 560 if (more != UTF8_DONE) { 561 if (*cp < 0x20 || *cp > 0x7e) { 562 /* Ignore nonprintable characters. */ 563 cp++; 564 continue; 565 } 566 utf8_set(ud, *cp); 567 cp++; 568 } 569 570 /* Draw the cell to the current screen. */ 571 screen_write_cell(&ctx[current], &sy.gc); 572 width[current] += ud->width; 573 continue; 574 } 575 576 /* This is a style. Work out where the end is and parse it. */ 577 end = format_skip(cp + 2, "]"); 578 if (end == NULL) { 579 log_debug("%s: no terminating ] at '%s'", __func__, 580 cp + 2); 581 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) 582 format_free_range(&frs, fr); 583 goto out; 584 } 585 tmp = xstrndup(cp + 2, end - (cp + 2)); 586 style_copy(&saved_sy, &sy); 587 if (style_parse(&sy, ¤t_default, tmp) != 0) { 588 log_debug("%s: invalid style '%s'", __func__, tmp); 589 free(tmp); 590 cp = end + 1; 591 continue; 592 } 593 log_debug("%s: style '%s' -> '%s'", __func__, tmp, 594 style_tostring(&sy)); 595 free(tmp); 596 597 /* If this style has a fill colour, store it for later. */ 598 if (sy.fill != 8) 599 fill = sy.fill; 600 601 /* If this style pushed or popped the default, update it. */ 602 if (sy.default_type == STYLE_DEFAULT_PUSH) { 603 memcpy(¤t_default, &saved_sy.gc, 604 sizeof current_default); 605 sy.default_type = STYLE_DEFAULT_BASE; 606 } else if (sy.default_type == STYLE_DEFAULT_POP) { 607 memcpy(¤t_default, base, sizeof current_default); 608 sy.default_type = STYLE_DEFAULT_BASE; 609 } 610 611 /* Check the list state. */ 612 switch (sy.list) { 613 case STYLE_LIST_ON: 614 /* 615 * Entering the list, exiting a marker, or exiting the 616 * focus. 617 */ 618 if (list_state != 0) { 619 if (fr != NULL) { /* abort any region */ 620 free(fr); 621 fr = NULL; 622 } 623 list_state = 0; 624 list_align = sy.align; 625 } 626 627 /* End the focus if started. */ 628 if (focus_start != -1 && focus_end == -1) 629 focus_end = s[LIST].cx; 630 631 current = LIST; 632 break; 633 case STYLE_LIST_FOCUS: 634 /* Entering the focus. */ 635 if (list_state != 0) /* not inside the list */ 636 break; 637 if (focus_start == -1) /* focus already started */ 638 focus_start = s[LIST].cx; 639 break; 640 case STYLE_LIST_OFF: 641 /* Exiting or outside the list. */ 642 if (list_state == 0) { 643 if (fr != NULL) { /* abort any region */ 644 free(fr); 645 fr = NULL; 646 } 647 if (focus_start != -1 && focus_end == -1) 648 focus_end = s[LIST].cx; 649 650 map[list_align] = AFTER; 651 if (list_align == STYLE_ALIGN_LEFT) 652 map[STYLE_ALIGN_DEFAULT] = AFTER; 653 list_state = 1; 654 } 655 current = map[sy.align]; 656 break; 657 case STYLE_LIST_LEFT_MARKER: 658 /* Entering left marker. */ 659 if (list_state != 0) /* not inside the list */ 660 break; 661 if (s[LIST_LEFT].cx != 0) /* already have marker */ 662 break; 663 if (fr != NULL) { /* abort any region */ 664 free(fr); 665 fr = NULL; 666 } 667 if (focus_start != -1 && focus_end == -1) 668 focus_start = focus_end = -1; 669 current = LIST_LEFT; 670 break; 671 case STYLE_LIST_RIGHT_MARKER: 672 /* Entering right marker. */ 673 if (list_state != 0) /* not inside the list */ 674 break; 675 if (s[LIST_RIGHT].cx != 0) /* already have marker */ 676 break; 677 if (fr != NULL) { /* abort any region */ 678 free(fr); 679 fr = NULL; 680 } 681 if (focus_start != -1 && focus_end == -1) 682 focus_start = focus_end = -1; 683 current = LIST_RIGHT; 684 break; 685 } 686 if (current != last) { 687 log_debug("%s: change %s -> %s", __func__, 688 names[last], names[current]); 689 last = current; 690 } 691 692 /* 693 * Check if the range style has changed and if so end the 694 * current range and start a new one if needed. 695 */ 696 if (srs != NULL) { 697 if (fr != NULL && !format_is_type(fr, &sy)) { 698 if (s[current].cx != fr->start) { 699 fr->end = s[current].cx + 1; 700 TAILQ_INSERT_TAIL(&frs, fr, entry); 701 } else 702 free(fr); 703 fr = NULL; 704 } 705 if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) { 706 fr = xcalloc(1, sizeof *fr); 707 fr->index = current; 708 709 fr->s = &s[current]; 710 fr->start = s[current].cx; 711 712 fr->type = sy.range_type; 713 fr->argument = sy.range_argument; 714 } 715 } 716 717 cp = end + 1; 718 } 719 free(fr); 720 721 for (i = 0; i < TOTAL; i++) { 722 screen_write_stop(&ctx[i]); 723 log_debug("%s: width %s is %u", __func__, names[i], width[i]); 724 } 725 if (focus_start != -1 && focus_end != -1) 726 log_debug("%s: focus %d-%d", __func__, focus_start, focus_end); 727 TAILQ_FOREACH(fr, &frs, entry) { 728 log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type, 729 fr->argument, names[fr->index], fr->start, fr->end); 730 } 731 732 /* Clear the available area. */ 733 if (fill != -1) { 734 memcpy(&gc, &grid_default_cell, sizeof gc); 735 gc.bg = fill; 736 for (i = 0; i < available; i++) 737 screen_write_putc(octx, &gc, ' '); 738 } 739 740 /* 741 * Draw the screens. How they are arranged depends on where the list 742 * appears. 743 */ 744 switch (list_align) { 745 case STYLE_ALIGN_DEFAULT: 746 /* No list. */ 747 format_draw_none(octx, available, ocx, ocy, &s[LEFT], 748 &s[CENTRE], &s[RIGHT], &frs); 749 break; 750 case STYLE_ALIGN_LEFT: 751 /* List is part of the left. */ 752 format_draw_left(octx, available, ocx, ocy, &s[LEFT], 753 &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], 754 &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); 755 break; 756 case STYLE_ALIGN_CENTRE: 757 /* List is part of the centre. */ 758 format_draw_centre(octx, available, ocx, ocy, &s[LEFT], 759 &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], 760 &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); 761 break; 762 case STYLE_ALIGN_RIGHT: 763 /* List is part of the right. */ 764 format_draw_right(octx, available, ocx, ocy, &s[LEFT], 765 &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], 766 &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); 767 break; 768 } 769 770 /* Create ranges to return. */ 771 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) { 772 sr = xcalloc(1, sizeof *sr); 773 sr->type = fr->type; 774 sr->argument = fr->argument; 775 sr->start = fr->start; 776 sr->end = fr->end; 777 TAILQ_INSERT_TAIL(srs, sr, entry); 778 779 log_debug("%s: range %d|%u at %u-%u", __func__, sr->type, 780 sr->argument, sr->start, sr->end); 781 782 format_free_range(&frs, fr); 783 } 784 785 out: 786 /* Free the screens. */ 787 for (i = 0; i < TOTAL; i++) 788 screen_free(&s[i]); 789 790 /* Restore the original cursor position. */ 791 screen_write_cursormove(octx, ocx, ocy, 0); 792 } 793 794 /* Get width, taking #[] into account. */ 795 u_int 796 format_width(const char *expanded) 797 { 798 const char *cp, *end; 799 u_int width = 0; 800 struct utf8_data ud; 801 enum utf8_state more; 802 803 cp = expanded; 804 while (*cp != '\0') { 805 if (cp[0] == '#' && cp[1] == '[') { 806 end = format_skip(cp + 2, "]"); 807 if (end == NULL) 808 return (0); 809 cp = end + 1; 810 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 811 while (*++cp != '\0' && more == UTF8_MORE) 812 more = utf8_append(&ud, *cp); 813 if (more == UTF8_DONE) 814 width += ud.width; 815 else 816 cp -= ud.have; 817 } else if (*cp > 0x1f && *cp < 0x7f) { 818 width++; 819 cp++; 820 } else 821 cp++; 822 } 823 return (width); 824 } 825 826 /* Trim on the left, taking #[] into account. */ 827 char * 828 format_trim_left(const char *expanded, u_int limit) 829 { 830 char *copy, *out; 831 const char *cp = expanded, *end; 832 u_int width = 0; 833 struct utf8_data ud; 834 enum utf8_state more; 835 836 out = copy = xmalloc(strlen(expanded) + 1); 837 while (*cp != '\0') { 838 if (cp[0] == '#' && cp[1] == '[') { 839 end = format_skip(cp + 2, "]"); 840 if (end == NULL) 841 break; 842 memcpy(out, cp, end + 1 - cp); 843 out += (end + 1 - cp); 844 cp = end + 1; 845 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 846 while (*++cp != '\0' && more == UTF8_MORE) 847 more = utf8_append(&ud, *cp); 848 if (more == UTF8_DONE) { 849 if (width + ud.width <= limit) { 850 memcpy(out, ud.data, ud.size); 851 out += ud.size; 852 } 853 width += ud.width; 854 } else { 855 cp -= ud.have; 856 cp++; 857 } 858 } else if (*cp > 0x1f && *cp < 0x7f) { 859 if (width + 1 <= limit) 860 *out++ = *cp; 861 width++; 862 cp++; 863 } else 864 cp++; 865 } 866 *out = '\0'; 867 return (copy); 868 } 869 870 /* Trim on the right, taking #[] into account. */ 871 char * 872 format_trim_right(const char *expanded, u_int limit) 873 { 874 char *copy, *out; 875 const char *cp = expanded, *end; 876 u_int width = 0, total_width, skip; 877 struct utf8_data ud; 878 enum utf8_state more; 879 880 total_width = format_width(expanded); 881 if (total_width <= limit) 882 return (xstrdup(expanded)); 883 skip = total_width - limit; 884 885 out = copy = xmalloc(strlen(expanded) + 1); 886 while (*cp != '\0') { 887 if (cp[0] == '#' && cp[1] == '[') { 888 end = format_skip(cp + 2, "]"); 889 if (end == NULL) 890 break; 891 memcpy(out, cp, end + 1 - cp); 892 out += (end + 1 - cp); 893 cp = end + 1; 894 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 895 while (*++cp != '\0' && more == UTF8_MORE) 896 more = utf8_append(&ud, *cp); 897 if (more == UTF8_DONE) { 898 if (width >= skip) { 899 memcpy(out, ud.data, ud.size); 900 out += ud.size; 901 } 902 width += ud.width; 903 } else { 904 cp -= ud.have; 905 cp++; 906 } 907 } else if (*cp > 0x1f && *cp < 0x7f) { 908 if (width >= skip) 909 *out++ = *cp; 910 width++; 911 cp++; 912 } else 913 cp++; 914 } 915 *out = '\0'; 916 return (copy); 917 } 918